Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
1
vendor/ruvector/npm/packages/replication/src/index.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAGH,OAAO,EACL,SAAS,EACT,YAAY,EACZ,WAAW,EACX,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
||||
1
vendor/ruvector/npm/packages/replication/src/index.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;;;AAEH,QAAQ;AACR,uCAkBoB;AAflB,uGAAA,WAAW,OAAA;AACX,yGAAA,aAAa,OAAA;AACb,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAEZ,2GAAA,eAAe,OAAA;AAIf,0GAAA,cAAc,OAAA;AACd,4GAAA,gBAAgB,OAAA;AAChB,gHAAA,oBAAoB,OAAA;AACpB,4GAAA,gBAAgB,OAAA;AAKlB,eAAe;AACf,qDAM2B;AALzB,8GAAA,WAAW,OAAA;AACX,wHAAA,qBAAqB,OAAA;AAErB,gHAAA,aAAa,OAAA;AACb,gHAAA,aAAa,OAAA;AAGf,cAAc;AACd,mDAA8C;AAArC,4GAAA,UAAU,OAAA;AAEnB,eAAe;AACf,qDAAgE;AAAvD,8GAAA,WAAW,OAAA;AAAE,iHAAA,cAAc,OAAA"}
|
||||
83
vendor/ruvector/npm/packages/replication/src/index.ts
vendored
Normal file
83
vendor/ruvector/npm/packages/replication/src/index.ts
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @ruvector/replication - Data Replication and Synchronization
|
||||
*
|
||||
* A TypeScript implementation of data replication capabilities including:
|
||||
* - Multi-node replica management
|
||||
* - Synchronous, asynchronous, and semi-synchronous replication modes
|
||||
* - Conflict resolution with vector clocks
|
||||
* - Change data capture and streaming
|
||||
* - Automatic failover and split-brain prevention
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import {
|
||||
* ReplicaSet,
|
||||
* ReplicaRole,
|
||||
* SyncManager,
|
||||
* ReplicationLog,
|
||||
* SyncMode,
|
||||
* ChangeOperation,
|
||||
* } from '@ruvector/replication';
|
||||
*
|
||||
* // Create a replica set
|
||||
* const replicaSet = new ReplicaSet('my-cluster');
|
||||
*
|
||||
* // Add replicas
|
||||
* replicaSet.addReplica('replica-1', '192.168.1.10:9001', ReplicaRole.Primary);
|
||||
* replicaSet.addReplica('replica-2', '192.168.1.11:9001', ReplicaRole.Secondary);
|
||||
* replicaSet.addReplica('replica-3', '192.168.1.12:9001', ReplicaRole.Secondary);
|
||||
*
|
||||
* // Create replication log and sync manager
|
||||
* const log = new ReplicationLog('replica-1');
|
||||
* const syncManager = new SyncManager(replicaSet, log);
|
||||
*
|
||||
* // Configure semi-sync replication
|
||||
* syncManager.setSyncMode(SyncMode.SemiSync, 1);
|
||||
*
|
||||
* // Listen for events
|
||||
* syncManager.on('changeReceived', (change) => {
|
||||
* console.log(`Change: ${change.operation} on ${change.key}`);
|
||||
* });
|
||||
*
|
||||
* // Record a change
|
||||
* await syncManager.recordChange('user:123', ChangeOperation.Update, { name: 'Alice' });
|
||||
* ```
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
// Types
|
||||
export {
|
||||
ReplicaId,
|
||||
LogicalClock,
|
||||
ReplicaRole,
|
||||
ReplicaStatus,
|
||||
SyncMode,
|
||||
HealthStatus,
|
||||
Replica,
|
||||
ChangeOperation,
|
||||
ChangeEvent,
|
||||
VectorClockValue,
|
||||
LogEntry,
|
||||
FailoverPolicy,
|
||||
ReplicationError,
|
||||
ReplicationErrorCode,
|
||||
ReplicationEvent,
|
||||
ReplicaSetConfig,
|
||||
SyncConfig,
|
||||
} from './types.js';
|
||||
|
||||
// Vector Clock
|
||||
export {
|
||||
VectorClock,
|
||||
VectorClockComparison,
|
||||
ConflictResolver,
|
||||
LastWriteWins,
|
||||
MergeFunction,
|
||||
} from './vector-clock.js';
|
||||
|
||||
// Replica Set
|
||||
export { ReplicaSet } from './replica-set.js';
|
||||
|
||||
// Sync Manager
|
||||
export { SyncManager, ReplicationLog } from './sync-manager.js';
|
||||
55
vendor/ruvector/npm/packages/replication/src/replica-set.d.ts
vendored
Normal file
55
vendor/ruvector/npm/packages/replication/src/replica-set.d.ts
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Replica Set Management
|
||||
* Manages a set of replicas for distributed data storage
|
||||
*/
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { type Replica, type ReplicaId, type ReplicaSetConfig, ReplicaRole, ReplicaStatus } from './types.js';
|
||||
/** Manages a set of replicas */
|
||||
export declare class ReplicaSet extends EventEmitter {
|
||||
private replicas;
|
||||
private config;
|
||||
private heartbeatTimer;
|
||||
constructor(name: string, config?: Partial<ReplicaSetConfig>);
|
||||
/** Get replica set name */
|
||||
get name(): string;
|
||||
/** Get the primary replica */
|
||||
get primary(): Replica | undefined;
|
||||
/** Get all secondary replicas */
|
||||
get secondaries(): Replica[];
|
||||
/** Get all active replicas */
|
||||
get activeReplicas(): Replica[];
|
||||
/** Get replica count */
|
||||
get size(): number;
|
||||
/** Check if quorum is met */
|
||||
get hasQuorum(): boolean;
|
||||
/** Add a replica to the set */
|
||||
addReplica(id: ReplicaId, address: string, role: ReplicaRole): Replica;
|
||||
/** Remove a replica from the set */
|
||||
removeReplica(id: ReplicaId): boolean;
|
||||
/** Get a replica by ID */
|
||||
getReplica(id: ReplicaId): Replica | undefined;
|
||||
/** Update replica status */
|
||||
updateStatus(id: ReplicaId, status: ReplicaStatus): void;
|
||||
/** Update replica lag */
|
||||
updateLag(id: ReplicaId, lag: number): void;
|
||||
/** Promote a secondary to primary */
|
||||
promote(id: ReplicaId): void;
|
||||
/** Trigger automatic failover */
|
||||
private triggerFailover;
|
||||
/** Start heartbeat monitoring */
|
||||
startHeartbeat(): void;
|
||||
/** Stop heartbeat monitoring */
|
||||
stopHeartbeat(): void;
|
||||
/** Get all replicas */
|
||||
getAllReplicas(): Replica[];
|
||||
/** Get replica set stats */
|
||||
getStats(): {
|
||||
total: number;
|
||||
active: number;
|
||||
syncing: number;
|
||||
offline: number;
|
||||
failed: number;
|
||||
hasQuorum: boolean;
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=replica-set.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/replica-set.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/replica-set.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"replica-set.d.ts","sourceRoot":"","sources":["replica-set.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,WAAW,EACX,aAAa,EAId,MAAM,YAAY,CAAC;AAWpB,gCAAgC;AAChC,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,cAAc,CAA+C;gBAEzD,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAK5D,2BAA2B;IAC3B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,8BAA8B;IAC9B,IAAI,OAAO,IAAI,OAAO,GAAG,SAAS,CAOjC;IAED,iCAAiC;IACjC,IAAI,WAAW,IAAI,OAAO,EAAE,CAI3B;IAED,8BAA8B;IAC9B,IAAI,cAAc,IAAI,OAAO,EAAE,CAE9B;IAED,wBAAwB;IACxB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,6BAA6B;IAC7B,IAAI,SAAS,IAAI,OAAO,CAGvB;IAED,+BAA+B;IAC/B,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO;IAyBtE,oCAAoC;IACpC,aAAa,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO;IAerC,0BAA0B;IAC1B,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS;IAI9C,4BAA4B;IAC5B,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI;IA4BxD,yBAAyB;IACzB,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ3C,qCAAqC;IACrC,OAAO,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAwB5B,iCAAiC;IACjC,OAAO,CAAC,eAAe;IAmBvB,iCAAiC;IACjC,cAAc,IAAI,IAAI;IAetB,gCAAgC;IAChC,aAAa,IAAI,IAAI;IAOrB,uBAAuB;IACvB,cAAc,IAAI,OAAO,EAAE;IAI3B,4BAA4B;IAC5B,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;KACpB;CAWF"}
|
||||
204
vendor/ruvector/npm/packages/replication/src/replica-set.js
vendored
Normal file
204
vendor/ruvector/npm/packages/replication/src/replica-set.js
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Replica Set Management
|
||||
* Manages a set of replicas for distributed data storage
|
||||
*/
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ReplicaSet = void 0;
|
||||
const eventemitter3_1 = __importDefault(require("eventemitter3"));
|
||||
const types_js_1 = require("./types.js");
|
||||
/** Default configuration */
|
||||
const DEFAULT_CONFIG = {
|
||||
name: 'default',
|
||||
minQuorum: 2,
|
||||
heartbeatInterval: 1000,
|
||||
healthCheckTimeout: 5000,
|
||||
failoverPolicy: types_js_1.FailoverPolicy.Automatic,
|
||||
};
|
||||
/** Manages a set of replicas */
|
||||
class ReplicaSet extends eventemitter3_1.default {
|
||||
constructor(name, config) {
|
||||
super();
|
||||
this.replicas = new Map();
|
||||
this.heartbeatTimer = null;
|
||||
this.config = { ...DEFAULT_CONFIG, name, ...config };
|
||||
}
|
||||
/** Get replica set name */
|
||||
get name() {
|
||||
return this.config.name;
|
||||
}
|
||||
/** Get the primary replica */
|
||||
get primary() {
|
||||
for (const replica of this.replicas.values()) {
|
||||
if (replica.role === types_js_1.ReplicaRole.Primary && replica.status === types_js_1.ReplicaStatus.Active) {
|
||||
return replica;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/** Get all secondary replicas */
|
||||
get secondaries() {
|
||||
return Array.from(this.replicas.values()).filter((r) => r.role === types_js_1.ReplicaRole.Secondary && r.status === types_js_1.ReplicaStatus.Active);
|
||||
}
|
||||
/** Get all active replicas */
|
||||
get activeReplicas() {
|
||||
return Array.from(this.replicas.values()).filter((r) => r.status === types_js_1.ReplicaStatus.Active);
|
||||
}
|
||||
/** Get replica count */
|
||||
get size() {
|
||||
return this.replicas.size;
|
||||
}
|
||||
/** Check if quorum is met */
|
||||
get hasQuorum() {
|
||||
const activeCount = this.activeReplicas.length;
|
||||
return activeCount >= this.config.minQuorum;
|
||||
}
|
||||
/** Add a replica to the set */
|
||||
addReplica(id, address, role) {
|
||||
if (this.replicas.has(id)) {
|
||||
throw new Error(`Replica ${id} already exists`);
|
||||
}
|
||||
// Check if adding a primary when one exists
|
||||
if (role === types_js_1.ReplicaRole.Primary && this.primary) {
|
||||
throw new Error('Primary already exists in replica set');
|
||||
}
|
||||
const replica = {
|
||||
id,
|
||||
address,
|
||||
role,
|
||||
status: types_js_1.ReplicaStatus.Active,
|
||||
lastSeen: Date.now(),
|
||||
lag: 0,
|
||||
};
|
||||
this.replicas.set(id, replica);
|
||||
this.emit(types_js_1.ReplicationEvent.ReplicaAdded, replica);
|
||||
return replica;
|
||||
}
|
||||
/** Remove a replica from the set */
|
||||
removeReplica(id) {
|
||||
const replica = this.replicas.get(id);
|
||||
if (!replica)
|
||||
return false;
|
||||
this.replicas.delete(id);
|
||||
this.emit(types_js_1.ReplicationEvent.ReplicaRemoved, replica);
|
||||
// If primary was removed, trigger failover
|
||||
if (replica.role === types_js_1.ReplicaRole.Primary && this.config.failoverPolicy === types_js_1.FailoverPolicy.Automatic) {
|
||||
this.triggerFailover();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/** Get a replica by ID */
|
||||
getReplica(id) {
|
||||
return this.replicas.get(id);
|
||||
}
|
||||
/** Update replica status */
|
||||
updateStatus(id, status) {
|
||||
const replica = this.replicas.get(id);
|
||||
if (!replica) {
|
||||
throw types_js_1.ReplicationError.replicaNotFound(id);
|
||||
}
|
||||
const previousStatus = replica.status;
|
||||
replica.status = status;
|
||||
replica.lastSeen = Date.now();
|
||||
if (previousStatus !== status) {
|
||||
this.emit(types_js_1.ReplicationEvent.ReplicaStatusChanged, {
|
||||
replica,
|
||||
previousStatus,
|
||||
newStatus: status,
|
||||
});
|
||||
// Check for failover conditions
|
||||
if (replica.role === types_js_1.ReplicaRole.Primary &&
|
||||
status === types_js_1.ReplicaStatus.Failed &&
|
||||
this.config.failoverPolicy === types_js_1.FailoverPolicy.Automatic) {
|
||||
this.triggerFailover();
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Update replica lag */
|
||||
updateLag(id, lag) {
|
||||
const replica = this.replicas.get(id);
|
||||
if (replica) {
|
||||
replica.lag = lag;
|
||||
replica.lastSeen = Date.now();
|
||||
}
|
||||
}
|
||||
/** Promote a secondary to primary */
|
||||
promote(id) {
|
||||
const replica = this.replicas.get(id);
|
||||
if (!replica) {
|
||||
throw types_js_1.ReplicationError.replicaNotFound(id);
|
||||
}
|
||||
if (replica.role === types_js_1.ReplicaRole.Primary) {
|
||||
return; // Already primary
|
||||
}
|
||||
// Demote current primary
|
||||
const currentPrimary = this.primary;
|
||||
if (currentPrimary) {
|
||||
currentPrimary.role = types_js_1.ReplicaRole.Secondary;
|
||||
}
|
||||
// Promote new primary
|
||||
replica.role = types_js_1.ReplicaRole.Primary;
|
||||
this.emit(types_js_1.ReplicationEvent.PrimaryChanged, {
|
||||
previousPrimary: currentPrimary?.id,
|
||||
newPrimary: id,
|
||||
});
|
||||
}
|
||||
/** Trigger automatic failover */
|
||||
triggerFailover() {
|
||||
this.emit(types_js_1.ReplicationEvent.FailoverStarted, {});
|
||||
// Find the best candidate (lowest lag, active secondary)
|
||||
const candidates = this.secondaries
|
||||
.filter((r) => r.status === types_js_1.ReplicaStatus.Active)
|
||||
.sort((a, b) => a.lag - b.lag);
|
||||
if (candidates.length === 0) {
|
||||
this.emit(types_js_1.ReplicationEvent.Error, types_js_1.ReplicationError.noPrimary());
|
||||
return;
|
||||
}
|
||||
const newPrimary = candidates[0];
|
||||
this.promote(newPrimary.id);
|
||||
this.emit(types_js_1.ReplicationEvent.FailoverCompleted, { newPrimary: newPrimary.id });
|
||||
}
|
||||
/** Start heartbeat monitoring */
|
||||
startHeartbeat() {
|
||||
if (this.heartbeatTimer)
|
||||
return;
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const replica of this.replicas.values()) {
|
||||
if (now - replica.lastSeen > this.config.healthCheckTimeout) {
|
||||
if (replica.status === types_js_1.ReplicaStatus.Active) {
|
||||
this.updateStatus(replica.id, types_js_1.ReplicaStatus.Offline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this.config.heartbeatInterval);
|
||||
}
|
||||
/** Stop heartbeat monitoring */
|
||||
stopHeartbeat() {
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = null;
|
||||
}
|
||||
}
|
||||
/** Get all replicas */
|
||||
getAllReplicas() {
|
||||
return Array.from(this.replicas.values());
|
||||
}
|
||||
/** Get replica set stats */
|
||||
getStats() {
|
||||
const replicas = Array.from(this.replicas.values());
|
||||
return {
|
||||
total: replicas.length,
|
||||
active: replicas.filter((r) => r.status === types_js_1.ReplicaStatus.Active).length,
|
||||
syncing: replicas.filter((r) => r.status === types_js_1.ReplicaStatus.Syncing).length,
|
||||
offline: replicas.filter((r) => r.status === types_js_1.ReplicaStatus.Offline).length,
|
||||
failed: replicas.filter((r) => r.status === types_js_1.ReplicaStatus.Failed).length,
|
||||
hasQuorum: this.hasQuorum,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.ReplicaSet = ReplicaSet;
|
||||
//# sourceMappingURL=replica-set.js.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/replica-set.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/replica-set.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
254
vendor/ruvector/npm/packages/replication/src/replica-set.ts
vendored
Normal file
254
vendor/ruvector/npm/packages/replication/src/replica-set.ts
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Replica Set Management
|
||||
* Manages a set of replicas for distributed data storage
|
||||
*/
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import {
|
||||
type Replica,
|
||||
type ReplicaId,
|
||||
type ReplicaSetConfig,
|
||||
ReplicaRole,
|
||||
ReplicaStatus,
|
||||
ReplicationError,
|
||||
ReplicationEvent,
|
||||
FailoverPolicy,
|
||||
} from './types.js';
|
||||
|
||||
/** Default configuration */
|
||||
const DEFAULT_CONFIG: ReplicaSetConfig = {
|
||||
name: 'default',
|
||||
minQuorum: 2,
|
||||
heartbeatInterval: 1000,
|
||||
healthCheckTimeout: 5000,
|
||||
failoverPolicy: FailoverPolicy.Automatic,
|
||||
};
|
||||
|
||||
/** Manages a set of replicas */
|
||||
export class ReplicaSet extends EventEmitter {
|
||||
private replicas: Map<ReplicaId, Replica> = new Map();
|
||||
private config: ReplicaSetConfig;
|
||||
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(name: string, config?: Partial<ReplicaSetConfig>) {
|
||||
super();
|
||||
this.config = { ...DEFAULT_CONFIG, name, ...config };
|
||||
}
|
||||
|
||||
/** Get replica set name */
|
||||
get name(): string {
|
||||
return this.config.name;
|
||||
}
|
||||
|
||||
/** Get the primary replica */
|
||||
get primary(): Replica | undefined {
|
||||
for (const replica of this.replicas.values()) {
|
||||
if (replica.role === ReplicaRole.Primary && replica.status === ReplicaStatus.Active) {
|
||||
return replica;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Get all secondary replicas */
|
||||
get secondaries(): Replica[] {
|
||||
return Array.from(this.replicas.values()).filter(
|
||||
(r) => r.role === ReplicaRole.Secondary && r.status === ReplicaStatus.Active,
|
||||
);
|
||||
}
|
||||
|
||||
/** Get all active replicas */
|
||||
get activeReplicas(): Replica[] {
|
||||
return Array.from(this.replicas.values()).filter((r) => r.status === ReplicaStatus.Active);
|
||||
}
|
||||
|
||||
/** Get replica count */
|
||||
get size(): number {
|
||||
return this.replicas.size;
|
||||
}
|
||||
|
||||
/** Check if quorum is met */
|
||||
get hasQuorum(): boolean {
|
||||
const activeCount = this.activeReplicas.length;
|
||||
return activeCount >= this.config.minQuorum;
|
||||
}
|
||||
|
||||
/** Add a replica to the set */
|
||||
addReplica(id: ReplicaId, address: string, role: ReplicaRole): Replica {
|
||||
if (this.replicas.has(id)) {
|
||||
throw new Error(`Replica ${id} already exists`);
|
||||
}
|
||||
|
||||
// Check if adding a primary when one exists
|
||||
if (role === ReplicaRole.Primary && this.primary) {
|
||||
throw new Error('Primary already exists in replica set');
|
||||
}
|
||||
|
||||
const replica: Replica = {
|
||||
id,
|
||||
address,
|
||||
role,
|
||||
status: ReplicaStatus.Active,
|
||||
lastSeen: Date.now(),
|
||||
lag: 0,
|
||||
};
|
||||
|
||||
this.replicas.set(id, replica);
|
||||
this.emit(ReplicationEvent.ReplicaAdded, replica);
|
||||
|
||||
return replica;
|
||||
}
|
||||
|
||||
/** Remove a replica from the set */
|
||||
removeReplica(id: ReplicaId): boolean {
|
||||
const replica = this.replicas.get(id);
|
||||
if (!replica) return false;
|
||||
|
||||
this.replicas.delete(id);
|
||||
this.emit(ReplicationEvent.ReplicaRemoved, replica);
|
||||
|
||||
// If primary was removed, trigger failover
|
||||
if (replica.role === ReplicaRole.Primary && this.config.failoverPolicy === FailoverPolicy.Automatic) {
|
||||
this.triggerFailover();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Get a replica by ID */
|
||||
getReplica(id: ReplicaId): Replica | undefined {
|
||||
return this.replicas.get(id);
|
||||
}
|
||||
|
||||
/** Update replica status */
|
||||
updateStatus(id: ReplicaId, status: ReplicaStatus): void {
|
||||
const replica = this.replicas.get(id);
|
||||
if (!replica) {
|
||||
throw ReplicationError.replicaNotFound(id);
|
||||
}
|
||||
|
||||
const previousStatus = replica.status;
|
||||
replica.status = status;
|
||||
replica.lastSeen = Date.now();
|
||||
|
||||
if (previousStatus !== status) {
|
||||
this.emit(ReplicationEvent.ReplicaStatusChanged, {
|
||||
replica,
|
||||
previousStatus,
|
||||
newStatus: status,
|
||||
});
|
||||
|
||||
// Check for failover conditions
|
||||
if (
|
||||
replica.role === ReplicaRole.Primary &&
|
||||
status === ReplicaStatus.Failed &&
|
||||
this.config.failoverPolicy === FailoverPolicy.Automatic
|
||||
) {
|
||||
this.triggerFailover();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Update replica lag */
|
||||
updateLag(id: ReplicaId, lag: number): void {
|
||||
const replica = this.replicas.get(id);
|
||||
if (replica) {
|
||||
replica.lag = lag;
|
||||
replica.lastSeen = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
/** Promote a secondary to primary */
|
||||
promote(id: ReplicaId): void {
|
||||
const replica = this.replicas.get(id);
|
||||
if (!replica) {
|
||||
throw ReplicationError.replicaNotFound(id);
|
||||
}
|
||||
|
||||
if (replica.role === ReplicaRole.Primary) {
|
||||
return; // Already primary
|
||||
}
|
||||
|
||||
// Demote current primary
|
||||
const currentPrimary = this.primary;
|
||||
if (currentPrimary) {
|
||||
currentPrimary.role = ReplicaRole.Secondary;
|
||||
}
|
||||
|
||||
// Promote new primary
|
||||
replica.role = ReplicaRole.Primary;
|
||||
this.emit(ReplicationEvent.PrimaryChanged, {
|
||||
previousPrimary: currentPrimary?.id,
|
||||
newPrimary: id,
|
||||
});
|
||||
}
|
||||
|
||||
/** Trigger automatic failover */
|
||||
private triggerFailover(): void {
|
||||
this.emit(ReplicationEvent.FailoverStarted, {});
|
||||
|
||||
// Find the best candidate (lowest lag, active secondary)
|
||||
const candidates = this.secondaries
|
||||
.filter((r) => r.status === ReplicaStatus.Active)
|
||||
.sort((a, b) => a.lag - b.lag);
|
||||
|
||||
if (candidates.length === 0) {
|
||||
this.emit(ReplicationEvent.Error, ReplicationError.noPrimary());
|
||||
return;
|
||||
}
|
||||
|
||||
const newPrimary = candidates[0];
|
||||
this.promote(newPrimary.id);
|
||||
|
||||
this.emit(ReplicationEvent.FailoverCompleted, { newPrimary: newPrimary.id });
|
||||
}
|
||||
|
||||
/** Start heartbeat monitoring */
|
||||
startHeartbeat(): void {
|
||||
if (this.heartbeatTimer) return;
|
||||
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const replica of this.replicas.values()) {
|
||||
if (now - replica.lastSeen > this.config.healthCheckTimeout) {
|
||||
if (replica.status === ReplicaStatus.Active) {
|
||||
this.updateStatus(replica.id, ReplicaStatus.Offline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this.config.heartbeatInterval);
|
||||
}
|
||||
|
||||
/** Stop heartbeat monitoring */
|
||||
stopHeartbeat(): void {
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get all replicas */
|
||||
getAllReplicas(): Replica[] {
|
||||
return Array.from(this.replicas.values());
|
||||
}
|
||||
|
||||
/** Get replica set stats */
|
||||
getStats(): {
|
||||
total: number;
|
||||
active: number;
|
||||
syncing: number;
|
||||
offline: number;
|
||||
failed: number;
|
||||
hasQuorum: boolean;
|
||||
} {
|
||||
const replicas = Array.from(this.replicas.values());
|
||||
return {
|
||||
total: replicas.length,
|
||||
active: replicas.filter((r) => r.status === ReplicaStatus.Active).length,
|
||||
syncing: replicas.filter((r) => r.status === ReplicaStatus.Syncing).length,
|
||||
offline: replicas.filter((r) => r.status === ReplicaStatus.Offline).length,
|
||||
failed: replicas.filter((r) => r.status === ReplicaStatus.Failed).length,
|
||||
hasQuorum: this.hasQuorum,
|
||||
};
|
||||
}
|
||||
}
|
||||
65
vendor/ruvector/npm/packages/replication/src/sync-manager.d.ts
vendored
Normal file
65
vendor/ruvector/npm/packages/replication/src/sync-manager.d.ts
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Sync Manager Implementation
|
||||
* Manages data synchronization across replicas
|
||||
*/
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { type ReplicaId, type SyncConfig, type LogEntry, SyncMode, ChangeOperation } from './types.js';
|
||||
import { VectorClock, type ConflictResolver } from './vector-clock.js';
|
||||
import type { ReplicaSet } from './replica-set.js';
|
||||
/** Replication log for tracking changes */
|
||||
export declare class ReplicationLog<T = unknown> {
|
||||
private entries;
|
||||
private sequence;
|
||||
private readonly replicaId;
|
||||
private vectorClock;
|
||||
constructor(replicaId: ReplicaId);
|
||||
/** Get the current sequence number */
|
||||
get currentSequence(): number;
|
||||
/** Get the current vector clock */
|
||||
get clock(): VectorClock;
|
||||
/** Append an entry to the log */
|
||||
append(data: T): LogEntry<T>;
|
||||
/** Get entries since a sequence number */
|
||||
getEntriesSince(sequence: number, limit?: number): LogEntry<T>[];
|
||||
/** Get entry by ID */
|
||||
getEntry(id: string): LogEntry<T> | undefined;
|
||||
/** Get all entries */
|
||||
getAllEntries(): LogEntry<T>[];
|
||||
/** Apply entries from another replica */
|
||||
applyEntries(entries: LogEntry<T>[]): void;
|
||||
/** Clear the log */
|
||||
clear(): void;
|
||||
}
|
||||
/** Manages synchronization across replicas */
|
||||
export declare class SyncManager<T = unknown> extends EventEmitter {
|
||||
private readonly replicaSet;
|
||||
private readonly log;
|
||||
private config;
|
||||
private conflictResolver;
|
||||
private pendingChanges;
|
||||
private syncTimer;
|
||||
constructor(replicaSet: ReplicaSet, log: ReplicationLog<T>, config?: Partial<SyncConfig>);
|
||||
/** Set sync mode */
|
||||
setSyncMode(mode: SyncMode, minReplicas?: number): void;
|
||||
/** Set custom conflict resolver */
|
||||
setConflictResolver(resolver: ConflictResolver<T>): void;
|
||||
/** Record a change for replication */
|
||||
recordChange(key: string, operation: ChangeOperation, value?: T, previousValue?: T): Promise<void>;
|
||||
/** Sync a change to all replicas */
|
||||
private syncAll;
|
||||
/** Sync to minimum number of replicas (semi-sync) */
|
||||
private syncMinimum;
|
||||
/** Start background sync for async mode */
|
||||
startBackgroundSync(interval?: number): void;
|
||||
/** Stop background sync */
|
||||
stopBackgroundSync(): void;
|
||||
/** Resolve a conflict between local and remote values */
|
||||
resolveConflict(local: T, remote: T, localClock: VectorClock, remoteClock: VectorClock): T;
|
||||
/** Get sync statistics */
|
||||
getStats(): {
|
||||
pendingChanges: number;
|
||||
lastSequence: number;
|
||||
syncMode: SyncMode;
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=sync-manager.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/sync-manager.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/sync-manager.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sync-manager.d.ts","sourceRoot":"","sources":["sync-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,QAAQ,EAEb,QAAQ,EAGR,eAAe,EAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAiB,MAAM,mBAAmB,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AASnD,2CAA2C;AAC3C,qBAAa,cAAc,CAAC,CAAC,GAAG,OAAO;IACrC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,WAAW,CAAc;gBAErB,SAAS,EAAE,SAAS;IAKhC,sCAAsC;IACtC,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,mCAAmC;IACnC,IAAI,KAAK,IAAI,WAAW,CAEvB;IAED,iCAAiC;IACjC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAgB5B,0CAA0C;IAC1C,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE;IAKhE,sBAAsB;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS;IAI7C,sBAAsB;IACtB,aAAa,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;IAI9B,yCAAyC;IACzC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAQ1C,oBAAoB;IACpB,KAAK,IAAI,IAAI;CAKd;AAED,8CAA8C;AAC9C,qBAAa,WAAW,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,YAAY;IACxD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,SAAS,CAA+C;gBAG9D,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,EACtB,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC;IAU9B,oBAAoB;IACpB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAOvD,mCAAmC;IACnC,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI;IAIxD,sCAAsC;IAChC,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,eAAe,EAC1B,KAAK,CAAC,EAAE,CAAC,EACT,aAAa,CAAC,EAAE,CAAC,GAChB,OAAO,CAAC,IAAI,CAAC;IAmChB,oCAAoC;YACtB,OAAO;IAWrB,qDAAqD;YACvC,WAAW;IAgBzB,2CAA2C;IAC3C,mBAAmB,CAAC,QAAQ,GAAE,MAAa,GAAG,IAAI;IAalD,2BAA2B;IAC3B,kBAAkB,IAAI,IAAI;IAO1B,yDAAyD;IACzD,eAAe,CACb,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,EACT,UAAU,EAAE,WAAW,EACvB,WAAW,EAAE,WAAW,GACvB,CAAC;IAgBJ,0BAA0B;IAC1B,QAAQ,IAAI;QACV,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,QAAQ,CAAC;KACpB;CAOF"}
|
||||
201
vendor/ruvector/npm/packages/replication/src/sync-manager.js
vendored
Normal file
201
vendor/ruvector/npm/packages/replication/src/sync-manager.js
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Sync Manager Implementation
|
||||
* Manages data synchronization across replicas
|
||||
*/
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SyncManager = exports.ReplicationLog = void 0;
|
||||
const eventemitter3_1 = __importDefault(require("eventemitter3"));
|
||||
const types_js_1 = require("./types.js");
|
||||
const vector_clock_js_1 = require("./vector-clock.js");
|
||||
/** Default sync configuration */
|
||||
const DEFAULT_SYNC_CONFIG = {
|
||||
mode: types_js_1.SyncMode.Asynchronous,
|
||||
batchSize: 100,
|
||||
maxLag: 5000,
|
||||
};
|
||||
/** Replication log for tracking changes */
|
||||
class ReplicationLog {
|
||||
constructor(replicaId) {
|
||||
this.entries = [];
|
||||
this.sequence = 0;
|
||||
this.replicaId = replicaId;
|
||||
this.vectorClock = new vector_clock_js_1.VectorClock();
|
||||
}
|
||||
/** Get the current sequence number */
|
||||
get currentSequence() {
|
||||
return this.sequence;
|
||||
}
|
||||
/** Get the current vector clock */
|
||||
get clock() {
|
||||
return this.vectorClock.clone();
|
||||
}
|
||||
/** Append an entry to the log */
|
||||
append(data) {
|
||||
this.sequence++;
|
||||
this.vectorClock.increment(this.replicaId);
|
||||
const entry = {
|
||||
id: `${this.replicaId}-${this.sequence}`,
|
||||
sequence: this.sequence,
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
vectorClock: this.vectorClock.getValue(),
|
||||
};
|
||||
this.entries.push(entry);
|
||||
return entry;
|
||||
}
|
||||
/** Get entries since a sequence number */
|
||||
getEntriesSince(sequence, limit) {
|
||||
const filtered = this.entries.filter((e) => e.sequence > sequence);
|
||||
return limit ? filtered.slice(0, limit) : filtered;
|
||||
}
|
||||
/** Get entry by ID */
|
||||
getEntry(id) {
|
||||
return this.entries.find((e) => e.id === id);
|
||||
}
|
||||
/** Get all entries */
|
||||
getAllEntries() {
|
||||
return [...this.entries];
|
||||
}
|
||||
/** Apply entries from another replica */
|
||||
applyEntries(entries) {
|
||||
for (const entry of entries) {
|
||||
const entryClock = new vector_clock_js_1.VectorClock(entry.vectorClock);
|
||||
this.vectorClock.merge(entryClock);
|
||||
}
|
||||
// Note: In a real implementation, entries would be merged properly
|
||||
}
|
||||
/** Clear the log */
|
||||
clear() {
|
||||
this.entries = [];
|
||||
this.sequence = 0;
|
||||
this.vectorClock = new vector_clock_js_1.VectorClock();
|
||||
}
|
||||
}
|
||||
exports.ReplicationLog = ReplicationLog;
|
||||
/** Manages synchronization across replicas */
|
||||
class SyncManager extends eventemitter3_1.default {
|
||||
constructor(replicaSet, log, config) {
|
||||
super();
|
||||
this.pendingChanges = [];
|
||||
this.syncTimer = null;
|
||||
this.replicaSet = replicaSet;
|
||||
this.log = log;
|
||||
this.config = { ...DEFAULT_SYNC_CONFIG, ...config };
|
||||
// Default to timestamp-based resolution
|
||||
this.conflictResolver = new vector_clock_js_1.LastWriteWins();
|
||||
}
|
||||
/** Set sync mode */
|
||||
setSyncMode(mode, minReplicas) {
|
||||
this.config.mode = mode;
|
||||
if (minReplicas !== undefined) {
|
||||
this.config.minReplicas = minReplicas;
|
||||
}
|
||||
}
|
||||
/** Set custom conflict resolver */
|
||||
setConflictResolver(resolver) {
|
||||
this.conflictResolver = resolver;
|
||||
}
|
||||
/** Record a change for replication */
|
||||
async recordChange(key, operation, value, previousValue) {
|
||||
const primary = this.replicaSet.primary;
|
||||
if (!primary) {
|
||||
throw types_js_1.ReplicationError.noPrimary();
|
||||
}
|
||||
const entry = this.log.append({ key, operation, value, previousValue });
|
||||
const change = {
|
||||
id: entry.id,
|
||||
operation,
|
||||
key,
|
||||
value,
|
||||
previousValue,
|
||||
timestamp: entry.timestamp,
|
||||
sourceReplica: primary.id,
|
||||
vectorClock: entry.vectorClock,
|
||||
};
|
||||
this.emit(types_js_1.ReplicationEvent.ChangeReceived, change);
|
||||
// Handle based on sync mode
|
||||
switch (this.config.mode) {
|
||||
case types_js_1.SyncMode.Synchronous:
|
||||
await this.syncAll(change);
|
||||
break;
|
||||
case types_js_1.SyncMode.SemiSync:
|
||||
await this.syncMinimum(change);
|
||||
break;
|
||||
case types_js_1.SyncMode.Asynchronous:
|
||||
this.pendingChanges.push(change);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/** Sync a change to all replicas */
|
||||
async syncAll(change) {
|
||||
const secondaries = this.replicaSet.secondaries;
|
||||
if (secondaries.length === 0)
|
||||
return;
|
||||
this.emit(types_js_1.ReplicationEvent.SyncStarted, { replicas: secondaries.map((r) => r.id) });
|
||||
// In a real implementation, this would send to all replicas
|
||||
// For now, we just emit the completion event
|
||||
this.emit(types_js_1.ReplicationEvent.SyncCompleted, { change, replicas: secondaries.map((r) => r.id) });
|
||||
}
|
||||
/** Sync to minimum number of replicas (semi-sync) */
|
||||
async syncMinimum(change) {
|
||||
const minReplicas = this.config.minReplicas ?? 1;
|
||||
const secondaries = this.replicaSet.secondaries;
|
||||
if (secondaries.length < minReplicas) {
|
||||
throw types_js_1.ReplicationError.quorumNotMet(minReplicas, secondaries.length);
|
||||
}
|
||||
// Sync to minimum number of replicas
|
||||
const targetReplicas = secondaries.slice(0, minReplicas);
|
||||
this.emit(types_js_1.ReplicationEvent.SyncStarted, { replicas: targetReplicas.map((r) => r.id) });
|
||||
// In a real implementation, this would wait for acknowledgments
|
||||
this.emit(types_js_1.ReplicationEvent.SyncCompleted, { change, replicas: targetReplicas.map((r) => r.id) });
|
||||
}
|
||||
/** Start background sync for async mode */
|
||||
startBackgroundSync(interval = 1000) {
|
||||
if (this.syncTimer)
|
||||
return;
|
||||
this.syncTimer = setInterval(async () => {
|
||||
if (this.pendingChanges.length > 0) {
|
||||
const batch = this.pendingChanges.splice(0, this.config.batchSize);
|
||||
for (const change of batch) {
|
||||
await this.syncAll(change);
|
||||
}
|
||||
}
|
||||
}, interval);
|
||||
}
|
||||
/** Stop background sync */
|
||||
stopBackgroundSync() {
|
||||
if (this.syncTimer) {
|
||||
clearInterval(this.syncTimer);
|
||||
this.syncTimer = null;
|
||||
}
|
||||
}
|
||||
/** Resolve a conflict between local and remote values */
|
||||
resolveConflict(local, remote, localClock, remoteClock) {
|
||||
// Check for causal relationship
|
||||
if (localClock.happensBefore(remoteClock)) {
|
||||
return remote; // Remote is newer
|
||||
}
|
||||
else if (localClock.happensAfter(remoteClock)) {
|
||||
return local; // Local is newer
|
||||
}
|
||||
// Concurrent - need conflict resolution
|
||||
this.emit(types_js_1.ReplicationEvent.ConflictDetected, { local, remote });
|
||||
const resolved = this.conflictResolver.resolve(local, remote, localClock, remoteClock);
|
||||
this.emit(types_js_1.ReplicationEvent.ConflictResolved, { local, remote, resolved });
|
||||
return resolved;
|
||||
}
|
||||
/** Get sync statistics */
|
||||
getStats() {
|
||||
return {
|
||||
pendingChanges: this.pendingChanges.length,
|
||||
lastSequence: this.log.currentSequence,
|
||||
syncMode: this.config.mode,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.SyncManager = SyncManager;
|
||||
//# sourceMappingURL=sync-manager.js.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/sync-manager.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/sync-manager.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
260
vendor/ruvector/npm/packages/replication/src/sync-manager.ts
vendored
Normal file
260
vendor/ruvector/npm/packages/replication/src/sync-manager.ts
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* Sync Manager Implementation
|
||||
* Manages data synchronization across replicas
|
||||
*/
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import {
|
||||
type ReplicaId,
|
||||
type SyncConfig,
|
||||
type LogEntry,
|
||||
type ChangeEvent,
|
||||
SyncMode,
|
||||
ReplicationError,
|
||||
ReplicationEvent,
|
||||
ChangeOperation,
|
||||
} from './types.js';
|
||||
import { VectorClock, type ConflictResolver, LastWriteWins } from './vector-clock.js';
|
||||
import type { ReplicaSet } from './replica-set.js';
|
||||
|
||||
/** Default sync configuration */
|
||||
const DEFAULT_SYNC_CONFIG: SyncConfig = {
|
||||
mode: SyncMode.Asynchronous,
|
||||
batchSize: 100,
|
||||
maxLag: 5000,
|
||||
};
|
||||
|
||||
/** Replication log for tracking changes */
|
||||
export class ReplicationLog<T = unknown> {
|
||||
private entries: LogEntry<T>[] = [];
|
||||
private sequence = 0;
|
||||
private readonly replicaId: ReplicaId;
|
||||
private vectorClock: VectorClock;
|
||||
|
||||
constructor(replicaId: ReplicaId) {
|
||||
this.replicaId = replicaId;
|
||||
this.vectorClock = new VectorClock();
|
||||
}
|
||||
|
||||
/** Get the current sequence number */
|
||||
get currentSequence(): number {
|
||||
return this.sequence;
|
||||
}
|
||||
|
||||
/** Get the current vector clock */
|
||||
get clock(): VectorClock {
|
||||
return this.vectorClock.clone();
|
||||
}
|
||||
|
||||
/** Append an entry to the log */
|
||||
append(data: T): LogEntry<T> {
|
||||
this.sequence++;
|
||||
this.vectorClock.increment(this.replicaId);
|
||||
|
||||
const entry: LogEntry<T> = {
|
||||
id: `${this.replicaId}-${this.sequence}`,
|
||||
sequence: this.sequence,
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
vectorClock: this.vectorClock.getValue(),
|
||||
};
|
||||
|
||||
this.entries.push(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/** Get entries since a sequence number */
|
||||
getEntriesSince(sequence: number, limit?: number): LogEntry<T>[] {
|
||||
const filtered = this.entries.filter((e) => e.sequence > sequence);
|
||||
return limit ? filtered.slice(0, limit) : filtered;
|
||||
}
|
||||
|
||||
/** Get entry by ID */
|
||||
getEntry(id: string): LogEntry<T> | undefined {
|
||||
return this.entries.find((e) => e.id === id);
|
||||
}
|
||||
|
||||
/** Get all entries */
|
||||
getAllEntries(): LogEntry<T>[] {
|
||||
return [...this.entries];
|
||||
}
|
||||
|
||||
/** Apply entries from another replica */
|
||||
applyEntries(entries: LogEntry<T>[]): void {
|
||||
for (const entry of entries) {
|
||||
const entryClock = new VectorClock(entry.vectorClock);
|
||||
this.vectorClock.merge(entryClock);
|
||||
}
|
||||
// Note: In a real implementation, entries would be merged properly
|
||||
}
|
||||
|
||||
/** Clear the log */
|
||||
clear(): void {
|
||||
this.entries = [];
|
||||
this.sequence = 0;
|
||||
this.vectorClock = new VectorClock();
|
||||
}
|
||||
}
|
||||
|
||||
/** Manages synchronization across replicas */
|
||||
export class SyncManager<T = unknown> extends EventEmitter {
|
||||
private readonly replicaSet: ReplicaSet;
|
||||
private readonly log: ReplicationLog<T>;
|
||||
private config: SyncConfig;
|
||||
private conflictResolver: ConflictResolver<T>;
|
||||
private pendingChanges: ChangeEvent<T>[] = [];
|
||||
private syncTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(
|
||||
replicaSet: ReplicaSet,
|
||||
log: ReplicationLog<T>,
|
||||
config?: Partial<SyncConfig>,
|
||||
) {
|
||||
super();
|
||||
this.replicaSet = replicaSet;
|
||||
this.log = log;
|
||||
this.config = { ...DEFAULT_SYNC_CONFIG, ...config };
|
||||
// Default to timestamp-based resolution
|
||||
this.conflictResolver = new LastWriteWins() as unknown as ConflictResolver<T>;
|
||||
}
|
||||
|
||||
/** Set sync mode */
|
||||
setSyncMode(mode: SyncMode, minReplicas?: number): void {
|
||||
this.config.mode = mode;
|
||||
if (minReplicas !== undefined) {
|
||||
this.config.minReplicas = minReplicas;
|
||||
}
|
||||
}
|
||||
|
||||
/** Set custom conflict resolver */
|
||||
setConflictResolver(resolver: ConflictResolver<T>): void {
|
||||
this.conflictResolver = resolver;
|
||||
}
|
||||
|
||||
/** Record a change for replication */
|
||||
async recordChange(
|
||||
key: string,
|
||||
operation: ChangeOperation,
|
||||
value?: T,
|
||||
previousValue?: T,
|
||||
): Promise<void> {
|
||||
const primary = this.replicaSet.primary;
|
||||
if (!primary) {
|
||||
throw ReplicationError.noPrimary();
|
||||
}
|
||||
|
||||
const entry = this.log.append({ key, operation, value, previousValue } as unknown as T);
|
||||
|
||||
const change: ChangeEvent<T> = {
|
||||
id: entry.id,
|
||||
operation,
|
||||
key,
|
||||
value,
|
||||
previousValue,
|
||||
timestamp: entry.timestamp,
|
||||
sourceReplica: primary.id,
|
||||
vectorClock: entry.vectorClock,
|
||||
};
|
||||
|
||||
this.emit(ReplicationEvent.ChangeReceived, change);
|
||||
|
||||
// Handle based on sync mode
|
||||
switch (this.config.mode) {
|
||||
case SyncMode.Synchronous:
|
||||
await this.syncAll(change);
|
||||
break;
|
||||
case SyncMode.SemiSync:
|
||||
await this.syncMinimum(change);
|
||||
break;
|
||||
case SyncMode.Asynchronous:
|
||||
this.pendingChanges.push(change);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sync a change to all replicas */
|
||||
private async syncAll(change: ChangeEvent<T>): Promise<void> {
|
||||
const secondaries = this.replicaSet.secondaries;
|
||||
if (secondaries.length === 0) return;
|
||||
|
||||
this.emit(ReplicationEvent.SyncStarted, { replicas: secondaries.map((r) => r.id) });
|
||||
|
||||
// In a real implementation, this would send to all replicas
|
||||
// For now, we just emit the completion event
|
||||
this.emit(ReplicationEvent.SyncCompleted, { change, replicas: secondaries.map((r) => r.id) });
|
||||
}
|
||||
|
||||
/** Sync to minimum number of replicas (semi-sync) */
|
||||
private async syncMinimum(change: ChangeEvent<T>): Promise<void> {
|
||||
const minReplicas = this.config.minReplicas ?? 1;
|
||||
const secondaries = this.replicaSet.secondaries;
|
||||
|
||||
if (secondaries.length < minReplicas) {
|
||||
throw ReplicationError.quorumNotMet(minReplicas, secondaries.length);
|
||||
}
|
||||
|
||||
// Sync to minimum number of replicas
|
||||
const targetReplicas = secondaries.slice(0, minReplicas);
|
||||
this.emit(ReplicationEvent.SyncStarted, { replicas: targetReplicas.map((r) => r.id) });
|
||||
|
||||
// In a real implementation, this would wait for acknowledgments
|
||||
this.emit(ReplicationEvent.SyncCompleted, { change, replicas: targetReplicas.map((r) => r.id) });
|
||||
}
|
||||
|
||||
/** Start background sync for async mode */
|
||||
startBackgroundSync(interval: number = 1000): void {
|
||||
if (this.syncTimer) return;
|
||||
|
||||
this.syncTimer = setInterval(async () => {
|
||||
if (this.pendingChanges.length > 0) {
|
||||
const batch = this.pendingChanges.splice(0, this.config.batchSize);
|
||||
for (const change of batch) {
|
||||
await this.syncAll(change);
|
||||
}
|
||||
}
|
||||
}, interval);
|
||||
}
|
||||
|
||||
/** Stop background sync */
|
||||
stopBackgroundSync(): void {
|
||||
if (this.syncTimer) {
|
||||
clearInterval(this.syncTimer);
|
||||
this.syncTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolve a conflict between local and remote values */
|
||||
resolveConflict(
|
||||
local: T,
|
||||
remote: T,
|
||||
localClock: VectorClock,
|
||||
remoteClock: VectorClock,
|
||||
): T {
|
||||
// Check for causal relationship
|
||||
if (localClock.happensBefore(remoteClock)) {
|
||||
return remote; // Remote is newer
|
||||
} else if (localClock.happensAfter(remoteClock)) {
|
||||
return local; // Local is newer
|
||||
}
|
||||
|
||||
// Concurrent - need conflict resolution
|
||||
this.emit(ReplicationEvent.ConflictDetected, { local, remote });
|
||||
const resolved = this.conflictResolver.resolve(local, remote, localClock, remoteClock);
|
||||
this.emit(ReplicationEvent.ConflictResolved, { local, remote, resolved });
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/** Get sync statistics */
|
||||
getStats(): {
|
||||
pendingChanges: number;
|
||||
lastSequence: number;
|
||||
syncMode: SyncMode;
|
||||
} {
|
||||
return {
|
||||
pendingChanges: this.pendingChanges.length,
|
||||
lastSequence: this.log.currentSequence,
|
||||
syncMode: this.config.mode,
|
||||
};
|
||||
}
|
||||
}
|
||||
158
vendor/ruvector/npm/packages/replication/src/types.d.ts
vendored
Normal file
158
vendor/ruvector/npm/packages/replication/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Replication Types
|
||||
* Data replication and synchronization types
|
||||
*/
|
||||
/** Unique identifier for a replica */
|
||||
export type ReplicaId = string;
|
||||
/** Logical timestamp for ordering events */
|
||||
export type LogicalClock = number;
|
||||
/** Role of a replica in the set */
|
||||
export declare enum ReplicaRole {
|
||||
Primary = "primary",
|
||||
Secondary = "secondary",
|
||||
Arbiter = "arbiter"
|
||||
}
|
||||
/** Status of a replica */
|
||||
export declare enum ReplicaStatus {
|
||||
Active = "active",
|
||||
Syncing = "syncing",
|
||||
Offline = "offline",
|
||||
Failed = "failed"
|
||||
}
|
||||
/** Synchronization mode */
|
||||
export declare enum SyncMode {
|
||||
/** All replicas must confirm before commit */
|
||||
Synchronous = "synchronous",
|
||||
/** Commit immediately, replicate in background */
|
||||
Asynchronous = "asynchronous",
|
||||
/** Wait for minimum number of replicas */
|
||||
SemiSync = "semi-sync"
|
||||
}
|
||||
/** Health status of a replica */
|
||||
export declare enum HealthStatus {
|
||||
Healthy = "healthy",
|
||||
Degraded = "degraded",
|
||||
Unhealthy = "unhealthy",
|
||||
Unknown = "unknown"
|
||||
}
|
||||
/** Replica information */
|
||||
export interface Replica {
|
||||
id: ReplicaId;
|
||||
address: string;
|
||||
role: ReplicaRole;
|
||||
status: ReplicaStatus;
|
||||
lastSeen: number;
|
||||
lag: number;
|
||||
}
|
||||
/** Change operation type */
|
||||
export declare enum ChangeOperation {
|
||||
Insert = "insert",
|
||||
Update = "update",
|
||||
Delete = "delete"
|
||||
}
|
||||
/** Change event for CDC */
|
||||
export interface ChangeEvent<T = unknown> {
|
||||
/** Unique event ID */
|
||||
id: string;
|
||||
/** Operation type */
|
||||
operation: ChangeOperation;
|
||||
/** Affected key/path */
|
||||
key: string;
|
||||
/** New value (for insert/update) */
|
||||
value?: T;
|
||||
/** Previous value (for update/delete) */
|
||||
previousValue?: T;
|
||||
/** Timestamp of the change */
|
||||
timestamp: number;
|
||||
/** Source replica */
|
||||
sourceReplica: ReplicaId;
|
||||
/** Vector clock for ordering */
|
||||
vectorClock: VectorClockValue;
|
||||
}
|
||||
/** Vector clock entry for a single node */
|
||||
export type VectorClockValue = Map<ReplicaId, LogicalClock>;
|
||||
/** Log entry for replication */
|
||||
export interface LogEntry<T = unknown> {
|
||||
/** Unique entry ID */
|
||||
id: string;
|
||||
/** Sequence number */
|
||||
sequence: number;
|
||||
/** Operation data */
|
||||
data: T;
|
||||
/** Timestamp */
|
||||
timestamp: number;
|
||||
/** Vector clock */
|
||||
vectorClock: VectorClockValue;
|
||||
}
|
||||
/** Failover policy */
|
||||
export declare enum FailoverPolicy {
|
||||
/** Automatic failover with quorum */
|
||||
Automatic = "automatic",
|
||||
/** Manual intervention required */
|
||||
Manual = "manual",
|
||||
/** Priority-based failover */
|
||||
Priority = "priority"
|
||||
}
|
||||
/** Replication error types */
|
||||
export declare class ReplicationError extends Error {
|
||||
readonly code: ReplicationErrorCode;
|
||||
constructor(message: string, code: ReplicationErrorCode);
|
||||
static replicaNotFound(id: string): ReplicationError;
|
||||
static noPrimary(): ReplicationError;
|
||||
static timeout(operation: string): ReplicationError;
|
||||
static quorumNotMet(needed: number, available: number): ReplicationError;
|
||||
static splitBrain(): ReplicationError;
|
||||
static conflictResolution(reason: string): ReplicationError;
|
||||
}
|
||||
export declare enum ReplicationErrorCode {
|
||||
ReplicaNotFound = "REPLICA_NOT_FOUND",
|
||||
NoPrimary = "NO_PRIMARY",
|
||||
Timeout = "TIMEOUT",
|
||||
SyncFailed = "SYNC_FAILED",
|
||||
ConflictResolution = "CONFLICT_RESOLUTION",
|
||||
FailoverFailed = "FAILOVER_FAILED",
|
||||
Network = "NETWORK",
|
||||
QuorumNotMet = "QUORUM_NOT_MET",
|
||||
SplitBrain = "SPLIT_BRAIN",
|
||||
InvalidState = "INVALID_STATE"
|
||||
}
|
||||
/** Events emitted by replication components */
|
||||
export declare enum ReplicationEvent {
|
||||
ReplicaAdded = "replicaAdded",
|
||||
ReplicaRemoved = "replicaRemoved",
|
||||
ReplicaStatusChanged = "replicaStatusChanged",
|
||||
PrimaryChanged = "primaryChanged",
|
||||
ChangeReceived = "changeReceived",
|
||||
SyncStarted = "syncStarted",
|
||||
SyncCompleted = "syncCompleted",
|
||||
ConflictDetected = "conflictDetected",
|
||||
ConflictResolved = "conflictResolved",
|
||||
FailoverStarted = "failoverStarted",
|
||||
FailoverCompleted = "failoverCompleted",
|
||||
Error = "error"
|
||||
}
|
||||
/** Configuration for replica set */
|
||||
export interface ReplicaSetConfig {
|
||||
/** Cluster name */
|
||||
name: string;
|
||||
/** Minimum replicas for quorum */
|
||||
minQuorum: number;
|
||||
/** Heartbeat interval in milliseconds */
|
||||
heartbeatInterval: number;
|
||||
/** Timeout for health checks in milliseconds */
|
||||
healthCheckTimeout: number;
|
||||
/** Failover policy */
|
||||
failoverPolicy: FailoverPolicy;
|
||||
}
|
||||
/** Configuration for sync manager */
|
||||
export interface SyncConfig {
|
||||
/** Sync mode */
|
||||
mode: SyncMode;
|
||||
/** Minimum replicas for semi-sync */
|
||||
minReplicas?: number;
|
||||
/** Batch size for streaming changes */
|
||||
batchSize: number;
|
||||
/** Maximum lag before triggering catchup */
|
||||
maxLag: number;
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/types.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/types.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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"}
|
||||
114
vendor/ruvector/npm/packages/replication/src/types.js
vendored
Normal file
114
vendor/ruvector/npm/packages/replication/src/types.js
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Replication Types
|
||||
* Data replication and synchronization types
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ReplicationEvent = exports.ReplicationErrorCode = exports.ReplicationError = exports.FailoverPolicy = exports.ChangeOperation = exports.HealthStatus = exports.SyncMode = exports.ReplicaStatus = exports.ReplicaRole = void 0;
|
||||
/** Role of a replica in the set */
|
||||
var ReplicaRole;
|
||||
(function (ReplicaRole) {
|
||||
ReplicaRole["Primary"] = "primary";
|
||||
ReplicaRole["Secondary"] = "secondary";
|
||||
ReplicaRole["Arbiter"] = "arbiter";
|
||||
})(ReplicaRole || (exports.ReplicaRole = ReplicaRole = {}));
|
||||
/** Status of a replica */
|
||||
var ReplicaStatus;
|
||||
(function (ReplicaStatus) {
|
||||
ReplicaStatus["Active"] = "active";
|
||||
ReplicaStatus["Syncing"] = "syncing";
|
||||
ReplicaStatus["Offline"] = "offline";
|
||||
ReplicaStatus["Failed"] = "failed";
|
||||
})(ReplicaStatus || (exports.ReplicaStatus = ReplicaStatus = {}));
|
||||
/** Synchronization mode */
|
||||
var SyncMode;
|
||||
(function (SyncMode) {
|
||||
/** All replicas must confirm before commit */
|
||||
SyncMode["Synchronous"] = "synchronous";
|
||||
/** Commit immediately, replicate in background */
|
||||
SyncMode["Asynchronous"] = "asynchronous";
|
||||
/** Wait for minimum number of replicas */
|
||||
SyncMode["SemiSync"] = "semi-sync";
|
||||
})(SyncMode || (exports.SyncMode = SyncMode = {}));
|
||||
/** Health status of a replica */
|
||||
var HealthStatus;
|
||||
(function (HealthStatus) {
|
||||
HealthStatus["Healthy"] = "healthy";
|
||||
HealthStatus["Degraded"] = "degraded";
|
||||
HealthStatus["Unhealthy"] = "unhealthy";
|
||||
HealthStatus["Unknown"] = "unknown";
|
||||
})(HealthStatus || (exports.HealthStatus = HealthStatus = {}));
|
||||
/** Change operation type */
|
||||
var ChangeOperation;
|
||||
(function (ChangeOperation) {
|
||||
ChangeOperation["Insert"] = "insert";
|
||||
ChangeOperation["Update"] = "update";
|
||||
ChangeOperation["Delete"] = "delete";
|
||||
})(ChangeOperation || (exports.ChangeOperation = ChangeOperation = {}));
|
||||
/** Failover policy */
|
||||
var FailoverPolicy;
|
||||
(function (FailoverPolicy) {
|
||||
/** Automatic failover with quorum */
|
||||
FailoverPolicy["Automatic"] = "automatic";
|
||||
/** Manual intervention required */
|
||||
FailoverPolicy["Manual"] = "manual";
|
||||
/** Priority-based failover */
|
||||
FailoverPolicy["Priority"] = "priority";
|
||||
})(FailoverPolicy || (exports.FailoverPolicy = FailoverPolicy = {}));
|
||||
/** Replication error types */
|
||||
class ReplicationError extends Error {
|
||||
constructor(message, code) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.name = 'ReplicationError';
|
||||
}
|
||||
static replicaNotFound(id) {
|
||||
return new ReplicationError(`Replica not found: ${id}`, ReplicationErrorCode.ReplicaNotFound);
|
||||
}
|
||||
static noPrimary() {
|
||||
return new ReplicationError('No primary replica available', ReplicationErrorCode.NoPrimary);
|
||||
}
|
||||
static timeout(operation) {
|
||||
return new ReplicationError(`Replication timeout: ${operation}`, ReplicationErrorCode.Timeout);
|
||||
}
|
||||
static quorumNotMet(needed, available) {
|
||||
return new ReplicationError(`Quorum not met: needed ${needed}, got ${available}`, ReplicationErrorCode.QuorumNotMet);
|
||||
}
|
||||
static splitBrain() {
|
||||
return new ReplicationError('Split-brain detected', ReplicationErrorCode.SplitBrain);
|
||||
}
|
||||
static conflictResolution(reason) {
|
||||
return new ReplicationError(`Conflict resolution failed: ${reason}`, ReplicationErrorCode.ConflictResolution);
|
||||
}
|
||||
}
|
||||
exports.ReplicationError = ReplicationError;
|
||||
var ReplicationErrorCode;
|
||||
(function (ReplicationErrorCode) {
|
||||
ReplicationErrorCode["ReplicaNotFound"] = "REPLICA_NOT_FOUND";
|
||||
ReplicationErrorCode["NoPrimary"] = "NO_PRIMARY";
|
||||
ReplicationErrorCode["Timeout"] = "TIMEOUT";
|
||||
ReplicationErrorCode["SyncFailed"] = "SYNC_FAILED";
|
||||
ReplicationErrorCode["ConflictResolution"] = "CONFLICT_RESOLUTION";
|
||||
ReplicationErrorCode["FailoverFailed"] = "FAILOVER_FAILED";
|
||||
ReplicationErrorCode["Network"] = "NETWORK";
|
||||
ReplicationErrorCode["QuorumNotMet"] = "QUORUM_NOT_MET";
|
||||
ReplicationErrorCode["SplitBrain"] = "SPLIT_BRAIN";
|
||||
ReplicationErrorCode["InvalidState"] = "INVALID_STATE";
|
||||
})(ReplicationErrorCode || (exports.ReplicationErrorCode = ReplicationErrorCode = {}));
|
||||
/** Events emitted by replication components */
|
||||
var ReplicationEvent;
|
||||
(function (ReplicationEvent) {
|
||||
ReplicationEvent["ReplicaAdded"] = "replicaAdded";
|
||||
ReplicationEvent["ReplicaRemoved"] = "replicaRemoved";
|
||||
ReplicationEvent["ReplicaStatusChanged"] = "replicaStatusChanged";
|
||||
ReplicationEvent["PrimaryChanged"] = "primaryChanged";
|
||||
ReplicationEvent["ChangeReceived"] = "changeReceived";
|
||||
ReplicationEvent["SyncStarted"] = "syncStarted";
|
||||
ReplicationEvent["SyncCompleted"] = "syncCompleted";
|
||||
ReplicationEvent["ConflictDetected"] = "conflictDetected";
|
||||
ReplicationEvent["ConflictResolved"] = "conflictResolved";
|
||||
ReplicationEvent["FailoverStarted"] = "failoverStarted";
|
||||
ReplicationEvent["FailoverCompleted"] = "failoverCompleted";
|
||||
ReplicationEvent["Error"] = "error";
|
||||
})(ReplicationEvent || (exports.ReplicationEvent = ReplicationEvent = {}));
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/types.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/types.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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"}
|
||||
205
vendor/ruvector/npm/packages/replication/src/types.ts
vendored
Normal file
205
vendor/ruvector/npm/packages/replication/src/types.ts
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Replication Types
|
||||
* Data replication and synchronization types
|
||||
*/
|
||||
|
||||
import type EventEmitter from 'eventemitter3';
|
||||
|
||||
/** Unique identifier for a replica */
|
||||
export type ReplicaId = string;
|
||||
|
||||
/** Logical timestamp for ordering events */
|
||||
export type LogicalClock = number;
|
||||
|
||||
/** Role of a replica in the set */
|
||||
export enum ReplicaRole {
|
||||
Primary = 'primary',
|
||||
Secondary = 'secondary',
|
||||
Arbiter = 'arbiter',
|
||||
}
|
||||
|
||||
/** Status of a replica */
|
||||
export enum ReplicaStatus {
|
||||
Active = 'active',
|
||||
Syncing = 'syncing',
|
||||
Offline = 'offline',
|
||||
Failed = 'failed',
|
||||
}
|
||||
|
||||
/** Synchronization mode */
|
||||
export enum SyncMode {
|
||||
/** All replicas must confirm before commit */
|
||||
Synchronous = 'synchronous',
|
||||
/** Commit immediately, replicate in background */
|
||||
Asynchronous = 'asynchronous',
|
||||
/** Wait for minimum number of replicas */
|
||||
SemiSync = 'semi-sync',
|
||||
}
|
||||
|
||||
/** Health status of a replica */
|
||||
export enum HealthStatus {
|
||||
Healthy = 'healthy',
|
||||
Degraded = 'degraded',
|
||||
Unhealthy = 'unhealthy',
|
||||
Unknown = 'unknown',
|
||||
}
|
||||
|
||||
/** Replica information */
|
||||
export interface Replica {
|
||||
id: ReplicaId;
|
||||
address: string;
|
||||
role: ReplicaRole;
|
||||
status: ReplicaStatus;
|
||||
lastSeen: number;
|
||||
lag: number; // Replication lag in milliseconds
|
||||
}
|
||||
|
||||
/** Change operation type */
|
||||
export enum ChangeOperation {
|
||||
Insert = 'insert',
|
||||
Update = 'update',
|
||||
Delete = 'delete',
|
||||
}
|
||||
|
||||
/** Change event for CDC */
|
||||
export interface ChangeEvent<T = unknown> {
|
||||
/** Unique event ID */
|
||||
id: string;
|
||||
/** Operation type */
|
||||
operation: ChangeOperation;
|
||||
/** Affected key/path */
|
||||
key: string;
|
||||
/** New value (for insert/update) */
|
||||
value?: T;
|
||||
/** Previous value (for update/delete) */
|
||||
previousValue?: T;
|
||||
/** Timestamp of the change */
|
||||
timestamp: number;
|
||||
/** Source replica */
|
||||
sourceReplica: ReplicaId;
|
||||
/** Vector clock for ordering */
|
||||
vectorClock: VectorClockValue;
|
||||
}
|
||||
|
||||
/** Vector clock entry for a single node */
|
||||
export type VectorClockValue = Map<ReplicaId, LogicalClock>;
|
||||
|
||||
/** Log entry for replication */
|
||||
export interface LogEntry<T = unknown> {
|
||||
/** Unique entry ID */
|
||||
id: string;
|
||||
/** Sequence number */
|
||||
sequence: number;
|
||||
/** Operation data */
|
||||
data: T;
|
||||
/** Timestamp */
|
||||
timestamp: number;
|
||||
/** Vector clock */
|
||||
vectorClock: VectorClockValue;
|
||||
}
|
||||
|
||||
/** Failover policy */
|
||||
export enum FailoverPolicy {
|
||||
/** Automatic failover with quorum */
|
||||
Automatic = 'automatic',
|
||||
/** Manual intervention required */
|
||||
Manual = 'manual',
|
||||
/** Priority-based failover */
|
||||
Priority = 'priority',
|
||||
}
|
||||
|
||||
/** Replication error types */
|
||||
export class ReplicationError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly code: ReplicationErrorCode,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ReplicationError';
|
||||
}
|
||||
|
||||
static replicaNotFound(id: string): ReplicationError {
|
||||
return new ReplicationError(`Replica not found: ${id}`, ReplicationErrorCode.ReplicaNotFound);
|
||||
}
|
||||
|
||||
static noPrimary(): ReplicationError {
|
||||
return new ReplicationError('No primary replica available', ReplicationErrorCode.NoPrimary);
|
||||
}
|
||||
|
||||
static timeout(operation: string): ReplicationError {
|
||||
return new ReplicationError(`Replication timeout: ${operation}`, ReplicationErrorCode.Timeout);
|
||||
}
|
||||
|
||||
static quorumNotMet(needed: number, available: number): ReplicationError {
|
||||
return new ReplicationError(
|
||||
`Quorum not met: needed ${needed}, got ${available}`,
|
||||
ReplicationErrorCode.QuorumNotMet,
|
||||
);
|
||||
}
|
||||
|
||||
static splitBrain(): ReplicationError {
|
||||
return new ReplicationError('Split-brain detected', ReplicationErrorCode.SplitBrain);
|
||||
}
|
||||
|
||||
static conflictResolution(reason: string): ReplicationError {
|
||||
return new ReplicationError(
|
||||
`Conflict resolution failed: ${reason}`,
|
||||
ReplicationErrorCode.ConflictResolution,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export enum ReplicationErrorCode {
|
||||
ReplicaNotFound = 'REPLICA_NOT_FOUND',
|
||||
NoPrimary = 'NO_PRIMARY',
|
||||
Timeout = 'TIMEOUT',
|
||||
SyncFailed = 'SYNC_FAILED',
|
||||
ConflictResolution = 'CONFLICT_RESOLUTION',
|
||||
FailoverFailed = 'FAILOVER_FAILED',
|
||||
Network = 'NETWORK',
|
||||
QuorumNotMet = 'QUORUM_NOT_MET',
|
||||
SplitBrain = 'SPLIT_BRAIN',
|
||||
InvalidState = 'INVALID_STATE',
|
||||
}
|
||||
|
||||
/** Events emitted by replication components */
|
||||
export enum ReplicationEvent {
|
||||
ReplicaAdded = 'replicaAdded',
|
||||
ReplicaRemoved = 'replicaRemoved',
|
||||
ReplicaStatusChanged = 'replicaStatusChanged',
|
||||
PrimaryChanged = 'primaryChanged',
|
||||
ChangeReceived = 'changeReceived',
|
||||
SyncStarted = 'syncStarted',
|
||||
SyncCompleted = 'syncCompleted',
|
||||
ConflictDetected = 'conflictDetected',
|
||||
ConflictResolved = 'conflictResolved',
|
||||
FailoverStarted = 'failoverStarted',
|
||||
FailoverCompleted = 'failoverCompleted',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
/** Configuration for replica set */
|
||||
export interface ReplicaSetConfig {
|
||||
/** Cluster name */
|
||||
name: string;
|
||||
/** Minimum replicas for quorum */
|
||||
minQuorum: number;
|
||||
/** Heartbeat interval in milliseconds */
|
||||
heartbeatInterval: number;
|
||||
/** Timeout for health checks in milliseconds */
|
||||
healthCheckTimeout: number;
|
||||
/** Failover policy */
|
||||
failoverPolicy: FailoverPolicy;
|
||||
}
|
||||
|
||||
/** Configuration for sync manager */
|
||||
export interface SyncConfig {
|
||||
/** Sync mode */
|
||||
mode: SyncMode;
|
||||
/** Minimum replicas for semi-sync */
|
||||
minReplicas?: number;
|
||||
/** Batch size for streaming changes */
|
||||
batchSize: number;
|
||||
/** Maximum lag before triggering catchup */
|
||||
maxLag: number;
|
||||
}
|
||||
63
vendor/ruvector/npm/packages/replication/src/vector-clock.d.ts
vendored
Normal file
63
vendor/ruvector/npm/packages/replication/src/vector-clock.d.ts
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Vector Clock Implementation
|
||||
* For conflict detection and resolution in distributed systems
|
||||
*/
|
||||
import type { ReplicaId, LogicalClock, VectorClockValue } from './types.js';
|
||||
/** Comparison result between vector clocks */
|
||||
export declare enum VectorClockComparison {
|
||||
/** First happens before second */
|
||||
Before = "before",
|
||||
/** First happens after second */
|
||||
After = "after",
|
||||
/** Clocks are concurrent (no causal relationship) */
|
||||
Concurrent = "concurrent",
|
||||
/** Clocks are equal */
|
||||
Equal = "equal"
|
||||
}
|
||||
/** Vector clock for tracking causality in distributed systems */
|
||||
export declare class VectorClock {
|
||||
private clock;
|
||||
constructor(initial?: VectorClockValue | Map<ReplicaId, LogicalClock>);
|
||||
/** Get the clock value for a replica */
|
||||
get(replicaId: ReplicaId): LogicalClock;
|
||||
/** Increment the clock for a replica */
|
||||
increment(replicaId: ReplicaId): void;
|
||||
/** Update with a received clock (merge) */
|
||||
merge(other: VectorClock): void;
|
||||
/** Create a copy of this clock */
|
||||
clone(): VectorClock;
|
||||
/** Get the clock value as a Map */
|
||||
getValue(): VectorClockValue;
|
||||
/** Compare two vector clocks */
|
||||
compare(other: VectorClock): VectorClockComparison;
|
||||
/** Check if this clock happens before another */
|
||||
happensBefore(other: VectorClock): boolean;
|
||||
/** Check if this clock happens after another */
|
||||
happensAfter(other: VectorClock): boolean;
|
||||
/** Check if clocks are concurrent (no causal relationship) */
|
||||
isConcurrent(other: VectorClock): boolean;
|
||||
/** Serialize to JSON */
|
||||
toJSON(): Record<string, number>;
|
||||
/** Create from JSON */
|
||||
static fromJSON(json: Record<string, number>): VectorClock;
|
||||
/** Create a new vector clock with a single entry */
|
||||
static single(replicaId: ReplicaId, time?: LogicalClock): VectorClock;
|
||||
}
|
||||
/** Conflict resolver interface */
|
||||
export interface ConflictResolver<T> {
|
||||
/** Resolve a conflict between two values */
|
||||
resolve(local: T, remote: T, localClock: VectorClock, remoteClock: VectorClock): T;
|
||||
}
|
||||
/** Last-write-wins conflict resolver */
|
||||
export declare class LastWriteWins<T extends {
|
||||
timestamp: number;
|
||||
}> implements ConflictResolver<T> {
|
||||
resolve(local: T, remote: T): T;
|
||||
}
|
||||
/** Custom merge function conflict resolver */
|
||||
export declare class MergeFunction<T> implements ConflictResolver<T> {
|
||||
private mergeFn;
|
||||
constructor(mergeFn: (local: T, remote: T) => T);
|
||||
resolve(local: T, remote: T): T;
|
||||
}
|
||||
//# sourceMappingURL=vector-clock.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/vector-clock.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/vector-clock.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vector-clock.d.ts","sourceRoot":"","sources":["vector-clock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5E,8CAA8C;AAC9C,oBAAY,qBAAqB;IAC/B,kCAAkC;IAClC,MAAM,WAAW;IACjB,iCAAiC;IACjC,KAAK,UAAU;IACf,qDAAqD;IACrD,UAAU,eAAe;IACzB,uBAAuB;IACvB,KAAK,UAAU;CAChB;AAED,iEAAiE;AACjE,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAA+B;gBAEhC,OAAO,CAAC,EAAE,gBAAgB,GAAG,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC;IAIrE,wCAAwC;IACxC,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY;IAIvC,wCAAwC;IACxC,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAKrC,2CAA2C;IAC3C,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAO/B,kCAAkC;IAClC,KAAK,IAAI,WAAW;IAIpB,mCAAmC;IACnC,QAAQ,IAAI,gBAAgB;IAI5B,gCAAgC;IAChC,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,qBAAqB;IA6BlD,iDAAiD;IACjD,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAI1C,gDAAgD;IAChD,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAIzC,8DAA8D;IAC9D,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAIzC,wBAAwB;IACxB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQhC,uBAAuB;IACvB,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,WAAW;IAQ1D,oDAAoD;IACpD,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,GAAE,YAAgB,GAAG,WAAW;CAKzE;AAED,kCAAkC;AAClC,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,4CAA4C;IAC5C,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,GAAG,CAAC,CAAC;CACpF;AAED,wCAAwC;AACxC,qBAAa,aAAa,CAAC,CAAC,SAAS;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAE,YAAW,gBAAgB,CAAC,CAAC,CAAC;IACxF,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC;CAGhC;AAED,8CAA8C;AAC9C,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,gBAAgB,CAAC,CAAC,CAAC;IAC9C,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC;IAEvD,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC;CAGhC"}
|
||||
131
vendor/ruvector/npm/packages/replication/src/vector-clock.js
vendored
Normal file
131
vendor/ruvector/npm/packages/replication/src/vector-clock.js
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Vector Clock Implementation
|
||||
* For conflict detection and resolution in distributed systems
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MergeFunction = exports.LastWriteWins = exports.VectorClock = exports.VectorClockComparison = void 0;
|
||||
/** Comparison result between vector clocks */
|
||||
var VectorClockComparison;
|
||||
(function (VectorClockComparison) {
|
||||
/** First happens before second */
|
||||
VectorClockComparison["Before"] = "before";
|
||||
/** First happens after second */
|
||||
VectorClockComparison["After"] = "after";
|
||||
/** Clocks are concurrent (no causal relationship) */
|
||||
VectorClockComparison["Concurrent"] = "concurrent";
|
||||
/** Clocks are equal */
|
||||
VectorClockComparison["Equal"] = "equal";
|
||||
})(VectorClockComparison || (exports.VectorClockComparison = VectorClockComparison = {}));
|
||||
/** Vector clock for tracking causality in distributed systems */
|
||||
class VectorClock {
|
||||
constructor(initial) {
|
||||
this.clock = new Map(initial);
|
||||
}
|
||||
/** Get the clock value for a replica */
|
||||
get(replicaId) {
|
||||
return this.clock.get(replicaId) ?? 0;
|
||||
}
|
||||
/** Increment the clock for a replica */
|
||||
increment(replicaId) {
|
||||
const current = this.get(replicaId);
|
||||
this.clock.set(replicaId, current + 1);
|
||||
}
|
||||
/** Update with a received clock (merge) */
|
||||
merge(other) {
|
||||
for (const [replicaId, otherTime] of other.clock) {
|
||||
const myTime = this.get(replicaId);
|
||||
this.clock.set(replicaId, Math.max(myTime, otherTime));
|
||||
}
|
||||
}
|
||||
/** Create a copy of this clock */
|
||||
clone() {
|
||||
return new VectorClock(new Map(this.clock));
|
||||
}
|
||||
/** Get the clock value as a Map */
|
||||
getValue() {
|
||||
return new Map(this.clock);
|
||||
}
|
||||
/** Compare two vector clocks */
|
||||
compare(other) {
|
||||
let isLess = false;
|
||||
let isGreater = false;
|
||||
// Get all unique replica IDs
|
||||
const allReplicas = new Set([...this.clock.keys(), ...other.clock.keys()]);
|
||||
for (const replicaId of allReplicas) {
|
||||
const myTime = this.get(replicaId);
|
||||
const otherTime = other.get(replicaId);
|
||||
if (myTime < otherTime) {
|
||||
isLess = true;
|
||||
}
|
||||
else if (myTime > otherTime) {
|
||||
isGreater = true;
|
||||
}
|
||||
}
|
||||
if (isLess && isGreater) {
|
||||
return VectorClockComparison.Concurrent;
|
||||
}
|
||||
else if (isLess) {
|
||||
return VectorClockComparison.Before;
|
||||
}
|
||||
else if (isGreater) {
|
||||
return VectorClockComparison.After;
|
||||
}
|
||||
else {
|
||||
return VectorClockComparison.Equal;
|
||||
}
|
||||
}
|
||||
/** Check if this clock happens before another */
|
||||
happensBefore(other) {
|
||||
return this.compare(other) === VectorClockComparison.Before;
|
||||
}
|
||||
/** Check if this clock happens after another */
|
||||
happensAfter(other) {
|
||||
return this.compare(other) === VectorClockComparison.After;
|
||||
}
|
||||
/** Check if clocks are concurrent (no causal relationship) */
|
||||
isConcurrent(other) {
|
||||
return this.compare(other) === VectorClockComparison.Concurrent;
|
||||
}
|
||||
/** Serialize to JSON */
|
||||
toJSON() {
|
||||
const obj = {};
|
||||
for (const [key, value] of this.clock) {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
/** Create from JSON */
|
||||
static fromJSON(json) {
|
||||
const clock = new VectorClock();
|
||||
for (const [key, value] of Object.entries(json)) {
|
||||
clock.clock.set(key, value);
|
||||
}
|
||||
return clock;
|
||||
}
|
||||
/** Create a new vector clock with a single entry */
|
||||
static single(replicaId, time = 1) {
|
||||
const clock = new VectorClock();
|
||||
clock.clock.set(replicaId, time);
|
||||
return clock;
|
||||
}
|
||||
}
|
||||
exports.VectorClock = VectorClock;
|
||||
/** Last-write-wins conflict resolver */
|
||||
class LastWriteWins {
|
||||
resolve(local, remote) {
|
||||
return local.timestamp >= remote.timestamp ? local : remote;
|
||||
}
|
||||
}
|
||||
exports.LastWriteWins = LastWriteWins;
|
||||
/** Custom merge function conflict resolver */
|
||||
class MergeFunction {
|
||||
constructor(mergeFn) {
|
||||
this.mergeFn = mergeFn;
|
||||
}
|
||||
resolve(local, remote) {
|
||||
return this.mergeFn(local, remote);
|
||||
}
|
||||
}
|
||||
exports.MergeFunction = MergeFunction;
|
||||
//# sourceMappingURL=vector-clock.js.map
|
||||
1
vendor/ruvector/npm/packages/replication/src/vector-clock.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/replication/src/vector-clock.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vector-clock.js","sourceRoot":"","sources":["vector-clock.ts"],"names":[],"mappings":"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"}
|
||||
148
vendor/ruvector/npm/packages/replication/src/vector-clock.ts
vendored
Normal file
148
vendor/ruvector/npm/packages/replication/src/vector-clock.ts
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Vector Clock Implementation
|
||||
* For conflict detection and resolution in distributed systems
|
||||
*/
|
||||
|
||||
import type { ReplicaId, LogicalClock, VectorClockValue } from './types.js';
|
||||
|
||||
/** Comparison result between vector clocks */
|
||||
export enum VectorClockComparison {
|
||||
/** First happens before second */
|
||||
Before = 'before',
|
||||
/** First happens after second */
|
||||
After = 'after',
|
||||
/** Clocks are concurrent (no causal relationship) */
|
||||
Concurrent = 'concurrent',
|
||||
/** Clocks are equal */
|
||||
Equal = 'equal',
|
||||
}
|
||||
|
||||
/** Vector clock for tracking causality in distributed systems */
|
||||
export class VectorClock {
|
||||
private clock: Map<ReplicaId, LogicalClock>;
|
||||
|
||||
constructor(initial?: VectorClockValue | Map<ReplicaId, LogicalClock>) {
|
||||
this.clock = new Map(initial);
|
||||
}
|
||||
|
||||
/** Get the clock value for a replica */
|
||||
get(replicaId: ReplicaId): LogicalClock {
|
||||
return this.clock.get(replicaId) ?? 0;
|
||||
}
|
||||
|
||||
/** Increment the clock for a replica */
|
||||
increment(replicaId: ReplicaId): void {
|
||||
const current = this.get(replicaId);
|
||||
this.clock.set(replicaId, current + 1);
|
||||
}
|
||||
|
||||
/** Update with a received clock (merge) */
|
||||
merge(other: VectorClock): void {
|
||||
for (const [replicaId, otherTime] of other.clock) {
|
||||
const myTime = this.get(replicaId);
|
||||
this.clock.set(replicaId, Math.max(myTime, otherTime));
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a copy of this clock */
|
||||
clone(): VectorClock {
|
||||
return new VectorClock(new Map(this.clock));
|
||||
}
|
||||
|
||||
/** Get the clock value as a Map */
|
||||
getValue(): VectorClockValue {
|
||||
return new Map(this.clock);
|
||||
}
|
||||
|
||||
/** Compare two vector clocks */
|
||||
compare(other: VectorClock): VectorClockComparison {
|
||||
let isLess = false;
|
||||
let isGreater = false;
|
||||
|
||||
// Get all unique replica IDs
|
||||
const allReplicas = new Set([...this.clock.keys(), ...other.clock.keys()]);
|
||||
|
||||
for (const replicaId of allReplicas) {
|
||||
const myTime = this.get(replicaId);
|
||||
const otherTime = other.get(replicaId);
|
||||
|
||||
if (myTime < otherTime) {
|
||||
isLess = true;
|
||||
} else if (myTime > otherTime) {
|
||||
isGreater = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLess && isGreater) {
|
||||
return VectorClockComparison.Concurrent;
|
||||
} else if (isLess) {
|
||||
return VectorClockComparison.Before;
|
||||
} else if (isGreater) {
|
||||
return VectorClockComparison.After;
|
||||
} else {
|
||||
return VectorClockComparison.Equal;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if this clock happens before another */
|
||||
happensBefore(other: VectorClock): boolean {
|
||||
return this.compare(other) === VectorClockComparison.Before;
|
||||
}
|
||||
|
||||
/** Check if this clock happens after another */
|
||||
happensAfter(other: VectorClock): boolean {
|
||||
return this.compare(other) === VectorClockComparison.After;
|
||||
}
|
||||
|
||||
/** Check if clocks are concurrent (no causal relationship) */
|
||||
isConcurrent(other: VectorClock): boolean {
|
||||
return this.compare(other) === VectorClockComparison.Concurrent;
|
||||
}
|
||||
|
||||
/** Serialize to JSON */
|
||||
toJSON(): Record<string, number> {
|
||||
const obj: Record<string, number> = {};
|
||||
for (const [key, value] of this.clock) {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Create from JSON */
|
||||
static fromJSON(json: Record<string, number>): VectorClock {
|
||||
const clock = new VectorClock();
|
||||
for (const [key, value] of Object.entries(json)) {
|
||||
clock.clock.set(key, value);
|
||||
}
|
||||
return clock;
|
||||
}
|
||||
|
||||
/** Create a new vector clock with a single entry */
|
||||
static single(replicaId: ReplicaId, time: LogicalClock = 1): VectorClock {
|
||||
const clock = new VectorClock();
|
||||
clock.clock.set(replicaId, time);
|
||||
return clock;
|
||||
}
|
||||
}
|
||||
|
||||
/** Conflict resolver interface */
|
||||
export interface ConflictResolver<T> {
|
||||
/** Resolve a conflict between two values */
|
||||
resolve(local: T, remote: T, localClock: VectorClock, remoteClock: VectorClock): T;
|
||||
}
|
||||
|
||||
/** Last-write-wins conflict resolver */
|
||||
export class LastWriteWins<T extends { timestamp: number }> implements ConflictResolver<T> {
|
||||
resolve(local: T, remote: T): T {
|
||||
return local.timestamp >= remote.timestamp ? local : remote;
|
||||
}
|
||||
}
|
||||
|
||||
/** Custom merge function conflict resolver */
|
||||
export class MergeFunction<T> implements ConflictResolver<T> {
|
||||
constructor(private mergeFn: (local: T, remote: T) => T) {}
|
||||
|
||||
resolve(local: T, remote: T): T {
|
||||
return this.mergeFn(local, remote);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user