-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent double-publishing #1006
Conversation
54651e6
to
727484c
Compare
@waxlamp I implemented this. This is what happens when two people try to publish at the same time (I added some |
727484c
to
9c8ee69
Compare
@djarecka would you be so kind to give this PR a test , in particular in relation to #955 -- may be we should merge it and see in staging but I would love to see at least some test-drive by someone else. I will try to test drive it as well in upcoming days, so far haven't spotted anything questionable in the diff |
@yarikoptic - you mean adding tests to the changes in vue code? |
After coming back to this with fresh eyes after 2 months, I think my approach here is fundamentally flawed. The implementation here does technically work, but it relies on the assumption that a locked I believe the ideal solution would be to introduce a Taking a step back, I think publishing would be more appropriate as a celery task. iirc, this issue was motivated by the fact that dandisets with tens of thousands of assets can take a few seconds (or more) to publish. That reason alone is enough evidence that this should be a celery task, I think. Plus, the concurrency logic becomes a lot simpler than what I have currently- @transaction.atomic
@action(detail=True, methods=['POST'])
def publish(self, request, **kwargs):
version: Version = self.get_queryset().select_for_update().filter(**kwargs).first()
if version.status == "PUBLISHING":
return 429
elif version.version != 'draft':
return 400
# other checks.....
.....
version.status = 'PUBLISHING'
version.save()
publish_task.delay(version.pk) # version.status would be set to PUBLISHED at the end of this task
return 200 |
2d8e8b6
to
cbd1dc1
Compare
I think this is a good design overall. Some thoughts:
|
Whoops, I meant to put
I like that idea as well 👍
Right, since publishing would now be done as a task, this is no longer a concern. A request would only hold the lock while checking the |
I think that code would be fine. It's probably more suitable than 409, but I don't think it matters much which 4xx code we pick for this. |
UI-wise, I think using the cancel button introduced in #1122 would be perfect here. |
6946c19
to
3bb4f88
Compare
c032eaf
to
0a04f30
Compare
444804d
to
0876494
Compare
@mvandenburgh, status update on this PR? AFAIK, the design discussions we've had here are still valid. Confirm? |
Yes, everything here is still valid, I just have to implement the design. This PR sort of fell off my radar in favor of higher priority bugs, but those seem to be mostly resolved so I'll rebase this and start working on it again |
AssetVersions.objects.bulk_create( | ||
[ | ||
AssetVersions(asset_id=asset['id'], version_id=new_version.id) | ||
for asset in already_published_assets.values('id') | ||
] | ||
) | ||
|
||
# Publish any draft assets | ||
draft_assets = old_version.assets.filter(published=False).all() | ||
for draft_asset in draft_assets: | ||
draft_asset.publish() | ||
Asset.objects.bulk_update(draft_assets, ['metadata', 'published']) | ||
|
||
AssetVersions.objects.bulk_create( | ||
[AssetVersions(asset_id=asset.id, version_id=new_version.id) for asset in draft_assets] | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these loops that iterate over every asset in a dandiset are probably going to lead to memory issues when publishing dandisets with large numbers of assets. These are copied 1-1 from the original publishing endpoint code, so I'm not going to attempt to fix them here; I'll do some memory profiling and make a follow up PR once I come up with a solution to this
old_version: Version = Version.objects.get(id=version_id) | ||
new_version: Version = old_version.publish_version | ||
|
||
new_version.doi = doi.create_doi(new_version) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unrelated, but could you make a follow on PR moving this into on_commit? This is a bug waiting to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain further about the bug risk?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@waxlamp as it is now, if something goes wrong and the transaction gets rolled back, the DOI is still created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah makes sense, thanks!
25f8c3c
to
0272d91
Compare
acb3aff
to
df664b3
Compare
0272d91
to
9cc8623
Compare
9cc8623
to
8762a67
Compare
@jwodder @yarikoptic the CLI tests are failing here. It appears to be happening because of the changes to the semantics of the publishing process in this PR, so we'll need some updates on the CLI side to unblock this. Currently, the publishing process is like this -
This PR updates it to do the following -
To tell when a version has finished publishing, you can poll the Can you make the necessary changes to the CLI to account for this? Please let me know if anything is unclear or if you need any more information. |
- After clicking the publish button, display a loading indicator and poll the server to determine when the publishing is complete - Once the publishing is complete, display a snackbar notification with a link to the newly published version - If the publish button is clicked while a publish is ongoing, display an error message and begin polling for the publish to complete. Once completed, display a link to it.
- Add new test for publish task - Modify publish rest test to only test for PUBLISHING status, not entire publish workflow - Consolidate two publish REST endpoint tests into one
Add specific error messages instead of returing "Dandiset metadata or asset metadata is not valid" everytime a publish can't occur
34318b8
to
e0d8267
Compare
🚀 PR was released in |
Closes #956
Closes #955
Currently, there is a race condition where two or more simultaneous calls to the
/publish
endpoint may result in multiple publishes occurring. This PR fixes this in two parts: