用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tools-only/X-Skills --skill realtime-realtime-sync命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
基于 SOC 职业分类
正在显示 SKILL.md
| name | realtime-realtime-sync |
| description | Use real-time sync patterns when you need: |
Scope: Conflict resolution, CRDTs, operational transformation, and eventual consistency patterns Lines: 398 Last Updated: 2025-10-18
Use real-time sync patterns when you need:
Don't use when:
1. Last-Write-Wins (LWW)
- Simplest approach
- Use timestamp or sequence number
- Data loss possible
2. Operational Transformation (OT)
- Transform operations based on concurrent ops
- Complex but preserves intent
- Used in Google Docs
3. Conflict-Free Replicated Data Types (CRDTs)
- Mathematical guarantees of convergence
- No conflict resolution needed
- Used in distributed systems
4. Three-Way Merge
- Compare: base, local, remote
- Identify conflicts explicitly
- User resolves ambiguities
Client A Server Client B
| | |
|-- Update A ---->| |
| |-- Broadcast -->|
| | |
|<-- Update B ----|<-- Update B ---|
| | |
|-- Resolve ----->| |
|<-- Merged ------|-- Merged ----->|
// Simple LWW with timestamps
interface VersionedData<T> {
data: T;
version: number;
timestamp: number;
clientId: string;
}
class LWWStore<T> {
private data: VersionedData<T> | null = null;
update(newData: T, clientId: string): VersionedData<T> {
const version = (this.data?.version ?? 0) + 1;
const timestamp = Date.now();
this.data = {
data: newData,
version,
timestamp,
clientId,
};
return this.data;
}
merge(remote: VersionedData<T>): boolean {
if (!this.data) {
this.data = remote;
return true;
}
// Resolve conflicts with LWW
if (remote.timestamp > ..) {
. = remote;
;
} (remote. === ..) {
(remote. > ..) {
. = remote;
;
}
}
;
}
(): T | {
.?. ?? ;
}
}
store = <>();
store.(, );
remoteData = {
: ,
: ,
: .() + ,
: ,
};
updated = store.(remoteData);
.(updated);
.(store.());
// LWW-Register CRDT
interface LWWRegister<T> {
value: T;
timestamp: number;
clientId: string;
}
class LWWRegisterCRDT<T> {
private register: LWWRegister<T>;
constructor(initialValue: T, clientId: string) {
this.register = {
value: initialValue,
timestamp: Date.now(),
clientId,
};
}
set(value: T, clientId: string): LWWRegister<T> {
this.register = {
value,
timestamp: Date.now(),
clientId,
};
return this.register;
}
merge(other: LWWRegister<T>): boolean {
if (other.timestamp > this.register.timestamp ||
(other.timestamp === this.register.timestamp &&
other. > ..)) {
. = other;
;
}
;
}
(): T {
..;
}
(): <T> {
{ .... };
}
}
// G-Set: Elements can only be added, never removed
class GSet<T> {
private elements: Set<T> = new Set();
add(element: T): boolean {
if (this.elements.has(element)) {
return false;
}
this.elements.add(element);
return true;
}
has(element: T): boolean {
return this.elements.has(element);
}
merge(other: GSet<T>): void {
other.elements.forEach((element) => {
this.elements.add(element);
});
}
toArray(): T[] {
return Array.from(this.elements);
}
getState(): T[] {
return this.toArray();
}
}
set1 = <>();
set1.();
set1.();
set2 = <>();
set2.();
set2.();
set1.(set2);
.(set1.());
// 2P-Set: Elements can be added and removed (but only once each)
class TwoPhaseSet<T> {
private addSet: Set<T> = new Set();
private removeSet: Set<T> = new Set();
add(element: T): boolean {
if (this.removeSet.has(element)) {
return false; // Cannot re-add removed element
}
this.addSet.add(element);
return true;
}
remove(element: T): boolean {
if (!this.addSet.has(element)) {
return false; // Cannot remove non-existent element
}
this.removeSet.add(element);
return true;
}
has(element: T): boolean {
return this.addSet.has(element) && !this..(element);
}
(: <T>): {
other..( ..(element));
other..( ..(element));
}
(): T[] {
.(.).( !..(element));
}
() {
{
: .(.),
: .(.),
};
}
}
// G-Counter: Grow-only counter (increment only)
class GCounter {
private counts: Map<string, number> = new Map();
increment(clientId: string, amount: number = 1): void {
const current = this.counts.get(clientId) ?? 0;
this.counts.set(clientId, current + amount);
}
value(): number {
let total = 0;
this.counts.forEach((count) => {
total += count;
});
return total;
}
merge(other: GCounter): void {
other.counts.forEach((count, clientId) => {
const current = this.counts.get(clientId) ?? 0;
this.counts.set(clientId, .(current, count));
});
}
(): <, > {
(.);
}
}
{
: = ();
: = ();
(: , : = ): {
..(clientId, amount);
}
(: , : = ): {
..(clientId, amount);
}
(): {
..() - ..();
}
(: ): {
..(other.);
..(other.);
}
}
counter = ();
counter.(, );
counter.(, );
.(counter.());
// Simple OT for text insertion and deletion
interface Operation {
type: 'insert' | 'delete';
position: number;
content?: string;
length?: number;
}
class OperationalTransform {
static transform(op1: Operation, op2: Operation): Operation {
if (op1.type === 'insert' && op2.type === 'insert') {
if (op1.position < op2.position) {
// op2 happens after op1's insertion
return {
...op2,
position: op2.position + (op1.content?.length ?? 0),
};
} else if (op1.position > op2.position) {
// op2 happens before op1, no transformation needed
return op2;
} else {
// Same position, use arbitrary tie-breaker
return {
...op2,
position: op2.position + (op1.content?.length ?? ),
};
}
}
(op1. === && op2. === ) {
(op2. <= op1.) {
op2;
} (op2. >= op1. + (op1. ?? )) {
{
...op2,
: op2. - (op1. ?? ),
};
} {
{
...op2,
: op1.,
};
}
}
(op1. === && op2. === ) {
(op2. < op1.) {
op2;
} (op2. >= op1. + (op1.?. ?? )) {
{
...op2,
: op2. + (op1.?. ?? ),
};
} {
{
...op2,
: op1.,
: (op2. ?? ) + (op1.?. ?? ),
};
}
}
(op1. === && op2. === ) {
(op2. < op1.) {
op2;
} (op2. >= op1. + (op1. ?? )) {
{
...op2,
: op2. - (op1. ?? ),
};
} {
{
...op2,
: op1.,
: .(, (op2. ?? ) - (op1. ?? )),
};
}
}
op2;
}
(: , : ): {
(op. === ) {
text.(, op.) + op. + text.(op.);
} (op. === ) {
text.(, op.) + text.(op. + (op. ?? ));
}
text;
}
}
text = ;
: = { : , : , : };
: = { : , : , : };
textA = .(text, op1);
textB = .(text, op2);
op2Transformed = .(op1, op2);
textA = .(textA, op2Transformed);
op1Transformed = .(op2, op1);
textB = .(textB, op1Transformed);
.(textA === textB);
interface SyncMessage {
type: 'update' | 'sync_request' | 'sync_response';
clientId: string;
data?: any;
version?: number;
}
class SyncManager<T> {
private ws: WebSocket;
private clientId: string;
private crdt: LWWRegisterCRDT<T>;
private onUpdate: (data: T) => void;
constructor(
url: string,
initialData: T,
onUpdate: (data: T) => void
) {
this.clientId = this.generateClientId();
this.crdt = new LWWRegisterCRDT(initialData, this.clientId);
this.onUpdate = onUpdate;
this.ws = new WebSocket(url);
this.();
}
() {
.. = {
.();
.();
};
.. = {
: = .(event.);
.(message);
};
}
() {
(message.) {
:
(message. && message. !== .) {
updated = ..(message.);
(updated) {
.(..());
}
}
;
:
(message.) {
..(message.);
.(..());
}
;
}
}
() {
..(data, .);
.();
.(..());
}
() {
: = {
: ,
: .,
: ..(),
};
..(.(message));
}
() {
: = {
: ,
: .,
};
..(.(message));
}
(): {
;
}
}
syncManager = (
,
,
{
.(, data);
}
);
syncManager.();
// LWW-Register: Single value, last write wins
const register = new LWWRegisterCRDT('initial', 'client-1');
// G-Set: Grow-only set (add only)
const gset = new GSet<string>();
// 2P-Set: Add and remove (once each)
const tpset = new TwoPhaseSet<string>();
// G-Counter: Increment-only counter
const gcounter = new GCounter();
// PN-Counter: Increment and decrement counter
const pncounter = new PNCounter();
// Last-Write-Wins: Use timestamp
if (remote.timestamp > local.timestamp) {
local = remote;
}
// Version Vector: Use vector clock
if (isGreaterThan(remote.vector, local.vector)) {
local = remote;
}
// Three-Way Merge: Compare base, local, remote
const merged = merge(base, local, remote);
// Full state sync
send({ type: 'full_sync', state: crdt.getState() });
// Delta sync (changes only)
send({ type: 'delta_sync', delta: crdt.getDelta() });
// Operational sync (operations)
send({ type: 'operation', op: { type: 'insert', ... } });
// Wrong: Ties cause non-deterministic results
if (remote.timestamp > local.timestamp) {
local = remote;
}
// What if timestamps are equal?
Why it's bad: Different clients may choose different values for concurrent updates
Better approach:
if (remote.timestamp > local.timestamp ||
(remote.timestamp === local.timestamp && remote.clientId > local.clientId)) {
local = remote;
}
// Wrong: Inefficient for large documents
socket.send(JSON.stringify({ type: 'update', data: fullDocument }));
Why it's bad: Wastes bandwidth, especially for small changes
Better approach:
// Send operations or deltas
socket.send(JSON.stringify({ type: 'operation', op: { type: 'insert', position: 5, content: 'x' } }));
// Wrong: Blindly overwrite
function update(data) {
this.data = data; // No version checking
}
Why it's bad: Lost updates when concurrent modifications occur
Better approach:
function update(data, version) {
if (version <= this.version) {
throw new Error('Conflict detected');
}
this.data = data;
this.version = version;
}
// Wrong: Blocking merge for large data
await merge(largeDocument); // Blocks UI
Why it's bad: UI freezes during merge
Better approach:
// Merge in Web Worker or use incremental merging
worker.postMessage({ type: 'merge', data: largeDocument });
Last Updated: 2025-10-18 Format Version: 1.0 (Atomic)