Skip to content

PWA Phase 3: Fix debounce race condition in analytics sync #104

@kevalyq

Description

@kevalyq

🐛 Bug Description

File: src/lib/analytics.ts (lines 113-122)
Severity: MEDIUM
Origin: Copilot review comment in PR #100

Problem

Race condition in debouncedSync() and syncEvents() coordination:

private debouncedSync(): void {
  if (this.syncTimeout) {
    clearTimeout(this.syncTimeout);
  }

  this.syncTimeout = window.setTimeout(() => {
    this.syncEvents();  // ← Async sync triggered
    this.syncTimeout = undefined;
  }, 1000);
}

async syncEvents(): Promise<void> {
  if (!this.isOnline || this.isDestroyed) return;

  // ❌ No clearing of pending debounced sync!
  if (this.isSyncing) {
    console.log("Sync already in progress, skipping...");
    return;
  }

  this.isSyncing = true;
  // ... sync logic
}

Issue: If syncEvents() is called directly (e.g., from handleOnline() at line 214) while a debounced sync is pending, the timeout is not cleared. This can cause:

  • Duplicate sync attempts
  • Race conditions between manual and debounced sync
  • Inefficient network usage

Expected Behavior

When a manual sync is triggered, any pending debounced sync should be cancelled.

Proposed Solution

Clear pending timeout at the start of syncEvents():

async syncEvents(): Promise<void> {
  if (!this.isOnline || this.isDestroyed) return;

  // ✅ Clear any pending debounced sync
  if (this.syncTimeout) {
    clearTimeout(this.syncTimeout);
    this.syncTimeout = undefined;
  }

  // Prevent concurrent syncs
  if (this.isSyncing) {
    console.log("Sync already in progress, skipping...");
    return;
  }

  this.isSyncing = true;
  // ... rest of sync logic
}

Reproduction Steps

  1. Track multiple analytics events rapidly while online
  2. Debounced sync is scheduled for 1 second later
  3. Before timeout fires, manually trigger sync (e.g., by toggling offline/online)
  4. Both syncs may try to execute

Context

Acceptance Criteria

  • syncEvents() clears pending syncTimeout at start
  • Manual sync cancels debounced sync
  • No duplicate sync attempts detected
  • Tests verify coordination between debounced and manual sync

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions