Skip to content

fix: guard the workout upload path against concurrent runs#912

Open
Anexus5919 wants to merge 1 commit into
Somil450:mainfrom
Anexus5919:fix/sync-guard-double-upload
Open

fix: guard the workout upload path against concurrent runs#912
Anexus5919 wants to merge 1 commit into
Somil450:mainfrom
Anexus5919:fix/sync-guard-double-upload

Conversation

@Anexus5919

Copy link
Copy Markdown
Contributor

📌 Related Issue

Fixes #875


📝 Description

syncInProgress only wrapped the online-event handler, so a sync reached through fullSyncWorkouts (called unguarded on login in useWorkoutSync) could run concurrently with an online-triggered sync. Both call getUnsyncedWorkouts and read the same unsynced set before either marks them synced, and uploadWorkoutToFirestore uses addDoc (no idempotency), so each concurrent run creates a separate Firestore document for the same workout.

🔹 What has been changed?

  • src/services/workoutSyncService.ts: moved the in-flight syncInProgress guard into syncWorkoutsToFirestore (the single upload choke point), so every entry point (login fullSyncWorkouts, the online handler, manual sync) shares it. Removed the now-redundant guard toggling from the online handler.

🔹 Why are these changes needed?

  • The guard check and set run synchronously before the first await, so two concurrent callers can no longer both pass it and double-upload. This prevents duplicate workout documents in Firestore.

🛠️ Type of Change

  • 🐛 Bug Fix

🧪 Testing

✅ Tests Performed

  • Tested locally
  • npx tsc --noEmit: changed file clean; npx eslint: 0 problems.
  • Verified by reasoning about the synchronous guard: the if (syncInProgress) return / syncInProgress = true pair runs before any await, so concurrent callers are serialized to one upload run.

🔹 Note on automated tests

No committed unit test: this path needs both IndexedDB and a Firestore mock, and the repo has no fake-indexeddb harness. Verification is by tsc/eslint and the reasoning above.

🌐 Browsers Tested

Not applicable (sync logic).


📷 Screenshots / Demo (if applicable)

Not applicable.


📋 Checklist

  • I have read the project's CONTRIBUTING guidelines
  • My code follows the project style guidelines
  • I have performed a self-review of my code
  • I have tested my changes locally
  • I have added/updated documentation where necessary (not applicable)
  • My changes do not introduce new warnings or errors
  • This PR is linked to an existing issue

💬 Additional Notes

Branched from latest upstream/main (5464425). A deeper follow-up (out of scope here) would give each workout a deterministic Firestore document id (e.g. setDoc keyed by crdtSessionId) so even a retry after a lost write acknowledgement cannot duplicate.

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

@Anexus5919 is attempting to deploy a commit to the somiljain2024-4175's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Anexus5919

Copy link
Copy Markdown
Contributor Author

@Somil450 @diksha78dev Kindly have a review on this pr. Thanks!

…docs

syncInProgress only wrapped the online-event handler, so a sync reached
through fullSyncWorkouts (called unguarded on login) could run at the same
time as an online-triggered sync. Both read the same unsynced workouts
before either marked them synced, and uploadWorkoutToFirestore uses addDoc
with no idempotency, so each concurrent run created a separate Firestore
document for the same workout.

Move the in-flight guard into syncWorkoutsToFirestore, the single upload
choke point, so every entry point shares it, and drop the now-redundant
toggling in the online handler.
@Anexus5919 Anexus5919 force-pushed the fix/sync-guard-double-upload branch from 461c6f2 to f4c53dc Compare June 23, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Concurrent auto-sync runs double-upload workouts, creating duplicate Firestore documents

1 participant