@ibgib/core-gib 0.1.23 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/other/graph-helper.d.mts +17 -0
- package/dist/common/other/graph-helper.d.mts.map +1 -1
- package/dist/common/other/graph-helper.mjs +44 -0
- package/dist/common/other/graph-helper.mjs.map +1 -1
- package/dist/sync/graft-info/graft-info-helpers.mjs +2 -2
- package/dist/sync/graft-info/graft-info-helpers.mjs.map +1 -1
- package/dist/sync/sync-conflict.respec.mjs +10 -15
- package/dist/sync/sync-conflict.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.respec.mjs +10 -12
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +10 -12
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +10 -12
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +9 -12
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +9 -14
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +9 -12
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts +2 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs +4 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +24 -11
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +58 -44
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +29 -6
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +29 -30
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +105 -198
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +19 -2
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +37 -4
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +12 -12
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +281 -218
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +51 -6
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +36 -9
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +1 -2
- package/dist/sync/sync-types.mjs.map +1 -1
- package/package.json +1 -1
- package/src/common/other/graph-helper.mts +53 -0
- package/src/sync/graft-info/graft-info-helpers.mts +3 -3
- package/src/sync/sync-conflict.respec.mts +10 -17
- package/src/sync/sync-helpers.mts +6 -6
- package/src/sync/sync-innerspace-constants.respec.mts +10 -12
- package/src/sync/sync-innerspace-deep-updates.respec.mts +10 -12
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +10 -12
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +9 -12
- package/src/sync/sync-innerspace-partial-update.respec.mts +9 -14
- package/src/sync/sync-innerspace.respec.mts +9 -12
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mts +7 -0
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +75 -50
- package/src/sync/sync-peer/sync-peer-types.mts +35 -11
- package/src/sync/sync-peer/sync-peer-v1.mts +136 -196
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +37 -8
- package/src/sync/sync-saga-coordinator.mts +318 -260
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +56 -4
- package/src/sync/sync-types.mts +46 -13
|
@@ -23,14 +23,18 @@ import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMA
|
|
|
23
23
|
import { appendToTimeline, createTimeline, getHistory, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
|
|
24
24
|
import {
|
|
25
25
|
SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
|
|
26
|
-
SyncRel8ns_V1, DomainIbGibAnalysisInfo,
|
|
26
|
+
SyncRel8ns_V1, DomainIbGibAnalysisInfo, NextSagaFrameInfo,
|
|
27
|
+
SYNC_CONFLICT_STRATEGY_VALID_VALUES,
|
|
28
|
+
HandleSagaResponseContextResult,
|
|
27
29
|
} from "./sync-types.mjs";
|
|
28
30
|
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
29
|
-
import { getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
31
|
+
import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
30
32
|
import {
|
|
31
33
|
SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
|
|
32
34
|
SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
|
|
33
35
|
SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
|
|
36
|
+
SyncSagaPushOfferInfo,
|
|
37
|
+
SyncSagaRequestAddrInfo,
|
|
34
38
|
} from "./sync-saga-message/sync-saga-message-types.mjs";
|
|
35
39
|
import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
36
40
|
import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
|
|
@@ -38,7 +42,7 @@ import { SyncSagaInfo } from "./sync-types.mjs";
|
|
|
38
42
|
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
|
|
39
43
|
import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
|
|
40
44
|
import { SyncSagaContextIbGib_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
|
|
41
|
-
import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
45
|
+
import { createSyncSagaContext, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
42
46
|
import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
|
|
43
47
|
import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
|
|
44
48
|
import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
|
|
@@ -58,14 +62,14 @@ const lcControlDomain = '[ControlDomain]';
|
|
|
58
62
|
|
|
59
63
|
/**
|
|
60
64
|
* Orchestrates the synchronization process between two spaces (Source and Destination).
|
|
61
|
-
*
|
|
65
|
+
*
|
|
62
66
|
* ## Execution Contexts
|
|
63
|
-
*
|
|
67
|
+
*
|
|
64
68
|
* The methods in this class are designed to be run on specific nodes in the sync topology:
|
|
65
|
-
*
|
|
69
|
+
*
|
|
66
70
|
* * **Sender/Local Node**: The node *initiating* the sync (calling `sync()`).
|
|
67
71
|
* * **Receiver/Remote Node**: The node *accepting* the sync request (the Peer).
|
|
68
|
-
*
|
|
72
|
+
*
|
|
69
73
|
* Note that in a peer-to-peer architecture, "Sender" and "Receiver" are roles relative
|
|
70
74
|
* to a specific Saga session, not fixed node identities.
|
|
71
75
|
*/
|
|
@@ -80,11 +84,11 @@ export class SyncSagaCoordinator {
|
|
|
80
84
|
|
|
81
85
|
/**
|
|
82
86
|
* Executes a synchronization saga using the Symmetric Sync Protocol.
|
|
83
|
-
*
|
|
87
|
+
*
|
|
84
88
|
* @remarks
|
|
85
89
|
* **Execution Context**: **Sender (Local)**.
|
|
86
90
|
* This method is the entry point for starting a sync session.
|
|
87
|
-
*
|
|
91
|
+
*
|
|
88
92
|
* @param opts.peer - The remote peer witness to communicate with.
|
|
89
93
|
* @param opts.localSpace - The local space that will be read from and written to.
|
|
90
94
|
* @param opts.metaspace - Service for creating temp spaces and managing ibgibs.
|
|
@@ -140,14 +144,14 @@ export class SyncSagaCoordinator {
|
|
|
140
144
|
(async () => {
|
|
141
145
|
try {
|
|
142
146
|
|
|
143
|
-
//
|
|
147
|
+
// BOOTSTRAP IDENTITY (Session Keystone)
|
|
144
148
|
const sessionIdentity = useSessionIdentity
|
|
145
149
|
? await this.getSessionIdentity({ sagaId, metaspace, tempSpace })
|
|
146
150
|
: undefined;
|
|
147
151
|
// if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
|
|
148
152
|
|
|
149
|
-
//
|
|
150
|
-
const {
|
|
153
|
+
// CREATE INITIAL FRAME (Stage.init)
|
|
154
|
+
const { initFrame, initDomainGraph } = await this.createInitFrame({
|
|
151
155
|
sagaId,
|
|
152
156
|
sessionIdentity,
|
|
153
157
|
domainIbGibs,
|
|
@@ -155,17 +159,16 @@ export class SyncSagaCoordinator {
|
|
|
155
159
|
metaspace, localSpace, tempSpace,
|
|
156
160
|
});
|
|
157
161
|
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// });
|
|
162
|
+
// KICK OFF THE PING-PONG SAGA LOOP (FSM)
|
|
163
|
+
await this.executeSagaLoop({
|
|
164
|
+
initFrame, initDomainGraph,
|
|
165
|
+
peer,
|
|
166
|
+
sessionIdentity,
|
|
167
|
+
updates$,
|
|
168
|
+
localSpace,
|
|
169
|
+
tempSpace,
|
|
170
|
+
metaspace
|
|
171
|
+
});
|
|
169
172
|
|
|
170
173
|
resolveDone();
|
|
171
174
|
if (!updates$.complete) { throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`); }
|
|
@@ -226,13 +229,13 @@ export class SyncSagaCoordinator {
|
|
|
226
229
|
|
|
227
230
|
/**
|
|
228
231
|
* Drives the FSM loop of the Saga.
|
|
229
|
-
*
|
|
232
|
+
*
|
|
230
233
|
* @remarks
|
|
231
234
|
* **Execution Context**: **Sender (Local)**.
|
|
232
|
-
*
|
|
235
|
+
*
|
|
233
236
|
* This method manages the "Ping Pong" request-response cycle on the Sender.
|
|
234
237
|
* It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
|
|
235
|
-
*
|
|
238
|
+
*
|
|
236
239
|
* **Data Transport Note**:
|
|
237
240
|
* Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
|
|
238
241
|
* When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
|
|
@@ -240,8 +243,8 @@ export class SyncSagaCoordinator {
|
|
|
240
243
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
241
244
|
*/
|
|
242
245
|
protected async executeSagaLoop({
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
initFrame,
|
|
247
|
+
initDomainGraph,
|
|
245
248
|
peer,
|
|
246
249
|
sessionIdentity,
|
|
247
250
|
updates$,
|
|
@@ -249,8 +252,8 @@ export class SyncSagaCoordinator {
|
|
|
249
252
|
tempSpace,
|
|
250
253
|
metaspace
|
|
251
254
|
}: {
|
|
252
|
-
|
|
253
|
-
|
|
255
|
+
initFrame: SyncIbGib_V1,
|
|
256
|
+
initDomainGraph: FlatIbGibGraph,
|
|
254
257
|
peer: SyncPeerWitness,
|
|
255
258
|
sessionIdentity?: KeystoneIbGib_V1,
|
|
256
259
|
updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
|
|
@@ -260,44 +263,23 @@ export class SyncSagaCoordinator {
|
|
|
260
263
|
}): Promise<void> {
|
|
261
264
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
262
265
|
|
|
263
|
-
|
|
264
|
-
let currentFrame: SyncIbGib_V1 | null =
|
|
265
|
-
|
|
266
|
+
/** The current frame we just generated (e.g., Init or Delta Request) */
|
|
267
|
+
let currentFrame: SyncIbGib_V1 | null = initFrame;
|
|
268
|
+
/** The **domain** payload we need to transmit (data we are sending) */
|
|
266
269
|
let nextDomainIbGibs: IbGib_V1[] = [];
|
|
267
270
|
|
|
268
|
-
// First, inject local/tempSpace into peer so it can pull as
|
|
269
|
-
// needed...code smell?
|
|
270
|
-
peer.senderSpace = localSpace;
|
|
271
|
-
peer.senderTempSpace = tempSpace;
|
|
272
|
-
|
|
273
271
|
while (currentFrame) {
|
|
274
|
-
//
|
|
275
|
-
// 1. Calculate Full Dependency Graph (including Ancestors/DNA)
|
|
276
|
-
// We must do this BEFORE creating the Context so we can list them
|
|
277
|
-
// all in payloadAddrsDomain and payloadAddrsControl.
|
|
278
|
-
const nextDomainIbGibsAndDeps: IbGib_V1[] = [];
|
|
279
|
-
|
|
280
|
-
// A. Payload (Standard Deep Deps)
|
|
281
|
-
// TODO: THIS IS EXTREMELY INEFFICIENT. adjust this algorithm to
|
|
282
|
-
// only do the dependencies that the other end doesn't need (diff
|
|
283
|
-
// between tip and LCA). This can be done using the information in
|
|
284
|
-
// the ack's conflicts array.
|
|
285
|
-
for (const nextDomainIbGib of nextDomainIbGibs) {
|
|
286
|
-
let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
|
|
287
|
-
if (!nextDomainIbGibGraph) {
|
|
288
|
-
nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
|
|
289
|
-
}
|
|
290
|
-
if (nextDomainIbGibGraph) {
|
|
291
|
-
nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
|
|
292
|
-
} else {
|
|
293
|
-
throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
if (logalot) { console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`); }
|
|
272
|
+
// first, prepare for context transmission...
|
|
297
273
|
|
|
298
|
-
|
|
299
|
-
|
|
274
|
+
/**
|
|
275
|
+
* this is used later in pollForDomainPayloads **iif** payloads are
|
|
276
|
+
* received **in the response**, i.e., on the return trip. So if we
|
|
277
|
+
* request addrs, the response should have these addrs and their
|
|
278
|
+
* dependencies. The ibgibs corresponding to these addrs will be
|
|
279
|
+
* streamed and placed into this map.
|
|
280
|
+
*/
|
|
300
281
|
const domainPayloadsMap = new Map<string, IbGib_V1>();
|
|
282
|
+
// #region set up peer observable for any domainPayloadsMap
|
|
301
283
|
const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
|
|
302
284
|
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
303
285
|
next: async (ibgib: IbGib_V1) => {
|
|
@@ -319,43 +301,49 @@ export class SyncSagaCoordinator {
|
|
|
319
301
|
await subscription.unsubscribe();
|
|
320
302
|
},
|
|
321
303
|
}));
|
|
304
|
+
// #endregion set up peer observable for any domainPayloadsMap
|
|
322
305
|
|
|
323
|
-
//
|
|
306
|
+
// ...create/compose the Request Context itself...
|
|
324
307
|
const requestCtx = await createSyncSagaContext({
|
|
325
308
|
sagaFrame: currentFrame,
|
|
326
309
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
327
|
-
|
|
328
310
|
/**
|
|
329
311
|
* init frame: empty
|
|
330
|
-
*
|
|
312
|
+
* ack frame: possible push offers
|
|
313
|
+
* delta frame: requested ibgibs or commit offer
|
|
314
|
+
* commit frame: empty
|
|
331
315
|
*/
|
|
332
|
-
payloadIbGibsDomain:
|
|
316
|
+
payloadIbGibsDomain: nextDomainIbGibs,
|
|
333
317
|
localSpace,
|
|
334
318
|
});
|
|
335
319
|
|
|
336
|
-
// Log what we're sending
|
|
320
|
+
// #region Log what we're sending
|
|
337
321
|
if (logalotControlDomain) {
|
|
338
|
-
const domainAddrs =
|
|
339
|
-
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I:
|
|
322
|
+
const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
323
|
+
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
|
|
340
324
|
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
341
325
|
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
342
326
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
343
327
|
}
|
|
328
|
+
// #endregion Log what we're sending
|
|
344
329
|
|
|
345
330
|
// update our saga listeners...
|
|
346
331
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
347
|
-
updates$.next(requestCtx); // spins off
|
|
332
|
+
updates$.next(requestCtx); // spins off for saga UI updates
|
|
348
333
|
|
|
349
|
-
// ...And send the context
|
|
334
|
+
// ...And send the context.
|
|
335
|
+
peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
|
|
350
336
|
const responseCtx = await peer.witness(requestCtx);
|
|
351
337
|
|
|
352
|
-
//
|
|
338
|
+
// the send returned, but a peer can return a falsy responseCtx, if
|
|
339
|
+
// we just sent a commit to them and they're done. If so, there will
|
|
340
|
+
// necessarily be no payload to wait to receive
|
|
353
341
|
if (!responseCtx) {
|
|
354
342
|
if (currentFrame) {
|
|
355
343
|
// Check for Commit (Peer silence expected)
|
|
356
344
|
const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
|
|
357
345
|
if (msg?.data?.stage === SyncStage.commit) {
|
|
358
|
-
if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete
|
|
346
|
+
if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`); }
|
|
359
347
|
currentFrame = null;
|
|
360
348
|
break;
|
|
361
349
|
} else {
|
|
@@ -366,19 +354,21 @@ export class SyncSagaCoordinator {
|
|
|
366
354
|
}
|
|
367
355
|
}
|
|
368
356
|
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
// ---------------------------------------------------------------------
|
|
372
|
-
// at this point, we have received the response context ibgib, but
|
|
357
|
+
// at this point, we did indeed receive a response to analyze. BUT!
|
|
358
|
+
// we have only necessarily received the response context ibgib.
|
|
373
359
|
// if there were payloads expected to be transferred, they may not
|
|
374
360
|
// be done yet.
|
|
361
|
+
|
|
375
362
|
if (!responseCtx.data) { throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`); }
|
|
376
|
-
updates$.next(responseCtx); // spins off
|
|
363
|
+
updates$.next(responseCtx); // spins off for saga UI updating
|
|
364
|
+
|
|
365
|
+
// validate context
|
|
366
|
+
await validateContextAndSagaFrame
|
|
377
367
|
|
|
378
368
|
// Extract expected domain addresses from response context
|
|
379
369
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] as string[] || [];
|
|
380
370
|
|
|
381
|
-
// Poll for them if needed
|
|
371
|
+
// Poll for them if needed. see above jsdocs for domainPayloadsMap
|
|
382
372
|
if (responsePayloadAddrsDomain.length > 0) {
|
|
383
373
|
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
384
374
|
expectedAddrs: responsePayloadAddrsDomain,
|
|
@@ -388,51 +378,61 @@ export class SyncSagaCoordinator {
|
|
|
388
378
|
});
|
|
389
379
|
}
|
|
390
380
|
|
|
391
|
-
//
|
|
392
|
-
// domain payloads (if applicable), so we are ready to do the next
|
|
393
|
-
// iteration in the saga loop
|
|
394
|
-
|
|
395
|
-
// Log what we received back
|
|
381
|
+
// #region Log what we received back
|
|
396
382
|
if (!responseCtx.sagaFrame) { throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`); }
|
|
397
383
|
if (logalotControlDomain) {
|
|
398
384
|
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] as string[] || [];
|
|
399
|
-
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I:
|
|
385
|
+
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
|
|
400
386
|
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
401
387
|
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
402
388
|
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
403
389
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
404
390
|
}
|
|
391
|
+
// #endregion Log what we received back
|
|
405
392
|
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
|
|
393
|
+
// at this point, we have received the context AND **all** of the
|
|
394
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
395
|
+
// iteration in the saga loop
|
|
396
|
+
|
|
397
|
+
// this is the part that drives the FSM forward, i.e., when we
|
|
398
|
+
// evolve the sync saga ibgib itself to the next frame if we aren't
|
|
399
|
+
// finished/errored out.
|
|
400
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
413
401
|
sagaContext: responseCtx,
|
|
414
402
|
mySpace: localSpace,
|
|
415
403
|
myTempSpace: tempSpace,
|
|
416
404
|
metaspace,
|
|
417
405
|
});
|
|
418
406
|
|
|
419
|
-
if (!
|
|
420
|
-
if (logalot) { console.log(`${lc} Handler returned null (Saga End)
|
|
407
|
+
if (!contextResult) {
|
|
408
|
+
if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`); }
|
|
421
409
|
break;
|
|
422
410
|
}
|
|
423
411
|
|
|
424
|
-
|
|
412
|
+
// #region error conditions throw
|
|
413
|
+
if (contextResult.errorMsg) {
|
|
414
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
|
|
415
|
+
} else if (!contextResult.nextFrameInfo) {
|
|
416
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
417
|
+
} else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
418
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
419
|
+
}
|
|
420
|
+
// #endregion error conditions throw
|
|
425
421
|
|
|
426
|
-
//
|
|
427
|
-
|
|
422
|
+
// we have another frame to process and send out, with possibly
|
|
423
|
+
// payload domain ibgibs as well
|
|
424
|
+
const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
|
|
425
|
+
currentFrame = frame;
|
|
426
|
+
nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
|
|
428
427
|
|
|
429
|
-
// Log handler output for next iteration
|
|
428
|
+
// #region Log handler output for next iteration
|
|
430
429
|
if (logalotControlDomain) {
|
|
431
430
|
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
432
|
-
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I:
|
|
431
|
+
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
|
|
433
432
|
console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
434
433
|
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
435
434
|
}
|
|
435
|
+
// #endregion Log handler output for next iteration
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
438
|
|
|
@@ -499,7 +499,7 @@ export class SyncSagaCoordinator {
|
|
|
499
499
|
|
|
500
500
|
const res = await getLatestAddrs({ space, tjpAddrs: tjps });
|
|
501
501
|
if (!res.data || !res.data.latestAddrsMap) {
|
|
502
|
-
throw new Error(`${lc} Failed to get latest addrs. (E:
|
|
502
|
+
throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
|
|
503
503
|
}
|
|
504
504
|
|
|
505
505
|
// if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
|
|
@@ -552,10 +552,10 @@ export class SyncSagaCoordinator {
|
|
|
552
552
|
|
|
553
553
|
/**
|
|
554
554
|
* Creates the Initial Saga Frame (Init Stage).
|
|
555
|
-
*
|
|
555
|
+
*
|
|
556
556
|
* @remarks
|
|
557
557
|
* **Execution Context**: **Sender (Local)**.
|
|
558
|
-
*
|
|
558
|
+
*
|
|
559
559
|
* Generates the first frame containing the Knowledge Vector of the Local Space.
|
|
560
560
|
* This is sent to the Receiver to begin Gap Analysis.
|
|
561
561
|
*/
|
|
@@ -575,14 +575,14 @@ export class SyncSagaCoordinator {
|
|
|
575
575
|
metaspace: MetaspaceService,
|
|
576
576
|
localSpace: IbGibSpaceAny,
|
|
577
577
|
tempSpace: IbGibSpaceAny,
|
|
578
|
-
}): Promise<{
|
|
578
|
+
}): Promise<{ initFrame: SyncIbGib_V1, initDomainGraph: { [addr: string]: IbGib_V1 } }> {
|
|
579
579
|
const lc = `${this.lc}[${this.createInitFrame.name}]`;
|
|
580
580
|
try {
|
|
581
581
|
if (logalot) { console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`); }
|
|
582
582
|
|
|
583
583
|
// Analyze Timelines & Stones
|
|
584
584
|
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
585
|
-
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
585
|
+
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
586
586
|
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
587
587
|
|
|
588
588
|
// we need to store the fullGraph in our tempSpace for later...
|
|
@@ -627,7 +627,7 @@ export class SyncSagaCoordinator {
|
|
|
627
627
|
|
|
628
628
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
629
629
|
|
|
630
|
-
return { sagaFrame,
|
|
630
|
+
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
631
631
|
} catch (error) {
|
|
632
632
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
633
633
|
throw error;
|
|
@@ -639,7 +639,7 @@ export class SyncSagaCoordinator {
|
|
|
639
639
|
/**
|
|
640
640
|
* Helper to poll for streaming domain payloads and put them in the
|
|
641
641
|
* local {@link tempSpace}.
|
|
642
|
-
*
|
|
642
|
+
*
|
|
643
643
|
* @returns when all {@link expectedAddrs} are done being transmitted.
|
|
644
644
|
*/
|
|
645
645
|
protected async pollForDomainPayloads({
|
|
@@ -662,7 +662,7 @@ export class SyncSagaCoordinator {
|
|
|
662
662
|
let pending = [...expectedAddrs];
|
|
663
663
|
const start = Date.now();
|
|
664
664
|
/**
|
|
665
|
-
* This needs
|
|
665
|
+
* This needs
|
|
666
666
|
*/
|
|
667
667
|
const timeoutMs = 5 * 60 * 1000; // 5 minutes...arbitrary at this point. This needs to be pulled out and improved eesh.
|
|
668
668
|
|
|
@@ -710,23 +710,23 @@ export class SyncSagaCoordinator {
|
|
|
710
710
|
* This is the heart of the "ping pong" transaction, where we send a context
|
|
711
711
|
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
712
712
|
* a whole.
|
|
713
|
-
*
|
|
713
|
+
*
|
|
714
714
|
* This is called in two places:
|
|
715
|
-
*
|
|
715
|
+
*
|
|
716
716
|
* ## 1. Sender
|
|
717
|
-
*
|
|
717
|
+
*
|
|
718
718
|
* On the sender, this is called within the {@link executeSagaLoop} which
|
|
719
|
-
* initiates and drives the sync.
|
|
720
|
-
*
|
|
719
|
+
* initiates and drives the sync.
|
|
720
|
+
*
|
|
721
721
|
* ## 2. Receiver
|
|
722
|
-
*
|
|
722
|
+
*
|
|
723
723
|
* On the receiver, this is called directly by the receiving endpoint. That
|
|
724
|
-
* endpoint's job is basically to get these things collocated and prepared
|
|
724
|
+
* endpoint's job is basically to get these things collocated and prepared
|
|
725
725
|
* to make this call.
|
|
726
|
-
*
|
|
726
|
+
*
|
|
727
727
|
* This is a one-off on the receiver.
|
|
728
728
|
*/
|
|
729
|
-
public async
|
|
729
|
+
public async handleResponseSagaContext({
|
|
730
730
|
sagaContext,
|
|
731
731
|
mySpace,
|
|
732
732
|
myTempSpace,
|
|
@@ -746,8 +746,8 @@ export class SyncSagaCoordinator {
|
|
|
746
746
|
identity?: KeystoneIbGib_V1,
|
|
747
747
|
identitySecret?: string,
|
|
748
748
|
metaspace: MetaspaceService,
|
|
749
|
-
}): Promise<
|
|
750
|
-
const lc = `${this.lc}[${this.
|
|
749
|
+
}): Promise<HandleSagaResponseContextResult | null> {
|
|
750
|
+
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
751
751
|
try {
|
|
752
752
|
if (logalot) { console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`); }
|
|
753
753
|
|
|
@@ -765,9 +765,10 @@ export class SyncSagaCoordinator {
|
|
|
765
765
|
*/
|
|
766
766
|
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
767
767
|
|
|
768
|
+
let nextFrameInfo: NextSagaFrameInfo;
|
|
768
769
|
switch (stage) {
|
|
769
770
|
case SyncStage.init:
|
|
770
|
-
|
|
771
|
+
nextFrameInfo = await this.handleInitFrame({
|
|
771
772
|
sagaIbGib,
|
|
772
773
|
messageData: messageData as SyncSagaMessageInitData_V1,
|
|
773
774
|
metaspace,
|
|
@@ -776,22 +777,30 @@ export class SyncSagaCoordinator {
|
|
|
776
777
|
identity,
|
|
777
778
|
identitySecret
|
|
778
779
|
});
|
|
780
|
+
break;
|
|
779
781
|
|
|
780
782
|
case SyncStage.ack:
|
|
781
|
-
|
|
783
|
+
nextFrameInfo = await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
|
|
784
|
+
break;
|
|
782
785
|
|
|
783
786
|
case SyncStage.delta:
|
|
784
|
-
|
|
787
|
+
nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
788
|
+
break;
|
|
785
789
|
|
|
786
790
|
case SyncStage.commit:
|
|
787
|
-
|
|
791
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
792
|
+
break;
|
|
788
793
|
|
|
789
794
|
default:
|
|
790
795
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
791
796
|
}
|
|
797
|
+
|
|
798
|
+
return { errorMsg: undefined, nextFrameInfo, }
|
|
799
|
+
|
|
792
800
|
} catch (error) {
|
|
793
|
-
|
|
794
|
-
|
|
801
|
+
const errorMsg = `${lc} ${extractErrorMsg(error)}`;
|
|
802
|
+
console.error(errorMsg);
|
|
803
|
+
return { errorMsg }
|
|
795
804
|
} finally {
|
|
796
805
|
if (logalot) { console.log(`${lc} complete.`); }
|
|
797
806
|
}
|
|
@@ -801,10 +810,10 @@ export class SyncSagaCoordinator {
|
|
|
801
810
|
|
|
802
811
|
/**
|
|
803
812
|
* Handles the `Init` frame.
|
|
804
|
-
*
|
|
813
|
+
*
|
|
805
814
|
* @remarks
|
|
806
815
|
* **Execution Context**: **Receiver (Remote)**.
|
|
807
|
-
*
|
|
816
|
+
*
|
|
808
817
|
* The Receiver performs Gap Analysis here:
|
|
809
818
|
* 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
|
|
810
819
|
* 2. Identifies what Sender needs (`pushOfferAddrs`).
|
|
@@ -828,7 +837,7 @@ export class SyncSagaCoordinator {
|
|
|
828
837
|
mySpace: IbGibSpaceAny,
|
|
829
838
|
/**
|
|
830
839
|
* Local temp space relative to the execution context's POV.
|
|
831
|
-
*
|
|
840
|
+
*
|
|
832
841
|
* NOTE: Since this always executes on the receiver's end, this should
|
|
833
842
|
* be the receiver's temp space.
|
|
834
843
|
*/
|
|
@@ -836,18 +845,17 @@ export class SyncSagaCoordinator {
|
|
|
836
845
|
metaspace: MetaspaceService,
|
|
837
846
|
identity?: KeystoneIbGib_V1,
|
|
838
847
|
identitySecret?: string,
|
|
839
|
-
}): Promise<
|
|
848
|
+
}): Promise<NextSagaFrameInfo> {
|
|
840
849
|
const lc = `${this.lc}[${this.handleInitFrame.name}]`;
|
|
841
850
|
try {
|
|
842
851
|
if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
|
|
843
852
|
|
|
844
|
-
console.log(`${lc} [TEST DEBUG]
|
|
845
|
-
if (logalot) { console.log(`${lc} starting...`); }
|
|
853
|
+
console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
|
|
846
854
|
|
|
847
855
|
// Extract Init Data
|
|
848
856
|
const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
|
|
849
857
|
if (initData.stage !== SyncStage.init) {
|
|
850
|
-
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E:
|
|
858
|
+
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
|
|
851
859
|
}
|
|
852
860
|
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
853
861
|
if (!initData || !initData.knowledgeVector) {
|
|
@@ -859,8 +867,8 @@ export class SyncSagaCoordinator {
|
|
|
859
867
|
|
|
860
868
|
// 2. Gap Analysis
|
|
861
869
|
const conflicts: SyncSagaConflictInfo[] = [];
|
|
862
|
-
const
|
|
863
|
-
const
|
|
870
|
+
const deltaRequestAddrInfos: SyncSagaRequestAddrInfo[] = [];
|
|
871
|
+
const pushOfferInfos: SyncSagaPushOfferInfo[] = [];
|
|
864
872
|
|
|
865
873
|
// First do the consant stones (Non-TJPs)
|
|
866
874
|
const stones = initData.stones || [];
|
|
@@ -874,13 +882,15 @@ export class SyncSagaCoordinator {
|
|
|
874
882
|
if (addrsNotFound && addrsNotFound.length > 0) {
|
|
875
883
|
if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
|
|
876
884
|
addrsNotFound.forEach(addr => {
|
|
877
|
-
if (!
|
|
885
|
+
if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
|
|
886
|
+
deltaRequestAddrInfos.push({ addr }); // no tjpAddr
|
|
887
|
+
}
|
|
878
888
|
});
|
|
879
889
|
}
|
|
880
890
|
}
|
|
881
891
|
|
|
882
892
|
/**
|
|
883
|
-
* "remote"
|
|
893
|
+
* "remote" is sender in this case
|
|
884
894
|
*/
|
|
885
895
|
const remoteKV = initData.knowledgeVector;
|
|
886
896
|
if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
|
|
@@ -889,7 +899,7 @@ export class SyncSagaCoordinator {
|
|
|
889
899
|
if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
|
|
890
900
|
|
|
891
901
|
// 1. Get Local Latest Addrs for all TJPs
|
|
892
|
-
let
|
|
902
|
+
let localLatestAddrsMap: { [tjp: string]: string | null } = {};
|
|
893
903
|
if (remoteTjps.length > 0) {
|
|
894
904
|
// Batch get latest addrs for the TJPs
|
|
895
905
|
const resGetLatestAddrs = await getLatestAddrs({
|
|
@@ -898,56 +908,91 @@ export class SyncSagaCoordinator {
|
|
|
898
908
|
});
|
|
899
909
|
if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
|
|
900
910
|
if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
|
|
901
|
-
|
|
902
|
-
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(
|
|
903
|
-
if (logalot) { console.log(`${lc} localKV: ${pretty(
|
|
911
|
+
localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
|
|
912
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
|
|
913
|
+
if (logalot) { console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`); }
|
|
904
914
|
}
|
|
905
915
|
|
|
906
916
|
// 2. Gap Analysis
|
|
907
917
|
for (const tjp of remoteTjps) {
|
|
908
918
|
const remoteAddr = remoteKV[tjp];
|
|
909
|
-
const localAddr =
|
|
919
|
+
const localAddr = localLatestAddrsMap[tjp];
|
|
910
920
|
|
|
911
921
|
if (!localAddr) {
|
|
912
922
|
// We (Receiver) don't have this timeline at all. Request it.
|
|
913
923
|
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
914
|
-
|
|
924
|
+
deltaRequestAddrInfos.push({
|
|
925
|
+
addr: remoteAddr,
|
|
926
|
+
tjpAddr: tjp,
|
|
927
|
+
// we don't have this timeline at all
|
|
928
|
+
// latestAddrAlreadyHave: undefined
|
|
929
|
+
});
|
|
915
930
|
continue;
|
|
916
931
|
}
|
|
917
932
|
|
|
933
|
+
// we do have this timeline...
|
|
934
|
+
|
|
918
935
|
if (localAddr === remoteAddr) {
|
|
919
|
-
//
|
|
936
|
+
// ...already synced
|
|
920
937
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
921
938
|
continue;
|
|
922
939
|
}
|
|
923
940
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
924
941
|
|
|
925
|
-
//
|
|
926
|
-
|
|
927
|
-
|
|
942
|
+
// we have this timeline but it's not synced...
|
|
943
|
+
|
|
944
|
+
// We're executing on receiver. Check if Remote (Sender) is in
|
|
945
|
+
// our past, and if so, we are ahead and need to push the delta
|
|
946
|
+
// to remote
|
|
947
|
+
const remoteIsInPast = await isPastFrame({
|
|
928
948
|
olderAddr: remoteAddr,
|
|
929
949
|
newerAddr: localAddr,
|
|
930
950
|
space: mySpace,
|
|
931
951
|
});
|
|
932
952
|
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
|
|
953
|
+
if (remoteIsInPast) {
|
|
954
|
+
// we're ahead, so push the delta of what the sender doesn't
|
|
955
|
+
// have (we have full knowledge)
|
|
956
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
|
|
957
|
+
const deltaGraph = await getDeltaDependencyGraph({
|
|
958
|
+
ibGibAddr: localAddr,
|
|
959
|
+
live: false, // always live: false right?
|
|
960
|
+
latestCommonFrameAddr: remoteAddr,
|
|
961
|
+
space: mySpace,
|
|
962
|
+
});
|
|
963
|
+
pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
|
|
936
964
|
} else {
|
|
937
|
-
// Remote is not in our past.
|
|
938
|
-
//
|
|
965
|
+
// Remote tip is not in our past. So, either Remote is
|
|
966
|
+
// ahead (we're in THEIR past, i.e., Fast-Backward) OR
|
|
967
|
+
// Diverged/conflict (we've both made edits).
|
|
939
968
|
|
|
940
969
|
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
970
|
+
/**
|
|
971
|
+
* we could first check for existence of remoteAddr in
|
|
972
|
+
* mySpace, but this is quick and easy. If it throws, we
|
|
973
|
+
* don't have it so it can't be in the past.
|
|
974
|
+
*/
|
|
975
|
+
let localIsInPast = false;
|
|
976
|
+
try {
|
|
977
|
+
// we could
|
|
978
|
+
localIsInPast = await isPastFrame({
|
|
979
|
+
olderAddr: localAddr,
|
|
980
|
+
newerAddr: remoteAddr,
|
|
981
|
+
space: mySpace,
|
|
982
|
+
});
|
|
983
|
+
} catch (error) {
|
|
984
|
+
// couldn't get one of them, so localIsInPast remains false.
|
|
985
|
+
if (logalot) { console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`); }
|
|
986
|
+
}
|
|
946
987
|
|
|
947
|
-
if (
|
|
988
|
+
if (localIsInPast) {
|
|
948
989
|
// Fast-Forward: We update to remote's tip.
|
|
949
990
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
950
|
-
|
|
991
|
+
deltaRequestAddrInfos.push({
|
|
992
|
+
addr: remoteAddr,
|
|
993
|
+
tjpAddr: tjp,
|
|
994
|
+
latestAddrAlreadyHave: localAddr,
|
|
995
|
+
});
|
|
951
996
|
} else {
|
|
952
997
|
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
953
998
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
|
|
@@ -966,7 +1011,7 @@ export class SyncSagaCoordinator {
|
|
|
966
1011
|
terminal: true
|
|
967
1012
|
});
|
|
968
1013
|
} else if (conflictStrategy === 'optimistic') {
|
|
969
|
-
// Optimistic: We want
|
|
1014
|
+
// Optimistic: We want resolve this.
|
|
970
1015
|
// We need to send our history to the Sender so they can Merge.
|
|
971
1016
|
|
|
972
1017
|
// Fetch Full History for Local Timeline
|
|
@@ -979,10 +1024,10 @@ export class SyncSagaCoordinator {
|
|
|
979
1024
|
// We have localAddr.
|
|
980
1025
|
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
981
1026
|
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
982
|
-
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E:
|
|
1027
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
983
1028
|
}
|
|
984
1029
|
const localTip = resLocalTip.ibGibs[0];
|
|
985
|
-
if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E:
|
|
1030
|
+
if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`); }
|
|
986
1031
|
|
|
987
1032
|
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
988
1033
|
|
|
@@ -994,9 +1039,8 @@ export class SyncSagaCoordinator {
|
|
|
994
1039
|
reason: 'divergence',
|
|
995
1040
|
terminal: false
|
|
996
1041
|
});
|
|
997
|
-
|
|
998
1042
|
} else {
|
|
999
|
-
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E:
|
|
1043
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
|
|
1000
1044
|
}
|
|
1001
1045
|
}
|
|
1002
1046
|
}
|
|
@@ -1012,57 +1056,15 @@ export class SyncSagaCoordinator {
|
|
|
1012
1056
|
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
1013
1057
|
}
|
|
1014
1058
|
|
|
1015
|
-
// 3. Build Knowledge Vector (Full History for known timelines)
|
|
1016
|
-
// [NEW] Smart Diff
|
|
1017
|
-
// We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
|
|
1018
|
-
// Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
|
|
1022
|
-
*/
|
|
1023
|
-
const knowledgeVector: { [groupKey: string]: string[] } = {};
|
|
1024
|
-
|
|
1025
|
-
// [Smart Diff] Populate knowledge from timelines identified by Sender
|
|
1026
|
-
for (const tjp of remoteTjps) {
|
|
1027
|
-
const localAddr = localKV[tjp];
|
|
1028
|
-
if (localAddr) {
|
|
1029
|
-
const res = await getFromSpace({ addr: localAddr, space: mySpace });
|
|
1030
|
-
if (res.success && res.ibGibs?.[0]) {
|
|
1031
|
-
const ibGib = res.ibGibs[0];
|
|
1032
|
-
const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
|
|
1033
|
-
if (!knowledgeVector[realTjp]) {
|
|
1034
|
-
const past = ibGib.rel8ns?.past || [];
|
|
1035
|
-
knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
// Also populate from `knowledgeVector` in Init if we want bidirectional?
|
|
1042
|
-
// No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
|
|
1043
|
-
// Let's assume standard flow:
|
|
1044
|
-
// 1. Sender says "I have X"
|
|
1045
|
-
// 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
|
|
1046
|
-
// Problem: Receiver doesn't know X is related to Y without X.
|
|
1047
|
-
|
|
1048
|
-
// SOLUTION:
|
|
1049
|
-
// The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
|
|
1050
|
-
// `SyncSagaMessageInitData_V1` extends `SyncInitData`.
|
|
1051
|
-
// `SyncInitData` has `knowledgeVector`.
|
|
1052
|
-
// If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
|
|
1053
|
-
|
|
1054
|
-
// Let's implement Sender populating `Init.knowledgeVector`.
|
|
1055
|
-
// But `SyncSagaCoordinator.startSaga` creates Init.
|
|
1056
|
-
|
|
1057
1059
|
// 3. Create Ack Frame
|
|
1058
|
-
|
|
1060
|
+
if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`); }
|
|
1061
|
+
const sagaId = sagaIbGib.data.uuid;
|
|
1059
1062
|
// Create Payload Stone
|
|
1060
1063
|
const ackData: SyncSagaMessageAckData_V1 = {
|
|
1061
1064
|
sagaId,
|
|
1062
1065
|
stage: SyncStage.ack,
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
knowledgeVector,
|
|
1066
|
+
deltaRequestAddrInfos,
|
|
1067
|
+
pushOfferInfos,
|
|
1066
1068
|
};
|
|
1067
1069
|
if (conflicts?.length > 0) { ackData.conflicts = conflicts; }
|
|
1068
1070
|
|
|
@@ -1090,14 +1092,63 @@ export class SyncSagaCoordinator {
|
|
|
1090
1092
|
*/
|
|
1091
1093
|
// let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
|
|
1092
1094
|
|
|
1093
|
-
|
|
1095
|
+
let payloadIbGibsDomain: IbGib_V1[] | undefined;
|
|
1096
|
+
if (pushOfferInfos.length > 0) {
|
|
1097
|
+
const searchSecondSpaceAddrs: IbGibAddr[] = [];
|
|
1098
|
+
payloadIbGibsDomain = [];
|
|
1099
|
+
const domainAddrs: IbGibAddr[] = pushOfferInfos.flatMap(x => x.addrs);
|
|
1100
|
+
const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
|
|
1101
|
+
const resGetRawData = resGet.rawResultIbGib?.data as IbGibSpaceResultData | undefined;
|
|
1102
|
+
if (!resGetRawData) { throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`); }
|
|
1103
|
+
const addrsNotFound = resGetRawData.addrsNotFound ?? [];
|
|
1104
|
+
const addrsErrored = resGetRawData.addrsErrored ?? [];
|
|
1105
|
+
if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
|
|
1106
|
+
// found all of them
|
|
1107
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
|
|
1108
|
+
} else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
|
|
1109
|
+
// found some of them
|
|
1110
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
|
|
1111
|
+
const foundPlusNotFoundCount =
|
|
1112
|
+
payloadIbGibsDomain.length +
|
|
1113
|
+
addrsNotFound.length +
|
|
1114
|
+
addrsErrored.length;
|
|
1115
|
+
if (foundPlusNotFoundCount === domainAddrs.length) {
|
|
1116
|
+
// we can still conceivably get the remaining addrs
|
|
1117
|
+
addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1118
|
+
addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1119
|
+
} else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
|
|
1120
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
|
|
1121
|
+
} else {
|
|
1122
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
|
|
1123
|
+
}
|
|
1124
|
+
} else {
|
|
1125
|
+
// found none of them(?)
|
|
1126
|
+
console.warn(`${lc} we were expecting all domainAddrs in mySpace (${mySpace.ib}) but we couldn't get all of them. addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. Trying temp space (${myTempSpace.ib}). (W: f61908e264f8dddd2e3fb9084463d826)`);
|
|
1127
|
+
domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1128
|
+
}
|
|
1129
|
+
if (searchSecondSpaceAddrs.length > 0) {
|
|
1130
|
+
if (logalot) { console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`); }
|
|
1131
|
+
const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
|
|
1132
|
+
if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
|
|
1133
|
+
// got them all the second try
|
|
1134
|
+
resGet2.ibGibs.forEach(x => payloadIbGibsDomain!.push(x));
|
|
1135
|
+
} else {
|
|
1136
|
+
resGet2.ibGibs?.forEach(x => payloadIbGibsDomain!.push(x));
|
|
1137
|
+
const addrsFailed = domainAddrs.filter(x =>
|
|
1138
|
+
!payloadIbGibsDomain!.map(pid => getIbGibAddr({ ibGib: pid })).includes(x)
|
|
1139
|
+
);
|
|
1140
|
+
throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
// we have now populated payloadIbGibsDomain
|
|
1094
1144
|
}
|
|
1095
1145
|
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1146
|
+
throw new Error(`not implemented (E: ed3f98abb0988c5ae8038bb8d741fb26)`);
|
|
1147
|
+
// return {
|
|
1148
|
+
// frame: ackFrame,
|
|
1149
|
+
// // conflictInfos,
|
|
1150
|
+
// payloadIbGibsDomain,
|
|
1151
|
+
// };
|
|
1101
1152
|
|
|
1102
1153
|
} catch (error) {
|
|
1103
1154
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1109,14 +1160,14 @@ export class SyncSagaCoordinator {
|
|
|
1109
1160
|
|
|
1110
1161
|
/**
|
|
1111
1162
|
* Handles the `Ack` frame.
|
|
1112
|
-
*
|
|
1163
|
+
*
|
|
1113
1164
|
* @remarks
|
|
1114
1165
|
* **Execution Context**: **Sender (Local)**.
|
|
1115
|
-
*
|
|
1166
|
+
*
|
|
1116
1167
|
* The Sender reacts to the Receiver's requirements:
|
|
1117
1168
|
* 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
|
|
1118
1169
|
* 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
|
|
1119
|
-
*
|
|
1170
|
+
*
|
|
1120
1171
|
* Returns a `Delta` frame.
|
|
1121
1172
|
*/
|
|
1122
1173
|
protected async handleAckFrame({
|
|
@@ -1133,7 +1184,7 @@ export class SyncSagaCoordinator {
|
|
|
1133
1184
|
tempSpace: IbGibSpaceAny,
|
|
1134
1185
|
metaspace: MetaspaceService,
|
|
1135
1186
|
identity?: KeystoneIbGib_V1,
|
|
1136
|
-
}): Promise<
|
|
1187
|
+
}): Promise<NextSagaFrameInfo> {
|
|
1137
1188
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
1138
1189
|
try {
|
|
1139
1190
|
if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
|
|
@@ -1161,7 +1212,7 @@ export class SyncSagaCoordinator {
|
|
|
1161
1212
|
console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
|
|
1162
1213
|
// Terminal failure. Sender should probably Commit(Fail) or just Abort.
|
|
1163
1214
|
// For now, throw to trigger abort.
|
|
1164
|
-
throw new Error(`${lc} Peer reported terminal conflicts. (E:
|
|
1215
|
+
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1165
1216
|
}
|
|
1166
1217
|
|
|
1167
1218
|
const optimisticConflicts = conflicts.filter(c => !c.terminal);
|
|
@@ -1189,7 +1240,7 @@ export class SyncSagaCoordinator {
|
|
|
1189
1240
|
// Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
|
|
1190
1241
|
|
|
1191
1242
|
// If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
|
|
1192
|
-
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1243
|
+
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1193
1244
|
// AND we piggyback our own requests?
|
|
1194
1245
|
// OR: We treat the Conflict Resolution as a sub-saga or side-effect?
|
|
1195
1246
|
|
|
@@ -1218,7 +1269,7 @@ export class SyncSagaCoordinator {
|
|
|
1218
1269
|
// We can fetch valid 'past' from space.
|
|
1219
1270
|
const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
|
|
1220
1271
|
const senderTipIbGib = resSenderTip.ibGibs?.[0];
|
|
1221
|
-
if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E:
|
|
1272
|
+
if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
|
|
1222
1273
|
|
|
1223
1274
|
// Basic Diff: Find what Receiver has that we don't.
|
|
1224
1275
|
// Actually, we need to traverse OUR past to find commonality.
|
|
@@ -1303,24 +1354,25 @@ export class SyncSagaCoordinator {
|
|
|
1303
1354
|
|
|
1304
1355
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1305
1356
|
|
|
1306
|
-
const deltaReqAddrs = ackData.
|
|
1307
|
-
const pushOfferAddrs = ackData.
|
|
1357
|
+
const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
|
|
1358
|
+
const pushOfferAddrs = ackData.pushOfferInfos || [];
|
|
1308
1359
|
|
|
1309
1360
|
// 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
|
|
1310
1361
|
const pullReqAddrs: string[] = [];
|
|
1311
1362
|
for (const addr of pushOfferAddrs) {
|
|
1312
|
-
const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1313
|
-
if (!existing) {
|
|
1314
|
-
|
|
1315
|
-
}
|
|
1363
|
+
// const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1364
|
+
// if (!existing) {
|
|
1365
|
+
// pullReqAddrs.push(addr);
|
|
1366
|
+
// }
|
|
1316
1367
|
}
|
|
1317
1368
|
|
|
1318
1369
|
// 2. Process Delta Requests (Push Payload)
|
|
1319
1370
|
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1371
|
+
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1320
1372
|
const skipAddrs = new Set<string>();
|
|
1321
1373
|
if (ackData.knowledgeVector) {
|
|
1322
1374
|
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1323
|
-
addrs.forEach(a => skipAddrs.add(a));
|
|
1375
|
+
// addrs.forEach(a => skipAddrs.add(a));
|
|
1324
1376
|
});
|
|
1325
1377
|
}
|
|
1326
1378
|
|
|
@@ -1328,24 +1380,24 @@ export class SyncSagaCoordinator {
|
|
|
1328
1380
|
// Gather all tips to sync first
|
|
1329
1381
|
const tipsToSync: IbGib_V1[] = [];
|
|
1330
1382
|
for (const addr of deltaReqAddrs) {
|
|
1331
|
-
let ibGib = srcGraph[addr];
|
|
1332
|
-
if (!ibGib) {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1338
|
-
if (ibGib) {
|
|
1339
|
-
|
|
1340
|
-
} else {
|
|
1341
|
-
|
|
1342
|
-
}
|
|
1383
|
+
// let ibGib = srcGraph[addr];
|
|
1384
|
+
// if (!ibGib) {
|
|
1385
|
+
// const res = await getFromSpace({ addr, space: destSpace });
|
|
1386
|
+
// if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1387
|
+
// ibGib = res.ibGibs[0];
|
|
1388
|
+
// }
|
|
1389
|
+
// }
|
|
1390
|
+
// if (ibGib) {
|
|
1391
|
+
// tipsToSync.push(ibGib);
|
|
1392
|
+
// } else {
|
|
1393
|
+
// throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
|
|
1394
|
+
// }
|
|
1343
1395
|
}
|
|
1344
1396
|
|
|
1345
1397
|
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1346
1398
|
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1347
1399
|
// `getDependencyGraph` takes a single ibGib.
|
|
1348
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1400
|
+
// We can optimize by doing it for each tip and unioning the result?
|
|
1349
1401
|
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1350
1402
|
// We will loop.
|
|
1351
1403
|
|
|
@@ -1420,7 +1472,8 @@ export class SyncSagaCoordinator {
|
|
|
1420
1472
|
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1421
1473
|
|
|
1422
1474
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1423
|
-
return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1475
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1476
|
+
throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
|
|
1424
1477
|
} catch (error) {
|
|
1425
1478
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1426
1479
|
throw error;
|
|
@@ -1431,10 +1484,10 @@ export class SyncSagaCoordinator {
|
|
|
1431
1484
|
|
|
1432
1485
|
/**
|
|
1433
1486
|
* Handles the `Delta` frame.
|
|
1434
|
-
*
|
|
1487
|
+
*
|
|
1435
1488
|
* @remarks
|
|
1436
1489
|
* **Execution Context**: **Both**.
|
|
1437
|
-
*
|
|
1490
|
+
*
|
|
1438
1491
|
* 1. **Ingestion**: Receives data sent by Peer (`payloadAddrs`). Resolves them from `Rel8ns.payload` (implicitly via space/context) and returns them in `receivedPayload` to be saved.
|
|
1439
1492
|
* 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
|
|
1440
1493
|
* 3. **Completion**: If no more requests, transitions to `Commit`.
|
|
@@ -1453,7 +1506,7 @@ export class SyncSagaCoordinator {
|
|
|
1453
1506
|
tempSpace: IbGibSpaceAny,
|
|
1454
1507
|
metaspace: MetaspaceService,
|
|
1455
1508
|
identity?: KeystoneIbGib_V1,
|
|
1456
|
-
}): Promise<
|
|
1509
|
+
}): Promise<NextSagaFrameInfo> {
|
|
1457
1510
|
const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
|
|
1458
1511
|
if (logalot) { console.log(`${lc} starting...`); }
|
|
1459
1512
|
|
|
@@ -1466,7 +1519,7 @@ export class SyncSagaCoordinator {
|
|
|
1466
1519
|
if (deltaData.stage !== SyncStage.delta) {
|
|
1467
1520
|
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1468
1521
|
}
|
|
1469
|
-
if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I:
|
|
1522
|
+
if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
|
|
1470
1523
|
|
|
1471
1524
|
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1472
1525
|
|
|
@@ -1477,8 +1530,8 @@ export class SyncSagaCoordinator {
|
|
|
1477
1530
|
// 1. Process Received Payload (Ingest)
|
|
1478
1531
|
const receivedPayloadIbGibs: IbGib_V1[] = [];
|
|
1479
1532
|
if (payloadAddrs.length > 0) {
|
|
1480
|
-
// We use `payloadAddrs` as the manifest.
|
|
1481
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1533
|
+
// We use `payloadAddrs` as the manifest.
|
|
1534
|
+
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1482
1535
|
// assuming the "Transport" layer put them there implicitly?
|
|
1483
1536
|
// OR, if we are local-only, we just get them.
|
|
1484
1537
|
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
@@ -1506,7 +1559,7 @@ export class SyncSagaCoordinator {
|
|
|
1506
1559
|
// Get the requested ibGib
|
|
1507
1560
|
let ibGib = srcGraph[addr];
|
|
1508
1561
|
if (!ibGib) {
|
|
1509
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1562
|
+
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1510
1563
|
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1511
1564
|
ibGib = res.ibGibs[0];
|
|
1512
1565
|
}
|
|
@@ -1602,7 +1655,7 @@ export class SyncSagaCoordinator {
|
|
|
1602
1655
|
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1603
1656
|
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1604
1657
|
// We have the tip!
|
|
1605
|
-
// Do we have the full history?
|
|
1658
|
+
// Do we have the full history?
|
|
1606
1659
|
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1607
1660
|
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1608
1661
|
|
|
@@ -1648,9 +1701,9 @@ export class SyncSagaCoordinator {
|
|
|
1648
1701
|
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1649
1702
|
requests: hasMyRequests ? myRequests : undefined,
|
|
1650
1703
|
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1651
|
-
// Wait. If we send data, we are NOT committing yet.
|
|
1704
|
+
// Wait. If we send data, we are NOT committing yet.
|
|
1652
1705
|
// We are sending data. The OTHER side must ingest it.
|
|
1653
|
-
// So proposeCommit = true?
|
|
1706
|
+
// So proposeCommit = true?
|
|
1654
1707
|
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1655
1708
|
// Yes.
|
|
1656
1709
|
};
|
|
@@ -1680,7 +1733,8 @@ export class SyncSagaCoordinator {
|
|
|
1680
1733
|
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1681
1734
|
|
|
1682
1735
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1683
|
-
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1736
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1737
|
+
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1684
1738
|
|
|
1685
1739
|
} else {
|
|
1686
1740
|
// We have nothing to send.
|
|
@@ -1712,7 +1766,8 @@ export class SyncSagaCoordinator {
|
|
|
1712
1766
|
if (identity) { commitCtrlPayloads.push(identity); }
|
|
1713
1767
|
|
|
1714
1768
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1715
|
-
return { frame: commitFrame, };
|
|
1769
|
+
// return { frame: commitFrame, };
|
|
1770
|
+
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1716
1771
|
|
|
1717
1772
|
} else {
|
|
1718
1773
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1770,7 +1825,8 @@ export class SyncSagaCoordinator {
|
|
|
1770
1825
|
if (identity) { commitCtrlPayloads2.push(identity); }
|
|
1771
1826
|
|
|
1772
1827
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1773
|
-
return { frame: commitFrame, };
|
|
1828
|
+
// return { frame: commitFrame, };
|
|
1829
|
+
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1774
1830
|
}
|
|
1775
1831
|
|
|
1776
1832
|
// Build control payloads for delta propose
|
|
@@ -1778,7 +1834,8 @@ export class SyncSagaCoordinator {
|
|
|
1778
1834
|
if (identity) { deltaCtrlPayloads.push(identity); }
|
|
1779
1835
|
|
|
1780
1836
|
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1781
|
-
return { frame: deltaFrame, };
|
|
1837
|
+
// return { frame: deltaFrame, };
|
|
1838
|
+
throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
|
|
1782
1839
|
}
|
|
1783
1840
|
}
|
|
1784
1841
|
}
|
|
@@ -1796,7 +1853,7 @@ export class SyncSagaCoordinator {
|
|
|
1796
1853
|
tempSpace: IbGibSpaceAny,
|
|
1797
1854
|
metaspace: MetaspaceService,
|
|
1798
1855
|
identity?: KeystoneIbGib_V1,
|
|
1799
|
-
}): Promise<
|
|
1856
|
+
}): Promise<NextSagaFrameInfo> {
|
|
1800
1857
|
const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
|
|
1801
1858
|
if (logalot) { console.log(`${lc} Commit received.`); }
|
|
1802
1859
|
|
|
@@ -1811,7 +1868,8 @@ export class SyncSagaCoordinator {
|
|
|
1811
1868
|
// Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
|
|
1812
1869
|
|
|
1813
1870
|
if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
|
|
1814
|
-
return
|
|
1871
|
+
// return { responseWasNull: true };
|
|
1872
|
+
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1815
1873
|
}
|
|
1816
1874
|
|
|
1817
1875
|
// #endregion Handlers
|
|
@@ -1909,7 +1967,7 @@ export class SyncSagaCoordinator {
|
|
|
1909
1967
|
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1910
1968
|
}
|
|
1911
1969
|
|
|
1912
|
-
// remove the existing sync msg stones' addrs
|
|
1970
|
+
// remove the existing sync msg stones' addrs
|
|
1913
1971
|
if (!prevSagaIbGib.rel8ns) { throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`); }
|
|
1914
1972
|
if (!prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] || prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME].length === 0) {
|
|
1915
1973
|
throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] falsy/empty? (E: 15156baad26fcccda80aa3a31718c726)`);
|