@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
|
@@ -5,13 +5,13 @@ import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
|
|
|
5
5
|
import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space-helper.mjs";
|
|
6
6
|
import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
|
|
7
7
|
import { appendToTimeline, createTimeline } from "../timeline/timeline-api.mjs";
|
|
8
|
-
import { SyncConflictStrategy, SyncMode, } from "./sync-types.mjs";
|
|
8
|
+
import { SyncConflictStrategy, SyncMode, SYNC_CONFLICT_STRATEGY_VALID_VALUES, } from "./sync-types.mjs";
|
|
9
9
|
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
10
|
-
import { getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
10
|
+
import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
11
11
|
import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
12
12
|
import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
|
|
13
13
|
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
|
|
14
|
-
import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
14
|
+
import { createSyncSagaContext, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
15
15
|
import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
|
|
16
16
|
import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
|
|
17
17
|
import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
@@ -95,30 +95,29 @@ export class SyncSagaCoordinator {
|
|
|
95
95
|
// Async execution wrapper
|
|
96
96
|
(async () => {
|
|
97
97
|
try {
|
|
98
|
-
//
|
|
98
|
+
// BOOTSTRAP IDENTITY (Session Keystone)
|
|
99
99
|
const sessionIdentity = useSessionIdentity
|
|
100
100
|
? await this.getSessionIdentity({ sagaId, metaspace, tempSpace })
|
|
101
101
|
: undefined;
|
|
102
102
|
// if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
|
|
103
|
-
//
|
|
104
|
-
const {
|
|
103
|
+
// CREATE INITIAL FRAME (Stage.init)
|
|
104
|
+
const { initFrame, initDomainGraph } = await this.createInitFrame({
|
|
105
105
|
sagaId,
|
|
106
106
|
sessionIdentity,
|
|
107
107
|
domainIbGibs,
|
|
108
108
|
conflictStrategy,
|
|
109
109
|
metaspace, localSpace, tempSpace,
|
|
110
110
|
});
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// });
|
|
111
|
+
// KICK OFF THE PING-PONG SAGA LOOP (FSM)
|
|
112
|
+
await this.executeSagaLoop({
|
|
113
|
+
initFrame, initDomainGraph,
|
|
114
|
+
peer,
|
|
115
|
+
sessionIdentity,
|
|
116
|
+
updates$,
|
|
117
|
+
localSpace,
|
|
118
|
+
tempSpace,
|
|
119
|
+
metaspace
|
|
120
|
+
});
|
|
122
121
|
resolveDone();
|
|
123
122
|
if (!updates$.complete) {
|
|
124
123
|
throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`);
|
|
@@ -189,45 +188,23 @@ export class SyncSagaCoordinator {
|
|
|
189
188
|
* the NEXT request context.
|
|
190
189
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
191
190
|
*/
|
|
192
|
-
async executeSagaLoop({
|
|
191
|
+
async executeSagaLoop({ initFrame, initDomainGraph, peer, sessionIdentity, updates$, localSpace, tempSpace, metaspace }) {
|
|
193
192
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
194
|
-
|
|
195
|
-
let currentFrame =
|
|
196
|
-
|
|
193
|
+
/** The current frame we just generated (e.g., Init or Delta Request) */
|
|
194
|
+
let currentFrame = initFrame;
|
|
195
|
+
/** The **domain** payload we need to transmit (data we are sending) */
|
|
197
196
|
let nextDomainIbGibs = [];
|
|
198
|
-
// First, inject local/tempSpace into peer so it can pull as
|
|
199
|
-
// needed...code smell?
|
|
200
|
-
peer.senderSpace = localSpace;
|
|
201
|
-
peer.senderTempSpace = tempSpace;
|
|
202
197
|
while (currentFrame) {
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// between tip and LCA). This can be done using the information in
|
|
212
|
-
// the ack's conflicts array.
|
|
213
|
-
for (const nextDomainIbGib of nextDomainIbGibs) {
|
|
214
|
-
let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
|
|
215
|
-
if (!nextDomainIbGibGraph) {
|
|
216
|
-
nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
|
|
217
|
-
}
|
|
218
|
-
if (nextDomainIbGibGraph) {
|
|
219
|
-
nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (logalot) {
|
|
226
|
-
console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`);
|
|
227
|
-
}
|
|
228
|
-
// 2. Create Context (Envelope)
|
|
229
|
-
// set up subscription for response's payload ibgibs (if any)
|
|
198
|
+
// first, prepare for context transmission...
|
|
199
|
+
/**
|
|
200
|
+
* this is used later in pollForDomainPayloads **iif** payloads are
|
|
201
|
+
* received **in the response**, i.e., on the return trip. So if we
|
|
202
|
+
* request addrs, the response should have these addrs and their
|
|
203
|
+
* dependencies. The ibgibs corresponding to these addrs will be
|
|
204
|
+
* streamed and placed into this map.
|
|
205
|
+
*/
|
|
230
206
|
const domainPayloadsMap = new Map();
|
|
207
|
+
// #region set up peer observable for any domainPayloadsMap
|
|
231
208
|
const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
|
|
232
209
|
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
233
210
|
next: async (ibgib) => {
|
|
@@ -256,38 +233,45 @@ export class SyncSagaCoordinator {
|
|
|
256
233
|
await subscription.unsubscribe();
|
|
257
234
|
},
|
|
258
235
|
}));
|
|
259
|
-
//
|
|
236
|
+
// #endregion set up peer observable for any domainPayloadsMap
|
|
237
|
+
// ...create/compose the Request Context itself...
|
|
260
238
|
const requestCtx = await createSyncSagaContext({
|
|
261
239
|
sagaFrame: currentFrame,
|
|
262
240
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
263
241
|
/**
|
|
264
242
|
* init frame: empty
|
|
265
|
-
*
|
|
243
|
+
* ack frame: possible push offers
|
|
244
|
+
* delta frame: requested ibgibs or commit offer
|
|
245
|
+
* commit frame: empty
|
|
266
246
|
*/
|
|
267
|
-
payloadIbGibsDomain:
|
|
247
|
+
payloadIbGibsDomain: nextDomainIbGibs,
|
|
268
248
|
localSpace,
|
|
269
249
|
});
|
|
270
|
-
// Log what we're sending
|
|
250
|
+
// #region Log what we're sending
|
|
271
251
|
if (logalotControlDomain) {
|
|
272
|
-
const domainAddrs =
|
|
273
|
-
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I:
|
|
252
|
+
const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
253
|
+
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
|
|
274
254
|
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
275
255
|
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
276
256
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
277
257
|
}
|
|
258
|
+
// #endregion Log what we're sending
|
|
278
259
|
// update our saga listeners...
|
|
279
260
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
280
|
-
updates$.next(requestCtx); // spins off
|
|
281
|
-
// ...And send the context
|
|
261
|
+
updates$.next(requestCtx); // spins off for saga UI updates
|
|
262
|
+
// ...And send the context.
|
|
263
|
+
peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
|
|
282
264
|
const responseCtx = await peer.witness(requestCtx);
|
|
283
|
-
//
|
|
265
|
+
// the send returned, but a peer can return a falsy responseCtx, if
|
|
266
|
+
// we just sent a commit to them and they're done. If so, there will
|
|
267
|
+
// necessarily be no payload to wait to receive
|
|
284
268
|
if (!responseCtx) {
|
|
285
269
|
if (currentFrame) {
|
|
286
270
|
// Check for Commit (Peer silence expected)
|
|
287
271
|
const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
|
|
288
272
|
if (msg?.data?.stage === SyncStage.commit) {
|
|
289
273
|
if (logalot) {
|
|
290
|
-
console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete
|
|
274
|
+
console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`);
|
|
291
275
|
}
|
|
292
276
|
currentFrame = null;
|
|
293
277
|
break;
|
|
@@ -300,19 +284,19 @@ export class SyncSagaCoordinator {
|
|
|
300
284
|
throw new Error(`(UNEXPECTED) no response and currentFrame falsy? (E: 8d1085ea2f28cfc3f9c922649864a826)`);
|
|
301
285
|
}
|
|
302
286
|
}
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
// ---------------------------------------------------------------------
|
|
306
|
-
// at this point, we have received the response context ibgib, but
|
|
287
|
+
// at this point, we did indeed receive a response to analyze. BUT!
|
|
288
|
+
// we have only necessarily received the response context ibgib.
|
|
307
289
|
// if there were payloads expected to be transferred, they may not
|
|
308
290
|
// be done yet.
|
|
309
291
|
if (!responseCtx.data) {
|
|
310
292
|
throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
|
|
311
293
|
}
|
|
312
|
-
updates$.next(responseCtx); // spins off
|
|
294
|
+
updates$.next(responseCtx); // spins off for saga UI updating
|
|
295
|
+
// validate context
|
|
296
|
+
await validateContextAndSagaFrame;
|
|
313
297
|
// Extract expected domain addresses from response context
|
|
314
298
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
|
|
315
|
-
// Poll for them if needed
|
|
299
|
+
// Poll for them if needed. see above jsdocs for domainPayloadsMap
|
|
316
300
|
if (responsePayloadAddrsDomain.length > 0) {
|
|
317
301
|
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
318
302
|
expectedAddrs: responsePayloadAddrsDomain,
|
|
@@ -321,49 +305,61 @@ export class SyncSagaCoordinator {
|
|
|
321
305
|
tempSpace,
|
|
322
306
|
});
|
|
323
307
|
}
|
|
324
|
-
//
|
|
325
|
-
// domain payloads (if applicable), so we are ready to do the next
|
|
326
|
-
// iteration in the saga loop
|
|
327
|
-
// Log what we received back
|
|
308
|
+
// #region Log what we received back
|
|
328
309
|
if (!responseCtx.sagaFrame) {
|
|
329
310
|
throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`);
|
|
330
311
|
}
|
|
331
312
|
if (logalotControlDomain) {
|
|
332
313
|
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] || [];
|
|
333
|
-
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I:
|
|
314
|
+
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
|
|
334
315
|
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
335
316
|
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
336
317
|
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
337
318
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
338
319
|
}
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
|
|
320
|
+
// #endregion Log what we received back
|
|
321
|
+
// at this point, we have received the context AND **all** of the
|
|
322
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
323
|
+
// iteration in the saga loop
|
|
324
|
+
// this is the part that drives the FSM forward, i.e., when we
|
|
325
|
+
// evolve the sync saga ibgib itself to the next frame if we aren't
|
|
326
|
+
// finished/errored out.
|
|
327
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
346
328
|
sagaContext: responseCtx,
|
|
347
329
|
mySpace: localSpace,
|
|
348
330
|
myTempSpace: tempSpace,
|
|
349
331
|
metaspace,
|
|
350
332
|
});
|
|
351
|
-
if (!
|
|
333
|
+
if (!contextResult) {
|
|
352
334
|
if (logalot) {
|
|
353
|
-
console.log(`${lc} Handler returned null (Saga End)
|
|
335
|
+
console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`);
|
|
354
336
|
}
|
|
355
337
|
break;
|
|
356
338
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
339
|
+
// #region error conditions throw
|
|
340
|
+
if (contextResult.errorMsg) {
|
|
341
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
|
|
342
|
+
}
|
|
343
|
+
else if (!contextResult.nextFrameInfo) {
|
|
344
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
345
|
+
}
|
|
346
|
+
else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
347
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
348
|
+
}
|
|
349
|
+
// #endregion error conditions throw
|
|
350
|
+
// we have another frame to process and send out, with possibly
|
|
351
|
+
// payload domain ibgibs as well
|
|
352
|
+
const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
|
|
353
|
+
currentFrame = frame;
|
|
354
|
+
nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
|
|
355
|
+
// #region Log handler output for next iteration
|
|
361
356
|
if (logalotControlDomain) {
|
|
362
357
|
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
363
|
-
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I:
|
|
358
|
+
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
|
|
364
359
|
console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
365
360
|
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
366
361
|
}
|
|
362
|
+
// #endregion Log handler output for next iteration
|
|
367
363
|
}
|
|
368
364
|
}
|
|
369
365
|
/**
|
|
@@ -423,7 +419,7 @@ export class SyncSagaCoordinator {
|
|
|
423
419
|
}
|
|
424
420
|
const res = await getLatestAddrs({ space, tjpAddrs: tjps });
|
|
425
421
|
if (!res.data || !res.data.latestAddrsMap) {
|
|
426
|
-
throw new Error(`${lc} Failed to get latest addrs. (E:
|
|
422
|
+
throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
|
|
427
423
|
}
|
|
428
424
|
// if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
|
|
429
425
|
return res.data.latestAddrsMap;
|
|
@@ -477,7 +473,7 @@ export class SyncSagaCoordinator {
|
|
|
477
473
|
}
|
|
478
474
|
// Analyze Timelines & Stones
|
|
479
475
|
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
480
|
-
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
476
|
+
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
481
477
|
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
482
478
|
// we need to store the fullGraph in our tempSpace for later...
|
|
483
479
|
await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
|
|
@@ -516,7 +512,7 @@ export class SyncSagaCoordinator {
|
|
|
516
512
|
localSpace,
|
|
517
513
|
});
|
|
518
514
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
519
|
-
return { sagaFrame,
|
|
515
|
+
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
520
516
|
}
|
|
521
517
|
catch (error) {
|
|
522
518
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -606,8 +602,8 @@ export class SyncSagaCoordinator {
|
|
|
606
602
|
*
|
|
607
603
|
* This is a one-off on the receiver.
|
|
608
604
|
*/
|
|
609
|
-
async
|
|
610
|
-
const lc = `${this.lc}[${this.
|
|
605
|
+
async handleResponseSagaContext({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
606
|
+
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
611
607
|
try {
|
|
612
608
|
if (logalot) {
|
|
613
609
|
console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`);
|
|
@@ -628,9 +624,10 @@ export class SyncSagaCoordinator {
|
|
|
628
624
|
* don't like this name, need to refactor
|
|
629
625
|
*/
|
|
630
626
|
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
627
|
+
let nextFrameInfo;
|
|
631
628
|
switch (stage) {
|
|
632
629
|
case SyncStage.init:
|
|
633
|
-
|
|
630
|
+
nextFrameInfo = await this.handleInitFrame({
|
|
634
631
|
sagaIbGib,
|
|
635
632
|
messageData: messageData,
|
|
636
633
|
metaspace,
|
|
@@ -639,19 +636,25 @@ export class SyncSagaCoordinator {
|
|
|
639
636
|
identity,
|
|
640
637
|
identitySecret
|
|
641
638
|
});
|
|
639
|
+
break;
|
|
642
640
|
case SyncStage.ack:
|
|
643
|
-
|
|
641
|
+
nextFrameInfo = await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
|
|
642
|
+
break;
|
|
644
643
|
case SyncStage.delta:
|
|
645
|
-
|
|
644
|
+
nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
645
|
+
break;
|
|
646
646
|
case SyncStage.commit:
|
|
647
|
-
|
|
647
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
648
|
+
break;
|
|
648
649
|
default:
|
|
649
650
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
650
651
|
}
|
|
652
|
+
return { errorMsg: undefined, nextFrameInfo, };
|
|
651
653
|
}
|
|
652
654
|
catch (error) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
+
const errorMsg = `${lc} ${extractErrorMsg(error)}`;
|
|
656
|
+
console.error(errorMsg);
|
|
657
|
+
return { errorMsg };
|
|
655
658
|
}
|
|
656
659
|
finally {
|
|
657
660
|
if (logalot) {
|
|
@@ -680,14 +683,11 @@ export class SyncSagaCoordinator {
|
|
|
680
683
|
if (logalot) {
|
|
681
684
|
console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`);
|
|
682
685
|
}
|
|
683
|
-
console.log(`${lc} [TEST DEBUG]
|
|
684
|
-
if (logalot) {
|
|
685
|
-
console.log(`${lc} starting...`);
|
|
686
|
-
}
|
|
686
|
+
console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
|
|
687
687
|
// Extract Init Data
|
|
688
688
|
const initData = messageData; // Using renamed variable for clarity
|
|
689
689
|
if (initData.stage !== SyncStage.init) {
|
|
690
|
-
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E:
|
|
690
|
+
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
|
|
691
691
|
}
|
|
692
692
|
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
693
693
|
if (!initData || !initData.knowledgeVector) {
|
|
@@ -697,8 +697,8 @@ export class SyncSagaCoordinator {
|
|
|
697
697
|
const conflictStrategy = sagaIbGib.data.conflictStrategy || SyncConflictStrategy.abort;
|
|
698
698
|
// 2. Gap Analysis
|
|
699
699
|
const conflicts = [];
|
|
700
|
-
const
|
|
701
|
-
const
|
|
700
|
+
const deltaRequestAddrInfos = [];
|
|
701
|
+
const pushOfferInfos = [];
|
|
702
702
|
// First do the consant stones (Non-TJPs)
|
|
703
703
|
const stones = initData.stones || [];
|
|
704
704
|
if (stones.length > 0) {
|
|
@@ -717,14 +717,14 @@ export class SyncSagaCoordinator {
|
|
|
717
717
|
console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`);
|
|
718
718
|
}
|
|
719
719
|
addrsNotFound.forEach(addr => {
|
|
720
|
-
if (!
|
|
721
|
-
|
|
720
|
+
if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
|
|
721
|
+
deltaRequestAddrInfos.push({ addr }); // no tjpAddr
|
|
722
722
|
}
|
|
723
723
|
});
|
|
724
724
|
}
|
|
725
725
|
}
|
|
726
726
|
/**
|
|
727
|
-
* "remote"
|
|
727
|
+
* "remote" is sender in this case
|
|
728
728
|
*/
|
|
729
729
|
const remoteKV = initData.knowledgeVector;
|
|
730
730
|
if (logalot) {
|
|
@@ -736,7 +736,7 @@ export class SyncSagaCoordinator {
|
|
|
736
736
|
console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`);
|
|
737
737
|
}
|
|
738
738
|
// 1. Get Local Latest Addrs for all TJPs
|
|
739
|
-
let
|
|
739
|
+
let localLatestAddrsMap = {};
|
|
740
740
|
if (remoteTjps.length > 0) {
|
|
741
741
|
// Batch get latest addrs for the TJPs
|
|
742
742
|
const resGetLatestAddrs = await getLatestAddrs({
|
|
@@ -749,52 +749,88 @@ export class SyncSagaCoordinator {
|
|
|
749
749
|
if (!resGetLatestAddrs.data.latestAddrsMap) {
|
|
750
750
|
throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`);
|
|
751
751
|
}
|
|
752
|
-
|
|
753
|
-
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(
|
|
752
|
+
localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
|
|
753
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
|
|
754
754
|
if (logalot) {
|
|
755
|
-
console.log(`${lc} localKV: ${pretty(
|
|
755
|
+
console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`);
|
|
756
756
|
}
|
|
757
757
|
}
|
|
758
758
|
// 2. Gap Analysis
|
|
759
759
|
for (const tjp of remoteTjps) {
|
|
760
760
|
const remoteAddr = remoteKV[tjp];
|
|
761
|
-
const localAddr =
|
|
761
|
+
const localAddr = localLatestAddrsMap[tjp];
|
|
762
762
|
if (!localAddr) {
|
|
763
763
|
// We (Receiver) don't have this timeline at all. Request it.
|
|
764
764
|
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
765
|
-
|
|
765
|
+
deltaRequestAddrInfos.push({
|
|
766
|
+
addr: remoteAddr,
|
|
767
|
+
tjpAddr: tjp,
|
|
768
|
+
// we don't have this timeline at all
|
|
769
|
+
// latestAddrAlreadyHave: undefined
|
|
770
|
+
});
|
|
766
771
|
continue;
|
|
767
772
|
}
|
|
773
|
+
// we do have this timeline...
|
|
768
774
|
if (localAddr === remoteAddr) {
|
|
769
|
-
//
|
|
775
|
+
// ...already synced
|
|
770
776
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
771
777
|
continue;
|
|
772
778
|
}
|
|
773
779
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
774
|
-
//
|
|
775
|
-
//
|
|
776
|
-
|
|
780
|
+
// we have this timeline but it's not synced...
|
|
781
|
+
// We're executing on receiver. Check if Remote (Sender) is in
|
|
782
|
+
// our past, and if so, we are ahead and need to push the delta
|
|
783
|
+
// to remote
|
|
784
|
+
const remoteIsInPast = await isPastFrame({
|
|
777
785
|
olderAddr: remoteAddr,
|
|
778
786
|
newerAddr: localAddr,
|
|
779
787
|
space: mySpace,
|
|
780
788
|
});
|
|
781
|
-
if (
|
|
782
|
-
|
|
783
|
-
|
|
789
|
+
if (remoteIsInPast) {
|
|
790
|
+
// we're ahead, so push the delta of what the sender doesn't
|
|
791
|
+
// have (we have full knowledge)
|
|
792
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
|
|
793
|
+
const deltaGraph = await getDeltaDependencyGraph({
|
|
794
|
+
ibGibAddr: localAddr,
|
|
795
|
+
live: false, // always live: false right?
|
|
796
|
+
latestCommonFrameAddr: remoteAddr,
|
|
797
|
+
space: mySpace,
|
|
798
|
+
});
|
|
799
|
+
pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
|
|
784
800
|
}
|
|
785
801
|
else {
|
|
786
|
-
// Remote is not in our past.
|
|
787
|
-
//
|
|
802
|
+
// Remote tip is not in our past. So, either Remote is
|
|
803
|
+
// ahead (we're in THEIR past, i.e., Fast-Backward) OR
|
|
804
|
+
// Diverged/conflict (we've both made edits).
|
|
788
805
|
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
806
|
+
/**
|
|
807
|
+
* we could first check for existence of remoteAddr in
|
|
808
|
+
* mySpace, but this is quick and easy. If it throws, we
|
|
809
|
+
* don't have it so it can't be in the past.
|
|
810
|
+
*/
|
|
811
|
+
let localIsInPast = false;
|
|
812
|
+
try {
|
|
813
|
+
// we could
|
|
814
|
+
localIsInPast = await isPastFrame({
|
|
815
|
+
olderAddr: localAddr,
|
|
816
|
+
newerAddr: remoteAddr,
|
|
817
|
+
space: mySpace,
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
catch (error) {
|
|
821
|
+
// couldn't get one of them, so localIsInPast remains false.
|
|
822
|
+
if (logalot) {
|
|
823
|
+
console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
if (localIsInPast) {
|
|
795
827
|
// Fast-Forward: We update to remote's tip.
|
|
796
828
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
797
|
-
|
|
829
|
+
deltaRequestAddrInfos.push({
|
|
830
|
+
addr: remoteAddr,
|
|
831
|
+
tjpAddr: tjp,
|
|
832
|
+
latestAddrAlreadyHave: localAddr,
|
|
833
|
+
});
|
|
798
834
|
}
|
|
799
835
|
else {
|
|
800
836
|
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
@@ -814,7 +850,7 @@ export class SyncSagaCoordinator {
|
|
|
814
850
|
});
|
|
815
851
|
}
|
|
816
852
|
else if (conflictStrategy === 'optimistic') {
|
|
817
|
-
// Optimistic: We want
|
|
853
|
+
// Optimistic: We want resolve this.
|
|
818
854
|
// We need to send our history to the Sender so they can Merge.
|
|
819
855
|
// Fetch Full History for Local Timeline
|
|
820
856
|
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
@@ -825,11 +861,11 @@ export class SyncSagaCoordinator {
|
|
|
825
861
|
// We have localAddr.
|
|
826
862
|
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
827
863
|
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
828
|
-
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E:
|
|
864
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
829
865
|
}
|
|
830
866
|
const localTip = resLocalTip.ibGibs[0];
|
|
831
867
|
if (!localTip) {
|
|
832
|
-
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E:
|
|
868
|
+
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`);
|
|
833
869
|
}
|
|
834
870
|
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
835
871
|
conflicts.push({
|
|
@@ -842,7 +878,7 @@ export class SyncSagaCoordinator {
|
|
|
842
878
|
});
|
|
843
879
|
}
|
|
844
880
|
else {
|
|
845
|
-
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E:
|
|
881
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
|
|
846
882
|
}
|
|
847
883
|
}
|
|
848
884
|
}
|
|
@@ -856,51 +892,17 @@ export class SyncSagaCoordinator {
|
|
|
856
892
|
}
|
|
857
893
|
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
858
894
|
}
|
|
859
|
-
// 3. Build Knowledge Vector (Full History for known timelines)
|
|
860
|
-
// [NEW] Smart Diff
|
|
861
|
-
// We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
|
|
862
|
-
// Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
|
|
863
|
-
/**
|
|
864
|
-
* we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
|
|
865
|
-
*/
|
|
866
|
-
const knowledgeVector = {};
|
|
867
|
-
// [Smart Diff] Populate knowledge from timelines identified by Sender
|
|
868
|
-
for (const tjp of remoteTjps) {
|
|
869
|
-
const localAddr = localKV[tjp];
|
|
870
|
-
if (localAddr) {
|
|
871
|
-
const res = await getFromSpace({ addr: localAddr, space: mySpace });
|
|
872
|
-
if (res.success && res.ibGibs?.[0]) {
|
|
873
|
-
const ibGib = res.ibGibs[0];
|
|
874
|
-
const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
|
|
875
|
-
if (!knowledgeVector[realTjp]) {
|
|
876
|
-
const past = ibGib.rel8ns?.past || [];
|
|
877
|
-
knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
// Also populate from `knowledgeVector` in Init if we want bidirectional?
|
|
883
|
-
// No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
|
|
884
|
-
// Let's assume standard flow:
|
|
885
|
-
// 1. Sender says "I have X"
|
|
886
|
-
// 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
|
|
887
|
-
// Problem: Receiver doesn't know X is related to Y without X.
|
|
888
|
-
// SOLUTION:
|
|
889
|
-
// The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
|
|
890
|
-
// `SyncSagaMessageInitData_V1` extends `SyncInitData`.
|
|
891
|
-
// `SyncInitData` has `knowledgeVector`.
|
|
892
|
-
// If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
|
|
893
|
-
// Let's implement Sender populating `Init.knowledgeVector`.
|
|
894
|
-
// But `SyncSagaCoordinator.startSaga` creates Init.
|
|
895
895
|
// 3. Create Ack Frame
|
|
896
|
+
if (!sagaIbGib.data) {
|
|
897
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`);
|
|
898
|
+
}
|
|
896
899
|
const sagaId = sagaIbGib.data.uuid;
|
|
897
900
|
// Create Payload Stone
|
|
898
901
|
const ackData = {
|
|
899
902
|
sagaId,
|
|
900
903
|
stage: SyncStage.ack,
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
knowledgeVector,
|
|
904
|
+
deltaRequestAddrInfos,
|
|
905
|
+
pushOfferInfos,
|
|
904
906
|
};
|
|
905
907
|
if (conflicts?.length > 0) {
|
|
906
908
|
ackData.conflicts = conflicts;
|
|
@@ -927,13 +929,68 @@ export class SyncSagaCoordinator {
|
|
|
927
929
|
* offers. an ack frame's payloads, if any, are those push offers
|
|
928
930
|
*/
|
|
929
931
|
// let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
932
|
+
let payloadIbGibsDomain;
|
|
933
|
+
if (pushOfferInfos.length > 0) {
|
|
934
|
+
const searchSecondSpaceAddrs = [];
|
|
935
|
+
payloadIbGibsDomain = [];
|
|
936
|
+
const domainAddrs = pushOfferInfos.flatMap(x => x.addrs);
|
|
937
|
+
const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
|
|
938
|
+
const resGetRawData = resGet.rawResultIbGib?.data;
|
|
939
|
+
if (!resGetRawData) {
|
|
940
|
+
throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`);
|
|
941
|
+
}
|
|
942
|
+
const addrsNotFound = resGetRawData.addrsNotFound ?? [];
|
|
943
|
+
const addrsErrored = resGetRawData.addrsErrored ?? [];
|
|
944
|
+
if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
|
|
945
|
+
// found all of them
|
|
946
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain.push(x); });
|
|
947
|
+
}
|
|
948
|
+
else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
|
|
949
|
+
// found some of them
|
|
950
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain.push(x); });
|
|
951
|
+
const foundPlusNotFoundCount = payloadIbGibsDomain.length +
|
|
952
|
+
addrsNotFound.length +
|
|
953
|
+
addrsErrored.length;
|
|
954
|
+
if (foundPlusNotFoundCount === domainAddrs.length) {
|
|
955
|
+
// we can still conceivably get the remaining addrs
|
|
956
|
+
addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
957
|
+
addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
958
|
+
}
|
|
959
|
+
else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
|
|
960
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
// found none of them(?)
|
|
968
|
+
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)`);
|
|
969
|
+
domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
970
|
+
}
|
|
971
|
+
if (searchSecondSpaceAddrs.length > 0) {
|
|
972
|
+
if (logalot) {
|
|
973
|
+
console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`);
|
|
974
|
+
}
|
|
975
|
+
const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
|
|
976
|
+
if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
|
|
977
|
+
// got them all the second try
|
|
978
|
+
resGet2.ibGibs.forEach(x => payloadIbGibsDomain.push(x));
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
resGet2.ibGibs?.forEach(x => payloadIbGibsDomain.push(x));
|
|
982
|
+
const addrsFailed = domainAddrs.filter(x => !payloadIbGibsDomain.map(pid => getIbGibAddr({ ibGib: pid })).includes(x));
|
|
983
|
+
throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
// we have now populated payloadIbGibsDomain
|
|
987
|
+
}
|
|
988
|
+
throw new Error(`not implemented (E: ed3f98abb0988c5ae8038bb8d741fb26)`);
|
|
989
|
+
// return {
|
|
990
|
+
// frame: ackFrame,
|
|
991
|
+
// // conflictInfos,
|
|
992
|
+
// payloadIbGibsDomain,
|
|
993
|
+
// };
|
|
937
994
|
}
|
|
938
995
|
catch (error) {
|
|
939
996
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -985,7 +1042,7 @@ export class SyncSagaCoordinator {
|
|
|
985
1042
|
console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
|
|
986
1043
|
// Terminal failure. Sender should probably Commit(Fail) or just Abort.
|
|
987
1044
|
// For now, throw to trigger abort.
|
|
988
|
-
throw new Error(`${lc} Peer reported terminal conflicts. (E:
|
|
1045
|
+
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
989
1046
|
}
|
|
990
1047
|
const optimisticConflicts = conflicts.filter(c => !c.terminal);
|
|
991
1048
|
const mergeDeltaReqs = []; // Additional requests for merging
|
|
@@ -1007,7 +1064,7 @@ export class SyncSagaCoordinator {
|
|
|
1007
1064
|
// Or can we send a 'Delta Request' frame?
|
|
1008
1065
|
// Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
|
|
1009
1066
|
// If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
|
|
1010
|
-
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1067
|
+
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1011
1068
|
// AND we piggyback our own requests?
|
|
1012
1069
|
// OR: We treat the Conflict Resolution as a sub-saga or side-effect?
|
|
1013
1070
|
// SIMPLIFICATION for V1:
|
|
@@ -1031,7 +1088,7 @@ export class SyncSagaCoordinator {
|
|
|
1031
1088
|
const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
|
|
1032
1089
|
const senderTipIbGib = resSenderTip.ibGibs?.[0];
|
|
1033
1090
|
if (!senderTipIbGib) {
|
|
1034
|
-
throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E:
|
|
1091
|
+
throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`);
|
|
1035
1092
|
}
|
|
1036
1093
|
// Basic Diff: Find what Receiver has that we don't.
|
|
1037
1094
|
// Actually, we need to traverse OUR past to find commonality.
|
|
@@ -1108,46 +1165,46 @@ export class SyncSagaCoordinator {
|
|
|
1108
1165
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1109
1166
|
}
|
|
1110
1167
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1111
|
-
const deltaReqAddrs = ackData.
|
|
1112
|
-
const pushOfferAddrs = ackData.
|
|
1168
|
+
const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
|
|
1169
|
+
const pushOfferAddrs = ackData.pushOfferInfos || [];
|
|
1113
1170
|
// 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
|
|
1114
1171
|
const pullReqAddrs = [];
|
|
1115
1172
|
for (const addr of pushOfferAddrs) {
|
|
1116
|
-
const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1117
|
-
if (!existing) {
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1173
|
+
// const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1174
|
+
// if (!existing) {
|
|
1175
|
+
// pullReqAddrs.push(addr);
|
|
1176
|
+
// }
|
|
1120
1177
|
}
|
|
1121
1178
|
// 2. Process Delta Requests (Push Payload)
|
|
1122
1179
|
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1180
|
+
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1123
1181
|
const skipAddrs = new Set();
|
|
1124
1182
|
if (ackData.knowledgeVector) {
|
|
1125
1183
|
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1126
|
-
addrs.forEach(a => skipAddrs.add(a));
|
|
1184
|
+
// addrs.forEach(a => skipAddrs.add(a));
|
|
1127
1185
|
});
|
|
1128
1186
|
}
|
|
1129
1187
|
const payloadIbGibs = [];
|
|
1130
1188
|
// Gather all tips to sync first
|
|
1131
1189
|
const tipsToSync = [];
|
|
1132
1190
|
for (const addr of deltaReqAddrs) {
|
|
1133
|
-
let ibGib = srcGraph[addr];
|
|
1134
|
-
if (!ibGib) {
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1140
|
-
if (ibGib) {
|
|
1141
|
-
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1191
|
+
// let ibGib = srcGraph[addr];
|
|
1192
|
+
// if (!ibGib) {
|
|
1193
|
+
// const res = await getFromSpace({ addr, space: destSpace });
|
|
1194
|
+
// if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1195
|
+
// ibGib = res.ibGibs[0];
|
|
1196
|
+
// }
|
|
1197
|
+
// }
|
|
1198
|
+
// if (ibGib) {
|
|
1199
|
+
// tipsToSync.push(ibGib);
|
|
1200
|
+
// } else {
|
|
1201
|
+
// throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
|
|
1202
|
+
// }
|
|
1146
1203
|
}
|
|
1147
1204
|
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1148
1205
|
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1149
1206
|
// `getDependencyGraph` takes a single ibGib.
|
|
1150
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1207
|
+
// We can optimize by doing it for each tip and unioning the result?
|
|
1151
1208
|
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1152
1209
|
// We will loop.
|
|
1153
1210
|
const allDepsSet = new Set();
|
|
@@ -1219,7 +1276,8 @@ export class SyncSagaCoordinator {
|
|
|
1219
1276
|
payloadIbGibsControl.push(identity);
|
|
1220
1277
|
}
|
|
1221
1278
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1222
|
-
return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1279
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1280
|
+
throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
|
|
1223
1281
|
}
|
|
1224
1282
|
catch (error) {
|
|
1225
1283
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1255,7 +1313,7 @@ export class SyncSagaCoordinator {
|
|
|
1255
1313
|
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1256
1314
|
}
|
|
1257
1315
|
if (logalot) {
|
|
1258
|
-
console.log(`${lc} deltaData: ${pretty(deltaData)} (I:
|
|
1316
|
+
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1259
1317
|
}
|
|
1260
1318
|
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1261
1319
|
const payloadAddrs = deltaData.payloadAddrs || [];
|
|
@@ -1264,8 +1322,8 @@ export class SyncSagaCoordinator {
|
|
|
1264
1322
|
// 1. Process Received Payload (Ingest)
|
|
1265
1323
|
const receivedPayloadIbGibs = [];
|
|
1266
1324
|
if (payloadAddrs.length > 0) {
|
|
1267
|
-
// We use `payloadAddrs` as the manifest.
|
|
1268
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1325
|
+
// We use `payloadAddrs` as the manifest.
|
|
1326
|
+
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1269
1327
|
// assuming the "Transport" layer put them there implicitly?
|
|
1270
1328
|
// OR, if we are local-only, we just get them.
|
|
1271
1329
|
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
@@ -1290,7 +1348,7 @@ export class SyncSagaCoordinator {
|
|
|
1290
1348
|
// Get the requested ibGib
|
|
1291
1349
|
let ibGib = srcGraph[addr];
|
|
1292
1350
|
if (!ibGib) {
|
|
1293
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1351
|
+
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1294
1352
|
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1295
1353
|
ibGib = res.ibGibs[0];
|
|
1296
1354
|
}
|
|
@@ -1376,7 +1434,7 @@ export class SyncSagaCoordinator {
|
|
|
1376
1434
|
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1377
1435
|
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1378
1436
|
// We have the tip!
|
|
1379
|
-
// Do we have the full history?
|
|
1437
|
+
// Do we have the full history?
|
|
1380
1438
|
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1381
1439
|
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1382
1440
|
// Perform Merge!
|
|
@@ -1420,9 +1478,9 @@ export class SyncSagaCoordinator {
|
|
|
1420
1478
|
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1421
1479
|
requests: hasMyRequests ? myRequests : undefined,
|
|
1422
1480
|
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1423
|
-
// Wait. If we send data, we are NOT committing yet.
|
|
1481
|
+
// Wait. If we send data, we are NOT committing yet.
|
|
1424
1482
|
// We are sending data. The OTHER side must ingest it.
|
|
1425
|
-
// So proposeCommit = true?
|
|
1483
|
+
// So proposeCommit = true?
|
|
1426
1484
|
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1427
1485
|
// Yes.
|
|
1428
1486
|
};
|
|
@@ -1448,7 +1506,8 @@ export class SyncSagaCoordinator {
|
|
|
1448
1506
|
payloadIbGibsControl.push(identity);
|
|
1449
1507
|
}
|
|
1450
1508
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1451
|
-
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1509
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1510
|
+
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1452
1511
|
}
|
|
1453
1512
|
else {
|
|
1454
1513
|
// We have nothing to send.
|
|
@@ -1477,7 +1536,8 @@ export class SyncSagaCoordinator {
|
|
|
1477
1536
|
commitCtrlPayloads.push(identity);
|
|
1478
1537
|
}
|
|
1479
1538
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1480
|
-
return { frame: commitFrame, };
|
|
1539
|
+
// return { frame: commitFrame, };
|
|
1540
|
+
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1481
1541
|
}
|
|
1482
1542
|
else {
|
|
1483
1543
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1531,7 +1591,8 @@ export class SyncSagaCoordinator {
|
|
|
1531
1591
|
commitCtrlPayloads2.push(identity);
|
|
1532
1592
|
}
|
|
1533
1593
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1534
|
-
return { frame: commitFrame, };
|
|
1594
|
+
// return { frame: commitFrame, };
|
|
1595
|
+
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1535
1596
|
}
|
|
1536
1597
|
// Build control payloads for delta propose
|
|
1537
1598
|
const deltaCtrlPayloads = [deltaFrame, deltaStone];
|
|
@@ -1539,7 +1600,8 @@ export class SyncSagaCoordinator {
|
|
|
1539
1600
|
deltaCtrlPayloads.push(identity);
|
|
1540
1601
|
}
|
|
1541
1602
|
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1542
|
-
return { frame: deltaFrame, };
|
|
1603
|
+
// return { frame: deltaFrame, };
|
|
1604
|
+
throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
|
|
1543
1605
|
}
|
|
1544
1606
|
}
|
|
1545
1607
|
}
|
|
@@ -1559,7 +1621,8 @@ export class SyncSagaCoordinator {
|
|
|
1559
1621
|
if (logalot) {
|
|
1560
1622
|
console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
|
|
1561
1623
|
}
|
|
1562
|
-
return
|
|
1624
|
+
// return { responseWasNull: true };
|
|
1625
|
+
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1563
1626
|
}
|
|
1564
1627
|
// #endregion Handlers
|
|
1565
1628
|
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|
|
@@ -1633,7 +1696,7 @@ export class SyncSagaCoordinator {
|
|
|
1633
1696
|
if (sessionIdentity) {
|
|
1634
1697
|
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1635
1698
|
}
|
|
1636
|
-
// remove the existing sync msg stones' addrs
|
|
1699
|
+
// remove the existing sync msg stones' addrs
|
|
1637
1700
|
if (!prevSagaIbGib.rel8ns) {
|
|
1638
1701
|
throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`);
|
|
1639
1702
|
}
|