Skip to content

feat(sync): share sync locking across all sync entry-points#333

Merged
itsmiso-ai merged 2 commits into
mainfrom
saffron/share-sync-lock
Jun 9, 2026
Merged

feat(sync): share sync locking across all sync entry-points#333
itsmiso-ai merged 2 commits into
mainfrom
saffron/share-sync-lock

Conversation

@itsmiso-ai

Copy link
Copy Markdown
Contributor

Fixes #313

Extract DB-backed lock from scheduled sync into shared module src/lib/sync-lock.ts and apply it to manual issue sync and automation sync endpoints. This prevents overlapping concurrent runs across all three sync types (scheduled, manual, automation), addressing race conditions from browser refreshes, cron overlap, or repeated clicks.

Changes

  • New src/lib/sync-lock.ts: acquireLock(syncType) / releaseLock(runId) with stale lock cleanup (>30 min) and transactional double-check
  • *`src/app/api/sync/route.ts* (manual issue sync): acquire lock before issue sync, return 409 if a sync is already running
  • *`src/app/api/automation/sync/route.ts* (automation sync): acquire lock before automation sync (single repo or batch), return 409 if locked
  • **`src/app/api/sync/scheduled/route.ts**: replace inline lock with shared module import, update error message to be sync-type-agnostic
  • **`prisma/schema.prisma**: update syncType comment to include 'automation'

Extract DB-backed lock from scheduled sync into shared module
(src/lib/sync-lock.ts) and apply it to manual issue sync and automation
sync endpoints. This prevents overlapping concurrent runs across all
three sync types (scheduled, manual, automation), addressing race
conditions from browser refreshes, cron overlap, or repeated clicks.

- New src/lib/sync-lock.ts: acquireLock(syncType) / releaseLock(runId)
  with stale lock cleanup (>30 min) and transactional double-check
- src/app/api/sync/route.ts: acquire lock before issue sync, return 409
  if a sync is already running
- src/app/api/automation/sync/route.ts: acquire lock before automation
  sync (single repo or batch), return 409 if locked
- src/app/api/sync/scheduled/route.ts: replace inline lock with shared
  module import, update error message to be sync-type-agnostic
- prisma/schema.prisma: update syncType comment to include 'automation'

@its-saffron its-saffron Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Automated Review

Full PR review.

Analysis engine: review@https://litellm.jory.dev/v1 (openai)

Recommendation

Approve. This PR correctly addresses the concurrency issues identified in PR 313 by extracting the database-backed locking logic into a shared module and applying it consistently across all sync entry-points (scheduled, manual, and automation).

Change-by-Change Findings

  • src/lib/sync-lock.ts (Added): Implements a robust, shared locking mechanism using the syncLock table. It includes necessary safety features: a 30-minute stale lock cleanup and transactional double-checking to prevent race conditions during lock acquisition.
  • prisma/schema.prisma (Modified): Updates the IssueSyncRun.syncType enum/comment to include automation, ensuring the audit trail correctly reflects all sync types.
  • src/app/api/automation/sync/route.ts (Modified): Integrates the new acquireLock and releaseLock logic. It correctly returns a 409 Conflict if a sync is already in progress and uses a try...finally block to ensure the lock is released even if the sync fails.
  • src/app/api/sync/route.ts (Modified): Integrates the shared lock for manual issue syncs. It also adds logic to update the IssueSyncRun record to completed upon successful sync, improving observability.
  • src/app/api/sync/scheduled/route.ts (Modified): Refactors the previously inline lock implementation to use the shared sync-lock.ts module, ensuring the scheduled sync respects the same global lock as other sync types.

Standards Compliance

  • Error Handling: Follows the error instanceof Error pattern and provides meaningful HTTP status codes (409 for conflicts, 500 for internal errors).
  • Validation: Validates lock acquisition before proceeding with heavy GitHub/database operations.
  • Code Standards: The implementation is internally consistent and follows the repository's pattern of using Prisma for state management.

Linked Issue Fit

  • Matches PR 313: The PR directly implements the recommendation to share sync locking/concurrency controls across all sync entry-points to prevent overlapping runs caused by cron overlap, browser refreshes, or repeated clicks.

The manual sync and automation sync routes now use acquireLock() which
requires prisma.syncLock and prisma.. Add these mocks to
the test fixtures so the tests pass without a real database.

@its-saffron its-saffron Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Automated Review

Full PR review.

Analysis engine: review@https://litellm.jory.dev/v1 (openai)

Recommendation

Approve. This PR correctly addresses the race conditions identified in issue PR 313 by centralizing the synchronization locking mechanism into a shared module. This ensures that scheduled, manual, and automation syncs cannot run concurrently, preventing overlapping GitHub API calls and database write conflicts.

Change-by-Change Findings

1. Shared Locking Module (src/lib/sync-lock.ts)

  • Implementation: Introduced a robust acquireLock and releaseLock mechanism using a single-row guard in the syncLock table.
  • Safety: Uses a database transaction to prevent race conditions during lock acquisition and implements a MAX_AGE_MS (30 minutes) to automatically clear stale locks.
  • Traceability: The lock acquisition creates an IssueSyncRun record, allowing for better auditability of which sync type holds the lock.

2. API Route Updates

  • src/app/api/automation/sync/route.ts: Now acquires an automation lock before proceeding. Correctly uses a try...finally block to ensure the lock is released even if the sync fails.
  • src/app/api/sync/route.ts: Now acquires a manual lock. Added logic to update the IssueSyncRun record to completed upon successful sync.
  • src/app/api/sync/scheduled/route.ts: Refactored to use the shared module instead of the previous inline implementation, ensuring consistency across all sync types.

3. Schema and Testing

  • prisma/schema.prisma: Updated syncType enum/comment to include automation for consistency.
  • Tests: Added unit tests for the new sync routes (automation/sync and sync) to verify lock acquisition, 409 Conflict responses, and lock release behavior.

Standards Compliance

  • Error Handling: Follows the error instanceof Error pattern and returns appropriate HTTP status codes (409 for lock conflicts, 500 for internal errors).
  • Code Standards: Implementation is internally consistent and follows the established pattern of using prisma.$transaction for atomic operations.
  • Validation: Validates lock status before proceeding with heavy I/O operations (GitHub API calls).

Linked Issue Fit

  • Issue PR 313: This PR directly satisfies the requirement to share sync locking/concurrency controls across all sync entry-points, specifically addressing the lack of shared locking between scheduled, manual, and automation syncs.

@itsmiso-ai itsmiso-ai merged commit df52419 into main Jun 9, 2026
3 checks passed
@itsmiso-ai itsmiso-ai deleted the saffron/share-sync-lock branch June 9, 2026 02:40
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.

Share sync locking/concurrency controls

1 participant