MCPcopy Create free account
hub / github.com/cloudflare/computer / SyncRPCServer

Class SyncRPCServer

packages/rpc/src/server.ts:89–228  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

87}
88
89class SyncRPCServer extends RpcTarget implements SyncRPC {
90 constructor(
91 private readonly db: Database,
92 private readonly options: Required<Pick<ServerOptions, "ignore">> &
93 Pick<ServerOptions, "afterApply" | "beforeFetch">,
94 ) {
95 super();
96 trackStub(this);
97 }
98
99 [Symbol.dispose](): void {
100 untrackStub(this);
101 }
102
103 async push(input: {
104 senderRev: number;
105 changes: ReadableStream<ChangeEntry>;
106 }): Promise<{ rev: number; appliedPushCursor: ChangeCursor }> {
107 const entries: ChangeEntry[] = [];
108 const reader = input.changes.getReader();
109 try {
110 while (true) {
111 const { value, done } = await reader.read();
112 if (done) break;
113 entries.push(value);
114 }
115 } finally {
116 reader.releaseLock();
117 }
118 // senderRev > 0 — the caller is a sync peer with its
119 // own rev space; advance the fetch cursor to that point so
120 // subsequent pulls and the cross-side invariant check see the
121 // right appliedPushCursor. The apply path's alreadyApplied()
122 // check is what stops the entries from ping-ponging back
123 // through the sender's own coalesce + apply loop on the next
124 // round trip.
125 //
126 // senderRev === 0 — the caller is an external writer
127 // (an orchestrator using the wire as a transport, the
128 // soak script, a manual curl). Treat the entries as
129 // local writes: bump rev through the normal apply path,
130 // leave pushRev untouched so the outbound sync loop
131 // ships them upstream on the next tick.
132 const isPeer = input.senderRev > 0;
133 // Wrap the whole batch in a single transactionSync so a
134 // mid-stream failure (e.g. a missing chunk in applyChangesSync's
135 // assembly step) rolls back every prior entry. Without this
136 // wrapper the receiver could be left with a subset of the
137 // pushed entries committed.
138 this.db.transactionSync(() => {
139 applyChangesSync(this.db, entries, new Map(), {
140 source: isPeer ? "upstream" : "local",
141 });
142 if (isPeer) {
143 const nextCursor = { rev: input.senderRev, path: null };
144 if (compareChangeCursors(nextCursor, readFetchCursor(this.db)) > 0) {
145 writeFetchCursor(this.db, nextCursor);
146 }

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected