Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions benchmarks/vectorClock_bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { mergeClocks, VectorClock } from '../src/vectorClock';

function createLargeClock(size: number, start: number): VectorClock {
const clock: VectorClock = {};
for (let i = 0; i < size; i++) {
clock[`client-${start + i}`] = i;
}
return clock;
}

const ITERATIONS = 10000;
const CLOCK_SIZE = 1000;

// Setup
const clock1 = createLargeClock(CLOCK_SIZE, 0);
const clock2 = createLargeClock(CLOCK_SIZE, CLOCK_SIZE / 2); // 50% overlap

console.log(`Benchmarking mergeClocks with ${ITERATIONS} iterations...`);
console.log(`Clock size: ${CLOCK_SIZE} keys`);

const start = performance.now();

for (let i = 0; i < ITERATIONS; i++) {
mergeClocks(clock1, clock2);
}

const end = performance.now();
const totalTime = end - start;
const avgTime = totalTime / ITERATIONS;

console.log(`Total time: ${totalTime.toFixed(2)} ms`);
console.log(`Average time per op: ${avgTime.toFixed(4)} ms`);
47 changes: 47 additions & 0 deletions src/vectorClock.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, it, expect } from 'vitest';
import { mergeClocks, VectorClock } from './vectorClock';

describe('mergeClocks', () => {
it('should merge two empty clocks', () => {
const local: VectorClock = {};
const remote: VectorClock = {};
const merged = mergeClocks(local, remote);
expect(merged).toEqual({});
});

it('should merge empty local with populated remote', () => {
const local: VectorClock = {};
const remote: VectorClock = { A: 1, B: 2 };
const merged = mergeClocks(local, remote);
expect(merged).toEqual({ A: 1, B: 2 });
});

it('should merge populated local with empty remote', () => {
const local: VectorClock = { A: 1, B: 2 };
const remote: VectorClock = {};
const merged = mergeClocks(local, remote);
expect(merged).toEqual({ A: 1, B: 2 });
});

it('should merge disjoint clocks', () => {
const local: VectorClock = { A: 1 };
const remote: VectorClock = { B: 2 };
const merged = mergeClocks(local, remote);
expect(merged).toEqual({ A: 1, B: 2 });
});

it('should merge overlapping clocks taking the max', () => {
const local: VectorClock = { A: 1, B: 2, C: 3 };
const remote: VectorClock = { A: 2, B: 1, D: 4 };
const merged = mergeClocks(local, remote);
expect(merged).toEqual({ A: 2, B: 2, C: 3, D: 4 });
});

it('should not mutate inputs', () => {
const local: VectorClock = { A: 1 };
const remote: VectorClock = { A: 2 };
mergeClocks(local, remote);
expect(local).toEqual({ A: 1 });
expect(remote).toEqual({ A: 2 });
});
});
14 changes: 7 additions & 7 deletions src/vectorClock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ export function incrementClock(clock: VectorClock, clientId: string): VectorCloc
export function mergeClocks(local: VectorClock, remote: VectorClock): VectorClock {
const merged = { ...local };

// Get all client IDs from both clocks
const clientIds = new Set([...Object.keys(local), ...Object.keys(remote)]);

clientIds.forEach((clientId) => {
const localCounter = local[clientId] || 0;
for (const clientId in remote) {
const remoteCounter = remote[clientId] || 0;
merged[clientId] = Math.max(localCounter, remoteCounter);
});
const localCounter = merged[clientId];

if (localCounter === undefined || remoteCounter > localCounter) {
merged[clientId] = remoteCounter;
}
}

return merged;
}
Expand Down
Loading