The nimbus_synchronize_preview_experiments_in_kinto celery task races against user state transitions on Preview experiments.
In Section A (experimenter/kinto/tasks.py#L498-L518), the task:
- Queries
should_publish_experiments and hydrates each one in memory with status=PREVIEW, publish_status=IDLE.
- Calls
kinto_client.create_record(data) (slow network I/O).
- Inside a
transaction.atomic(), sets published_dto + published_date on the stale in-memory instance and calls experiment.save(), which writes all fields back.
If a user clicks "Request Launch" on the same Preview experiment between (1) and (3) — PreviewToReviewForm commits status=DRAFT, status_next=LIVE, publish_status=REVIEW — the task's experiment.save() overwrites those changes back to status=PREVIEW, publish_status=IDLE. The user sees the HTMX response render Review controls, then a hard refresh shows the experiment still in Preview.
Fix
In Section A, re-fetch the row with select_for_update() filtered on status=PREVIEW inside the transaction. If the experiment is no longer in Preview, skip the DB update — the next sync run reconciles Kinto via Section C. Also narrow the save() call with update_fields=["published_dto", "published_date"] so we don't write back fields we didn't intend to change.
Acceptance Criteria
- A concurrent user transition out of Preview during the Kinto push is preserved (not reverted by the task).
- Regression test in
TestNimbusSynchronizePreviewExperimentsInKinto covers this case.
┆Issue is synchronized with this Jira Task
The
nimbus_synchronize_preview_experiments_in_kintocelery task races against user state transitions on Preview experiments.In Section A (experimenter/kinto/tasks.py#L498-L518), the task:
should_publish_experimentsand hydrates each one in memory withstatus=PREVIEW, publish_status=IDLE.kinto_client.create_record(data)(slow network I/O).transaction.atomic(), setspublished_dto+published_dateon the stale in-memory instance and callsexperiment.save(), which writes all fields back.If a user clicks "Request Launch" on the same Preview experiment between (1) and (3) —
PreviewToReviewFormcommitsstatus=DRAFT, status_next=LIVE, publish_status=REVIEW— the task'sexperiment.save()overwrites those changes back tostatus=PREVIEW, publish_status=IDLE. The user sees the HTMX response render Review controls, then a hard refresh shows the experiment still in Preview.Fix
In Section A, re-fetch the row with
select_for_update()filtered onstatus=PREVIEWinside the transaction. If the experiment is no longer in Preview, skip the DB update — the next sync run reconciles Kinto via Section C. Also narrow thesave()call withupdate_fields=["published_dto", "published_date"]so we don't write back fields we didn't intend to change.Acceptance Criteria
TestNimbusSynchronizePreviewExperimentsInKintocovers this case.┆Issue is synchronized with this Jira Task