Skip to content

avoid pushing stale merges #1068

@jchris

Description

@jchris

Problem

Currently, when a client is significantly out of date (e.g., 10+ commits behind the remote), it pushes its local changes directly to the remote, forcing the remote and other clients to handle merge conflicts. This creates unnecessary merge complexity on the server side and can lead to performance issues in multi-client scenarios.

Proposed Solution

Implement a "merge-first" strategy where out-of-date clients:

  1. Detect when they are significantly behind (10+ commits)
  2. Pull the latest remote state locally
  3. Merge their local changes with the remote state client-side
  4. Push the already-merged result to remote

This reduces server-side merge complexity and improves sync performance for all clients.

Implementation Plan

Detection Logic

File: core/base/crdt-clock.ts

  • Add method to compare local head with remote head length
  • Determine "out of date" threshold (10+ head length difference)

Merge Strategy

File: core/base/crdt.ts

  • Modify applyMeta() to trigger local merge when out-of-date detected
  • Add pre-push merge logic in sync operations

Sync Coordination

File: core/base/crdt-clock.ts (in int_applyHead)

  • Add logic to detect remote head advancement
  • Trigger local merge before pushing local changes

Validation Strategy

Console.log Diagnostic Points

1. Out-of-Date Detection (core/base/crdt-clock.ts):

console.log('🔍 MERGE_STRATEGY: Checking if client is out of date', {
  localHeadLength: this.head.length,
  remoteHeadLength: remoteHead.length,
  commitsBehind: remoteHead.length - this.head.length,
  shouldMergeFirst: (remoteHead.length - this.head.length) >= 10,
  dbName: this.opts.name,
  timestamp: Date.now()
});

2. Pre-Push Merge Decision (core/base/crdt.ts):

console.log('🔄 MERGE_STRATEGY: Executing local merge before push', {
  strategy: 'merge-first',
  localChanges: localHead.length,
  remoteChanges: remoteHead.length,
  mergeComplexity: 'low|medium|high',
  dbName: this.opts.name,
  timestamp: Date.now()
});

3. Merge Completion (core/base/crdt-clock.ts):

console.log('✅ MERGE_STRATEGY: Local merge completed', {
  originalHeadLength: originalHead.length,
  mergedHeadLength: mergedHead.length,
  conflictsResolved: conflictCount,
  readyForPush: true,
  dbName: this.opts.name,
  timestamp: Date.now()
});

Testing Strategy

Test File: core/tests/fireproof/merge-first-strategy.test.ts

Test Cases:

  1. Client not out-of-date (< 10 commits behind)

    • Should push directly without local merge
    • Verify no merge-first console logs appear
  2. Client significantly out-of-date (10+ commits behind)

    • Should trigger local merge before push
    • Verify merge-first console logs appear in correct sequence
    • Verify final push contains merged result
  3. Multi-client scenario

    • Multiple clients with different lag levels
    • Verify only out-of-date clients trigger merge-first
    • Verify reduced merge conflicts on remote

Validation Commands:

# Test out-of-date detection
pnpm test core/tests/fireproof/merge-first-strategy.test.ts -t "should detect out-of-date client" --reporter=verbose

# Test merge-first execution  
pnpm test core/tests/fireproof/merge-first-strategy.test.ts -t "should merge locally before push" --reporter=verbose

# Test multi-client coordination
pnpm test core/tests/fireproof/merge-first-strategy.test.ts -t "should coordinate merge-first across clients" --reporter=verbose

Expected Benefits

  • Reduced server-side merge complexity
  • Better performance for clients that aren't out-of-date
  • Fewer merge conflicts propagated to other clients
  • More predictable sync behavior in multi-client scenarios

Success Criteria

  1. ✅ Out-of-date detection works reliably (console logs confirm)
  2. ✅ Local merge executes only when needed (10+ commits behind)
  3. ✅ Merged results push successfully without conflicts
  4. ✅ Non-out-of-date clients unaffected (no performance regression)
  5. ✅ Multi-client scenarios show reduced merge conflicts

This enhancement would make Fireproof's sync behavior more intelligent and reduce the burden on remotes and other clients when dealing with significantly out-of-date clients.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions