@ibgib/core-gib 0.1.23 → 0.1.26
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/common/other/ibgib-helper.d.mts +1 -1
- package/dist/common/other/ibgib-helper.d.mts.map +1 -1
- package/dist/common/other/ibgib-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-constants.d.mts +1 -0
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +1 -0
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +5 -0
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +37 -1
- package/dist/sync/sync-helpers.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 -13
- 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 +176 -76
- 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 +38 -55
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +111 -244
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +32 -1
- 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 +96 -7
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +59 -13
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +446 -304
- 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 +35 -10
- 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/common/other/ibgib-helper.mts +1 -1
- package/src/sync/graft-info/graft-info-helpers.mts +3 -3
- package/src/sync/sync-conflict.respec.mts +10 -17
- package/src/sync/sync-constants.mts +1 -0
- package/src/sync/sync-helpers.mts +47 -7
- 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 +200 -75
- package/src/sync/sync-peer/sync-peer-types.mts +35 -11
- package/src/sync/sync-peer/sync-peer-v1.mts +154 -257
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +90 -13
- package/src/sync/sync-saga-coordinator.mts +536 -356
- 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,18 +84,18 @@ 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.
|
|
91
95
|
* @param opts.domainIbGibs - The root ibgibs defining the scope of the sync.
|
|
92
96
|
* @param opts.useSessionIdentity - (Optional) Whether to create an ephemeral session identity. Default: true.
|
|
93
97
|
*/
|
|
94
|
-
async sync({
|
|
98
|
+
public async sync({
|
|
95
99
|
peer,
|
|
96
100
|
domainIbGibs,
|
|
97
101
|
conflictStrategy = SyncConflictStrategy.abort,
|
|
@@ -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)`); }
|
|
@@ -188,6 +191,91 @@ export class SyncSagaCoordinator {
|
|
|
188
191
|
};
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
/**
|
|
195
|
+
* This is what the receiving side of the sync calls to drive the FSM to the
|
|
196
|
+
* next stage.
|
|
197
|
+
*
|
|
198
|
+
* So whereas the sender executes a saga loop and drives the entire process,
|
|
199
|
+
* this is a reactive one-off that drives just the single step that the
|
|
200
|
+
* receiver does in that saga.
|
|
201
|
+
*
|
|
202
|
+
* @returns next context result if another round, else if commit returns
|
|
203
|
+
* null
|
|
204
|
+
*/
|
|
205
|
+
public async receiverContinueSync({
|
|
206
|
+
sagaContext,
|
|
207
|
+
mySpace,
|
|
208
|
+
myTempSpace,
|
|
209
|
+
identity,
|
|
210
|
+
identitySecret,
|
|
211
|
+
metaspace,
|
|
212
|
+
}: {
|
|
213
|
+
sagaContext: SyncSagaContextIbGib_V1,
|
|
214
|
+
/**
|
|
215
|
+
* Local space relative to the execution context's POV
|
|
216
|
+
*/
|
|
217
|
+
mySpace: IbGibSpaceAny,
|
|
218
|
+
/**
|
|
219
|
+
* Local temp space relative to the execution context's POV
|
|
220
|
+
*/
|
|
221
|
+
myTempSpace: IbGibSpaceAny,
|
|
222
|
+
identity?: KeystoneIbGib_V1,
|
|
223
|
+
identitySecret?: string,
|
|
224
|
+
metaspace: MetaspaceService,
|
|
225
|
+
}): Promise<SyncSagaContextIbGib_V1 | null> {
|
|
226
|
+
const lc = `${this.lc}[${this.receiverContinueSync.name}]`;
|
|
227
|
+
try {
|
|
228
|
+
if (logalot) { console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`); }
|
|
229
|
+
|
|
230
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
231
|
+
sagaContext,
|
|
232
|
+
mySpace,
|
|
233
|
+
myTempSpace,
|
|
234
|
+
identity,
|
|
235
|
+
identitySecret,
|
|
236
|
+
metaspace,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!contextResult) {
|
|
240
|
+
if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`); }
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// #region error conditions throw
|
|
245
|
+
if (contextResult.errorMsg) {
|
|
246
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
|
|
247
|
+
} else if (!contextResult.nextFrameInfo) {
|
|
248
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
|
|
249
|
+
} else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
250
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
|
|
251
|
+
}
|
|
252
|
+
// #endregion error conditions throw
|
|
253
|
+
|
|
254
|
+
// create the return context
|
|
255
|
+
const { frame, payloadIbGibsDomain } = contextResult.nextFrameInfo;
|
|
256
|
+
|
|
257
|
+
const responseCtx = await createSyncSagaContext({
|
|
258
|
+
sagaFrame: frame,
|
|
259
|
+
localSpace: mySpace,
|
|
260
|
+
payloadIbGibsDomain,
|
|
261
|
+
// todo: we need to thoroughly go through the identity per each step after getting basic merging
|
|
262
|
+
sessionKeystones: identity ? [identity] : undefined, // ??
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const immediateValidationErrors = await validateContextAndSagaFrame({
|
|
266
|
+
context: responseCtx,
|
|
267
|
+
});
|
|
268
|
+
if (immediateValidationErrors.length > 0) { throw new Error(`(UNEXPECTED) just created sync saga context () and there were immediateValidationErrors? immediateValidationErrors: ${immediateValidationErrors} (E: c120e8e0aa98673d685267a8a36e5826)`); }
|
|
269
|
+
|
|
270
|
+
return responseCtx;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
273
|
+
throw error;
|
|
274
|
+
} finally {
|
|
275
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
191
279
|
protected async getSessionIdentity({
|
|
192
280
|
sagaId,
|
|
193
281
|
metaspace,
|
|
@@ -226,13 +314,13 @@ export class SyncSagaCoordinator {
|
|
|
226
314
|
|
|
227
315
|
/**
|
|
228
316
|
* Drives the FSM loop of the Saga.
|
|
229
|
-
*
|
|
317
|
+
*
|
|
230
318
|
* @remarks
|
|
231
319
|
* **Execution Context**: **Sender (Local)**.
|
|
232
|
-
*
|
|
320
|
+
*
|
|
233
321
|
* This method manages the "Ping Pong" request-response cycle on the Sender.
|
|
234
322
|
* It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
|
|
235
|
-
*
|
|
323
|
+
*
|
|
236
324
|
* **Data Transport Note**:
|
|
237
325
|
* Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
|
|
238
326
|
* When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
|
|
@@ -240,8 +328,8 @@ export class SyncSagaCoordinator {
|
|
|
240
328
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
241
329
|
*/
|
|
242
330
|
protected async executeSagaLoop({
|
|
243
|
-
|
|
244
|
-
|
|
331
|
+
initFrame,
|
|
332
|
+
initDomainGraph,
|
|
245
333
|
peer,
|
|
246
334
|
sessionIdentity,
|
|
247
335
|
updates$,
|
|
@@ -249,8 +337,14 @@ export class SyncSagaCoordinator {
|
|
|
249
337
|
tempSpace,
|
|
250
338
|
metaspace
|
|
251
339
|
}: {
|
|
252
|
-
|
|
253
|
-
|
|
340
|
+
initFrame: SyncIbGib_V1,
|
|
341
|
+
/**
|
|
342
|
+
* This is the initial dependency graph of all domain ibgibs passed in
|
|
343
|
+
* to the original {@link sync} call.
|
|
344
|
+
*
|
|
345
|
+
* if we're executing on the sender, this will be populated
|
|
346
|
+
*/
|
|
347
|
+
initDomainGraph: FlatIbGibGraph,
|
|
254
348
|
peer: SyncPeerWitness,
|
|
255
349
|
sessionIdentity?: KeystoneIbGib_V1,
|
|
256
350
|
updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
|
|
@@ -260,44 +354,23 @@ export class SyncSagaCoordinator {
|
|
|
260
354
|
}): Promise<void> {
|
|
261
355
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
262
356
|
|
|
263
|
-
|
|
264
|
-
let currentFrame: SyncIbGib_V1 | null =
|
|
265
|
-
|
|
357
|
+
/** The current frame we just generated (e.g., Init or Delta Request) */
|
|
358
|
+
let currentFrame: SyncIbGib_V1 | null = initFrame;
|
|
359
|
+
/** The **domain** payload we need to transmit (data we are sending) */
|
|
266
360
|
let nextDomainIbGibs: IbGib_V1[] = [];
|
|
267
361
|
|
|
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
362
|
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)`); }
|
|
363
|
+
// first, prepare for context transmission...
|
|
297
364
|
|
|
298
|
-
|
|
299
|
-
|
|
365
|
+
/**
|
|
366
|
+
* this is used later in pollForDomainPayloads **iif** payloads are
|
|
367
|
+
* received **in the response**, i.e., on the return trip. So if we
|
|
368
|
+
* request addrs, the response should have these addrs and their
|
|
369
|
+
* dependencies. The ibgibs corresponding to these addrs will be
|
|
370
|
+
* streamed and placed into this map.
|
|
371
|
+
*/
|
|
300
372
|
const domainPayloadsMap = new Map<string, IbGib_V1>();
|
|
373
|
+
// #region set up peer observable for any domainPayloadsMap
|
|
301
374
|
const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
|
|
302
375
|
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
303
376
|
next: async (ibgib: IbGib_V1) => {
|
|
@@ -319,43 +392,49 @@ export class SyncSagaCoordinator {
|
|
|
319
392
|
await subscription.unsubscribe();
|
|
320
393
|
},
|
|
321
394
|
}));
|
|
395
|
+
// #endregion set up peer observable for any domainPayloadsMap
|
|
322
396
|
|
|
323
|
-
//
|
|
397
|
+
// ...create/compose the Request Context itself...
|
|
324
398
|
const requestCtx = await createSyncSagaContext({
|
|
325
399
|
sagaFrame: currentFrame,
|
|
326
400
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
327
|
-
|
|
328
401
|
/**
|
|
329
402
|
* init frame: empty
|
|
330
|
-
*
|
|
403
|
+
* ack frame: possible push offers
|
|
404
|
+
* delta frame: requested ibgibs or commit offer
|
|
405
|
+
* commit frame: empty
|
|
331
406
|
*/
|
|
332
|
-
payloadIbGibsDomain:
|
|
407
|
+
payloadIbGibsDomain: nextDomainIbGibs,
|
|
333
408
|
localSpace,
|
|
334
409
|
});
|
|
335
410
|
|
|
336
|
-
// Log what we're sending
|
|
411
|
+
// #region Log what we're sending
|
|
337
412
|
if (logalotControlDomain) {
|
|
338
|
-
const domainAddrs =
|
|
339
|
-
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I:
|
|
413
|
+
const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
414
|
+
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
|
|
340
415
|
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
341
416
|
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
342
417
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
343
418
|
}
|
|
419
|
+
// #endregion Log what we're sending
|
|
344
420
|
|
|
345
421
|
// update our saga listeners...
|
|
346
422
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
347
|
-
updates$.next(requestCtx); // spins off
|
|
423
|
+
updates$.next(requestCtx); // spins off for saga UI updates
|
|
348
424
|
|
|
349
|
-
// ...And send the context
|
|
425
|
+
// ...And send the context.
|
|
426
|
+
peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
|
|
350
427
|
const responseCtx = await peer.witness(requestCtx);
|
|
351
428
|
|
|
352
|
-
//
|
|
429
|
+
// the send returned, but a peer can return a falsy responseCtx, if
|
|
430
|
+
// we just sent a commit to them and they're done. If so, there will
|
|
431
|
+
// necessarily be no payload to wait to receive
|
|
353
432
|
if (!responseCtx) {
|
|
354
433
|
if (currentFrame) {
|
|
355
434
|
// Check for Commit (Peer silence expected)
|
|
356
435
|
const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
|
|
357
436
|
if (msg?.data?.stage === SyncStage.commit) {
|
|
358
|
-
if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete
|
|
437
|
+
if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`); }
|
|
359
438
|
currentFrame = null;
|
|
360
439
|
break;
|
|
361
440
|
} else {
|
|
@@ -366,19 +445,24 @@ export class SyncSagaCoordinator {
|
|
|
366
445
|
}
|
|
367
446
|
}
|
|
368
447
|
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
// ---------------------------------------------------------------------
|
|
372
|
-
// at this point, we have received the response context ibgib, but
|
|
448
|
+
// at this point, we did indeed receive a response to analyze. BUT!
|
|
449
|
+
// we have only necessarily received the response context ibgib.
|
|
373
450
|
// if there were payloads expected to be transferred, they may not
|
|
374
451
|
// be done yet.
|
|
452
|
+
|
|
375
453
|
if (!responseCtx.data) { throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`); }
|
|
376
|
-
updates$.next(responseCtx); // spins off
|
|
454
|
+
updates$.next(responseCtx); // spins off for saga UI updating
|
|
455
|
+
|
|
456
|
+
// immediately validate context/saga frame (but not payloads because
|
|
457
|
+
// we may not have those yet)
|
|
458
|
+
const contextAndSagaFrameValidationErrors =
|
|
459
|
+
await validateContextAndSagaFrame({ context: responseCtx });
|
|
460
|
+
if (contextAndSagaFrameValidationErrors.length > 0) { throw new Error(`contextAndSagaFrameValidationErrors: ${contextAndSagaFrameValidationErrors} (E: 6eebe8e7fa437c00a8cde3ada3c66826)`); }
|
|
377
461
|
|
|
378
462
|
// Extract expected domain addresses from response context
|
|
379
463
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] as string[] || [];
|
|
380
464
|
|
|
381
|
-
// Poll for them if needed
|
|
465
|
+
// Poll for them if needed. see above jsdocs for domainPayloadsMap
|
|
382
466
|
if (responsePayloadAddrsDomain.length > 0) {
|
|
383
467
|
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
384
468
|
expectedAddrs: responsePayloadAddrsDomain,
|
|
@@ -388,51 +472,62 @@ export class SyncSagaCoordinator {
|
|
|
388
472
|
});
|
|
389
473
|
}
|
|
390
474
|
|
|
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
|
|
475
|
+
// #region Log what we received back
|
|
396
476
|
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
477
|
if (logalotControlDomain) {
|
|
398
478
|
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] as string[] || [];
|
|
399
|
-
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I:
|
|
479
|
+
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
|
|
400
480
|
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
401
481
|
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
402
482
|
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
403
483
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
404
484
|
}
|
|
485
|
+
// #endregion Log what we received back
|
|
486
|
+
|
|
487
|
+
// at this point, we have received the context AND **all** of the
|
|
488
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
489
|
+
// iteration in the saga loop
|
|
405
490
|
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
// conflictInfos?: SyncSagaConflictInfo;
|
|
411
|
-
// }
|
|
412
|
-
const handleResult = await this.handleSagaContext({
|
|
491
|
+
// this is the part that drives the FSM forward, i.e., when we
|
|
492
|
+
// evolve the sync saga ibgib itself to the next frame if we aren't
|
|
493
|
+
// finished/errored out.
|
|
494
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
413
495
|
sagaContext: responseCtx,
|
|
496
|
+
initDomainGraph,
|
|
414
497
|
mySpace: localSpace,
|
|
415
498
|
myTempSpace: tempSpace,
|
|
416
499
|
metaspace,
|
|
417
500
|
});
|
|
418
501
|
|
|
419
|
-
if (!
|
|
420
|
-
if (logalot) { console.log(`${lc} Handler returned null (Saga End)
|
|
502
|
+
if (!contextResult) {
|
|
503
|
+
if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`); }
|
|
421
504
|
break;
|
|
422
505
|
}
|
|
423
506
|
|
|
424
|
-
|
|
507
|
+
// #region error conditions throw
|
|
508
|
+
if (contextResult.errorMsg) {
|
|
509
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
|
|
510
|
+
} else if (!contextResult.nextFrameInfo) {
|
|
511
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
512
|
+
} else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
513
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
514
|
+
}
|
|
515
|
+
// #endregion error conditions throw
|
|
425
516
|
|
|
426
|
-
//
|
|
427
|
-
|
|
517
|
+
// we have another frame to process and send out, with possibly
|
|
518
|
+
// payload domain ibgibs as well
|
|
519
|
+
const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
|
|
520
|
+
currentFrame = frame;
|
|
521
|
+
nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
|
|
428
522
|
|
|
429
|
-
// Log handler output for next iteration
|
|
523
|
+
// #region Log handler output for next iteration
|
|
430
524
|
if (logalotControlDomain) {
|
|
431
525
|
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
432
|
-
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I:
|
|
526
|
+
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
|
|
433
527
|
console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
434
528
|
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
435
529
|
}
|
|
530
|
+
// #endregion Log handler output for next iteration
|
|
436
531
|
}
|
|
437
532
|
}
|
|
438
533
|
|
|
@@ -499,7 +594,7 @@ export class SyncSagaCoordinator {
|
|
|
499
594
|
|
|
500
595
|
const res = await getLatestAddrs({ space, tjpAddrs: tjps });
|
|
501
596
|
if (!res.data || !res.data.latestAddrsMap) {
|
|
502
|
-
throw new Error(`${lc} Failed to get latest addrs. (E:
|
|
597
|
+
throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
|
|
503
598
|
}
|
|
504
599
|
|
|
505
600
|
// if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
|
|
@@ -552,10 +647,10 @@ export class SyncSagaCoordinator {
|
|
|
552
647
|
|
|
553
648
|
/**
|
|
554
649
|
* Creates the Initial Saga Frame (Init Stage).
|
|
555
|
-
*
|
|
650
|
+
*
|
|
556
651
|
* @remarks
|
|
557
652
|
* **Execution Context**: **Sender (Local)**.
|
|
558
|
-
*
|
|
653
|
+
*
|
|
559
654
|
* Generates the first frame containing the Knowledge Vector of the Local Space.
|
|
560
655
|
* This is sent to the Receiver to begin Gap Analysis.
|
|
561
656
|
*/
|
|
@@ -575,14 +670,14 @@ export class SyncSagaCoordinator {
|
|
|
575
670
|
metaspace: MetaspaceService,
|
|
576
671
|
localSpace: IbGibSpaceAny,
|
|
577
672
|
tempSpace: IbGibSpaceAny,
|
|
578
|
-
}): Promise<{
|
|
673
|
+
}): Promise<{ initFrame: SyncIbGib_V1, initDomainGraph: { [addr: string]: IbGib_V1 } }> {
|
|
579
674
|
const lc = `${this.lc}[${this.createInitFrame.name}]`;
|
|
580
675
|
try {
|
|
581
676
|
if (logalot) { console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`); }
|
|
582
677
|
|
|
583
678
|
// Analyze Timelines & Stones
|
|
584
679
|
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
585
|
-
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
680
|
+
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
586
681
|
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
587
682
|
|
|
588
683
|
// we need to store the fullGraph in our tempSpace for later...
|
|
@@ -627,7 +722,7 @@ export class SyncSagaCoordinator {
|
|
|
627
722
|
|
|
628
723
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
629
724
|
|
|
630
|
-
return { sagaFrame,
|
|
725
|
+
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
631
726
|
} catch (error) {
|
|
632
727
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
633
728
|
throw error;
|
|
@@ -639,7 +734,7 @@ export class SyncSagaCoordinator {
|
|
|
639
734
|
/**
|
|
640
735
|
* Helper to poll for streaming domain payloads and put them in the
|
|
641
736
|
* local {@link tempSpace}.
|
|
642
|
-
*
|
|
737
|
+
*
|
|
643
738
|
* @returns when all {@link expectedAddrs} are done being transmitted.
|
|
644
739
|
*/
|
|
645
740
|
protected async pollForDomainPayloads({
|
|
@@ -662,7 +757,7 @@ export class SyncSagaCoordinator {
|
|
|
662
757
|
let pending = [...expectedAddrs];
|
|
663
758
|
const start = Date.now();
|
|
664
759
|
/**
|
|
665
|
-
* This needs
|
|
760
|
+
* This needs
|
|
666
761
|
*/
|
|
667
762
|
const timeoutMs = 5 * 60 * 1000; // 5 minutes...arbitrary at this point. This needs to be pulled out and improved eesh.
|
|
668
763
|
|
|
@@ -708,26 +803,28 @@ export class SyncSagaCoordinator {
|
|
|
708
803
|
|
|
709
804
|
/**
|
|
710
805
|
* This is the heart of the "ping pong" transaction, where we send a context
|
|
806
|
+
*
|
|
711
807
|
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
712
808
|
* a whole.
|
|
713
|
-
*
|
|
809
|
+
*
|
|
714
810
|
* This is called in two places:
|
|
715
|
-
*
|
|
811
|
+
*
|
|
716
812
|
* ## 1. Sender
|
|
717
|
-
*
|
|
813
|
+
*
|
|
718
814
|
* On the sender, this is called within the {@link executeSagaLoop} which
|
|
719
|
-
* initiates and drives the sync.
|
|
720
|
-
*
|
|
815
|
+
* initiates and drives the sync.
|
|
816
|
+
*
|
|
721
817
|
* ## 2. Receiver
|
|
722
|
-
*
|
|
818
|
+
*
|
|
723
819
|
* 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
|
|
820
|
+
* endpoint's job is basically to get these things collocated and prepared
|
|
725
821
|
* to make this call.
|
|
726
|
-
*
|
|
822
|
+
*
|
|
727
823
|
* This is a one-off on the receiver.
|
|
728
824
|
*/
|
|
729
|
-
public async
|
|
825
|
+
public async handleResponseSagaContext({
|
|
730
826
|
sagaContext,
|
|
827
|
+
initDomainGraph,
|
|
731
828
|
mySpace,
|
|
732
829
|
myTempSpace,
|
|
733
830
|
identity,
|
|
@@ -735,6 +832,13 @@ export class SyncSagaCoordinator {
|
|
|
735
832
|
metaspace,
|
|
736
833
|
}: {
|
|
737
834
|
sagaContext: SyncSagaContextIbGib_V1,
|
|
835
|
+
/**
|
|
836
|
+
* This is the initial dependency graph of all domain ibgibs passed in
|
|
837
|
+
* to the original {@link sync} call.
|
|
838
|
+
*
|
|
839
|
+
* if we're executing on the sender, this will be populated
|
|
840
|
+
*/
|
|
841
|
+
initDomainGraph?: FlatIbGibGraph,
|
|
738
842
|
/**
|
|
739
843
|
* Local space relative to the execution context's POV
|
|
740
844
|
*/
|
|
@@ -746,8 +850,8 @@ export class SyncSagaCoordinator {
|
|
|
746
850
|
identity?: KeystoneIbGib_V1,
|
|
747
851
|
identitySecret?: string,
|
|
748
852
|
metaspace: MetaspaceService,
|
|
749
|
-
}): Promise<
|
|
750
|
-
const lc = `${this.lc}[${this.
|
|
853
|
+
}): Promise<HandleSagaResponseContextResult> {
|
|
854
|
+
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
751
855
|
try {
|
|
752
856
|
if (logalot) { console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`); }
|
|
753
857
|
|
|
@@ -765,9 +869,10 @@ export class SyncSagaCoordinator {
|
|
|
765
869
|
*/
|
|
766
870
|
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
767
871
|
|
|
872
|
+
let nextFrameInfo: NextSagaFrameInfo;
|
|
768
873
|
switch (stage) {
|
|
769
874
|
case SyncStage.init:
|
|
770
|
-
|
|
875
|
+
nextFrameInfo = await this.handleInitFrame({
|
|
771
876
|
sagaIbGib,
|
|
772
877
|
messageData: messageData as SyncSagaMessageInitData_V1,
|
|
773
878
|
metaspace,
|
|
@@ -776,22 +881,39 @@ export class SyncSagaCoordinator {
|
|
|
776
881
|
identity,
|
|
777
882
|
identitySecret
|
|
778
883
|
});
|
|
884
|
+
break;
|
|
779
885
|
|
|
780
886
|
case SyncStage.ack:
|
|
781
|
-
|
|
887
|
+
if (!initDomainGraph) { throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`); }
|
|
888
|
+
nextFrameInfo = await this.handleAckFrame({
|
|
889
|
+
sagaIbGib,
|
|
890
|
+
srcGraph,
|
|
891
|
+
initDomainGraph,
|
|
892
|
+
metaspace,
|
|
893
|
+
destSpace: mySpace,
|
|
894
|
+
tempSpace: myTempSpace,
|
|
895
|
+
identity,
|
|
896
|
+
});
|
|
897
|
+
break;
|
|
782
898
|
|
|
783
899
|
case SyncStage.delta:
|
|
784
|
-
|
|
900
|
+
nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
901
|
+
break;
|
|
785
902
|
|
|
786
903
|
case SyncStage.commit:
|
|
787
|
-
|
|
904
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
905
|
+
break;
|
|
788
906
|
|
|
789
907
|
default:
|
|
790
908
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
791
909
|
}
|
|
910
|
+
|
|
911
|
+
return { errorMsg: undefined, nextFrameInfo, }
|
|
912
|
+
|
|
792
913
|
} catch (error) {
|
|
793
|
-
|
|
794
|
-
|
|
914
|
+
const errorMsg = `${lc} ${extractErrorMsg(error)}`;
|
|
915
|
+
console.error(errorMsg);
|
|
916
|
+
return { errorMsg }
|
|
795
917
|
} finally {
|
|
796
918
|
if (logalot) { console.log(`${lc} complete.`); }
|
|
797
919
|
}
|
|
@@ -801,10 +923,10 @@ export class SyncSagaCoordinator {
|
|
|
801
923
|
|
|
802
924
|
/**
|
|
803
925
|
* Handles the `Init` frame.
|
|
804
|
-
*
|
|
926
|
+
*
|
|
805
927
|
* @remarks
|
|
806
928
|
* **Execution Context**: **Receiver (Remote)**.
|
|
807
|
-
*
|
|
929
|
+
*
|
|
808
930
|
* The Receiver performs Gap Analysis here:
|
|
809
931
|
* 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
|
|
810
932
|
* 2. Identifies what Sender needs (`pushOfferAddrs`).
|
|
@@ -828,7 +950,7 @@ export class SyncSagaCoordinator {
|
|
|
828
950
|
mySpace: IbGibSpaceAny,
|
|
829
951
|
/**
|
|
830
952
|
* Local temp space relative to the execution context's POV.
|
|
831
|
-
*
|
|
953
|
+
*
|
|
832
954
|
* NOTE: Since this always executes on the receiver's end, this should
|
|
833
955
|
* be the receiver's temp space.
|
|
834
956
|
*/
|
|
@@ -836,18 +958,17 @@ export class SyncSagaCoordinator {
|
|
|
836
958
|
metaspace: MetaspaceService,
|
|
837
959
|
identity?: KeystoneIbGib_V1,
|
|
838
960
|
identitySecret?: string,
|
|
839
|
-
}): Promise<
|
|
961
|
+
}): Promise<NextSagaFrameInfo> {
|
|
840
962
|
const lc = `${this.lc}[${this.handleInitFrame.name}]`;
|
|
841
963
|
try {
|
|
842
964
|
if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
|
|
843
965
|
|
|
844
|
-
console.log(`${lc} [TEST DEBUG]
|
|
845
|
-
if (logalot) { console.log(`${lc} starting...`); }
|
|
966
|
+
console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
|
|
846
967
|
|
|
847
968
|
// Extract Init Data
|
|
848
969
|
const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
|
|
849
970
|
if (initData.stage !== SyncStage.init) {
|
|
850
|
-
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E:
|
|
971
|
+
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
|
|
851
972
|
}
|
|
852
973
|
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
853
974
|
if (!initData || !initData.knowledgeVector) {
|
|
@@ -859,8 +980,8 @@ export class SyncSagaCoordinator {
|
|
|
859
980
|
|
|
860
981
|
// 2. Gap Analysis
|
|
861
982
|
const conflicts: SyncSagaConflictInfo[] = [];
|
|
862
|
-
const
|
|
863
|
-
const
|
|
983
|
+
const deltaRequestAddrInfos: SyncSagaRequestAddrInfo[] = [];
|
|
984
|
+
const pushOfferInfos: SyncSagaPushOfferInfo[] = [];
|
|
864
985
|
|
|
865
986
|
// First do the consant stones (Non-TJPs)
|
|
866
987
|
const stones = initData.stones || [];
|
|
@@ -874,13 +995,15 @@ export class SyncSagaCoordinator {
|
|
|
874
995
|
if (addrsNotFound && addrsNotFound.length > 0) {
|
|
875
996
|
if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
|
|
876
997
|
addrsNotFound.forEach(addr => {
|
|
877
|
-
if (!
|
|
998
|
+
if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
|
|
999
|
+
deltaRequestAddrInfos.push({ addr }); // no tjpAddr
|
|
1000
|
+
}
|
|
878
1001
|
});
|
|
879
1002
|
}
|
|
880
1003
|
}
|
|
881
1004
|
|
|
882
1005
|
/**
|
|
883
|
-
* "remote"
|
|
1006
|
+
* "remote" is sender in this case
|
|
884
1007
|
*/
|
|
885
1008
|
const remoteKV = initData.knowledgeVector;
|
|
886
1009
|
if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
|
|
@@ -889,7 +1012,7 @@ export class SyncSagaCoordinator {
|
|
|
889
1012
|
if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
|
|
890
1013
|
|
|
891
1014
|
// 1. Get Local Latest Addrs for all TJPs
|
|
892
|
-
let
|
|
1015
|
+
let localLatestAddrsMap: { [tjp: string]: string | null } = {};
|
|
893
1016
|
if (remoteTjps.length > 0) {
|
|
894
1017
|
// Batch get latest addrs for the TJPs
|
|
895
1018
|
const resGetLatestAddrs = await getLatestAddrs({
|
|
@@ -898,56 +1021,91 @@ export class SyncSagaCoordinator {
|
|
|
898
1021
|
});
|
|
899
1022
|
if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
|
|
900
1023
|
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(
|
|
1024
|
+
localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
|
|
1025
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
|
|
1026
|
+
if (logalot) { console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`); }
|
|
904
1027
|
}
|
|
905
1028
|
|
|
906
1029
|
// 2. Gap Analysis
|
|
907
1030
|
for (const tjp of remoteTjps) {
|
|
908
1031
|
const remoteAddr = remoteKV[tjp];
|
|
909
|
-
const localAddr =
|
|
1032
|
+
const localAddr = localLatestAddrsMap[tjp];
|
|
910
1033
|
|
|
911
1034
|
if (!localAddr) {
|
|
912
1035
|
// We (Receiver) don't have this timeline at all. Request it.
|
|
913
1036
|
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
914
|
-
|
|
1037
|
+
deltaRequestAddrInfos.push({
|
|
1038
|
+
addr: remoteAddr,
|
|
1039
|
+
tjpAddr: tjp,
|
|
1040
|
+
// we don't have this timeline at all
|
|
1041
|
+
// latestAddrAlreadyHave: undefined
|
|
1042
|
+
});
|
|
915
1043
|
continue;
|
|
916
1044
|
}
|
|
917
1045
|
|
|
1046
|
+
// we do have this timeline...
|
|
1047
|
+
|
|
918
1048
|
if (localAddr === remoteAddr) {
|
|
919
|
-
//
|
|
1049
|
+
// ...already synced
|
|
920
1050
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
921
1051
|
continue;
|
|
922
1052
|
}
|
|
923
1053
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
924
1054
|
|
|
925
|
-
//
|
|
926
|
-
|
|
927
|
-
|
|
1055
|
+
// we have this timeline but it's not synced...
|
|
1056
|
+
|
|
1057
|
+
// We're executing on receiver. Check if Remote (Sender) is in
|
|
1058
|
+
// our past, and if so, we are ahead and need to push the delta
|
|
1059
|
+
// to remote
|
|
1060
|
+
const remoteIsInPast = await isPastFrame({
|
|
928
1061
|
olderAddr: remoteAddr,
|
|
929
1062
|
newerAddr: localAddr,
|
|
930
1063
|
space: mySpace,
|
|
931
1064
|
});
|
|
932
1065
|
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
|
|
1066
|
+
if (remoteIsInPast) {
|
|
1067
|
+
// we're ahead, so push the delta of what the sender doesn't
|
|
1068
|
+
// have (we have full knowledge)
|
|
1069
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
|
|
1070
|
+
const deltaGraph = await getDeltaDependencyGraph({
|
|
1071
|
+
ibGibAddr: localAddr,
|
|
1072
|
+
live: false, // always live: false right?
|
|
1073
|
+
latestCommonFrameAddr: remoteAddr,
|
|
1074
|
+
space: mySpace,
|
|
1075
|
+
});
|
|
1076
|
+
pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
|
|
936
1077
|
} else {
|
|
937
|
-
// Remote is not in our past.
|
|
938
|
-
//
|
|
1078
|
+
// Remote tip is not in our past. So, either Remote is
|
|
1079
|
+
// ahead (we're in THEIR past, i.e., Fast-Backward) OR
|
|
1080
|
+
// Diverged/conflict (we've both made edits).
|
|
939
1081
|
|
|
940
1082
|
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1083
|
+
/**
|
|
1084
|
+
* we could first check for existence of remoteAddr in
|
|
1085
|
+
* mySpace, but this is quick and easy. If it throws, we
|
|
1086
|
+
* don't have it so it can't be in the past.
|
|
1087
|
+
*/
|
|
1088
|
+
let localIsInPast = false;
|
|
1089
|
+
try {
|
|
1090
|
+
// we could
|
|
1091
|
+
localIsInPast = await isPastFrame({
|
|
1092
|
+
olderAddr: localAddr,
|
|
1093
|
+
newerAddr: remoteAddr,
|
|
1094
|
+
space: mySpace,
|
|
1095
|
+
});
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
// couldn't get one of them, so localIsInPast remains false.
|
|
1098
|
+
if (logalot) { console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`); }
|
|
1099
|
+
}
|
|
946
1100
|
|
|
947
|
-
if (
|
|
1101
|
+
if (localIsInPast) {
|
|
948
1102
|
// Fast-Forward: We update to remote's tip.
|
|
949
1103
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
950
|
-
|
|
1104
|
+
deltaRequestAddrInfos.push({
|
|
1105
|
+
addr: remoteAddr,
|
|
1106
|
+
tjpAddr: tjp,
|
|
1107
|
+
latestAddrAlreadyHave: localAddr,
|
|
1108
|
+
});
|
|
951
1109
|
} else {
|
|
952
1110
|
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
953
1111
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
|
|
@@ -966,7 +1124,7 @@ export class SyncSagaCoordinator {
|
|
|
966
1124
|
terminal: true
|
|
967
1125
|
});
|
|
968
1126
|
} else if (conflictStrategy === 'optimistic') {
|
|
969
|
-
// Optimistic: We want
|
|
1127
|
+
// Optimistic: We want resolve this.
|
|
970
1128
|
// We need to send our history to the Sender so they can Merge.
|
|
971
1129
|
|
|
972
1130
|
// Fetch Full History for Local Timeline
|
|
@@ -979,10 +1137,10 @@ export class SyncSagaCoordinator {
|
|
|
979
1137
|
// We have localAddr.
|
|
980
1138
|
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
981
1139
|
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
982
|
-
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E:
|
|
1140
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
983
1141
|
}
|
|
984
1142
|
const localTip = resLocalTip.ibGibs[0];
|
|
985
|
-
if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E:
|
|
1143
|
+
if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`); }
|
|
986
1144
|
|
|
987
1145
|
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
988
1146
|
|
|
@@ -994,9 +1152,8 @@ export class SyncSagaCoordinator {
|
|
|
994
1152
|
reason: 'divergence',
|
|
995
1153
|
terminal: false
|
|
996
1154
|
});
|
|
997
|
-
|
|
998
1155
|
} else {
|
|
999
|
-
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E:
|
|
1156
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
|
|
1000
1157
|
}
|
|
1001
1158
|
}
|
|
1002
1159
|
}
|
|
@@ -1012,57 +1169,15 @@ export class SyncSagaCoordinator {
|
|
|
1012
1169
|
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
1013
1170
|
}
|
|
1014
1171
|
|
|
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
1172
|
// 3. Create Ack Frame
|
|
1058
|
-
|
|
1173
|
+
if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`); }
|
|
1174
|
+
const sagaId = sagaIbGib.data.uuid;
|
|
1059
1175
|
// Create Payload Stone
|
|
1060
1176
|
const ackData: SyncSagaMessageAckData_V1 = {
|
|
1061
1177
|
sagaId,
|
|
1062
1178
|
stage: SyncStage.ack,
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
knowledgeVector,
|
|
1179
|
+
deltaRequestAddrInfos,
|
|
1180
|
+
pushOfferInfos,
|
|
1066
1181
|
};
|
|
1067
1182
|
if (conflicts?.length > 0) { ackData.conflicts = conflicts; }
|
|
1068
1183
|
|
|
@@ -1088,15 +1203,60 @@ export class SyncSagaCoordinator {
|
|
|
1088
1203
|
* we want to push ibgibs to the remote/sender if we have push
|
|
1089
1204
|
* offers. an ack frame's payloads, if any, are those push offers
|
|
1090
1205
|
*/
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1206
|
+
let payloadIbGibsDomain: IbGib_V1[] | undefined;
|
|
1207
|
+
if (pushOfferInfos.length > 0) {
|
|
1208
|
+
const searchSecondSpaceAddrs: IbGibAddr[] = [];
|
|
1209
|
+
payloadIbGibsDomain = [];
|
|
1210
|
+
const domainAddrs: IbGibAddr[] = pushOfferInfos.flatMap(x => x.addrs);
|
|
1211
|
+
const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
|
|
1212
|
+
const resGetRawData = resGet.rawResultIbGib?.data as IbGibSpaceResultData | undefined;
|
|
1213
|
+
if (!resGetRawData) { throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`); }
|
|
1214
|
+
const addrsNotFound = resGetRawData.addrsNotFound ?? [];
|
|
1215
|
+
const addrsErrored = resGetRawData.addrsErrored ?? [];
|
|
1216
|
+
if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
|
|
1217
|
+
// found all of them
|
|
1218
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
|
|
1219
|
+
} else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
|
|
1220
|
+
// found some of them
|
|
1221
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
|
|
1222
|
+
const foundPlusNotFoundCount =
|
|
1223
|
+
payloadIbGibsDomain.length +
|
|
1224
|
+
addrsNotFound.length +
|
|
1225
|
+
addrsErrored.length;
|
|
1226
|
+
if (foundPlusNotFoundCount === domainAddrs.length) {
|
|
1227
|
+
// we can still conceivably get the remaining addrs
|
|
1228
|
+
addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1229
|
+
addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1230
|
+
} else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
|
|
1231
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
|
|
1232
|
+
} else {
|
|
1233
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
|
|
1234
|
+
}
|
|
1235
|
+
} else {
|
|
1236
|
+
// found none of them(?)
|
|
1237
|
+
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)`);
|
|
1238
|
+
domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1239
|
+
}
|
|
1240
|
+
if (searchSecondSpaceAddrs.length > 0) {
|
|
1241
|
+
if (logalot) { console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`); }
|
|
1242
|
+
const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
|
|
1243
|
+
if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
|
|
1244
|
+
// got them all the second try
|
|
1245
|
+
resGet2.ibGibs.forEach(x => payloadIbGibsDomain!.push(x));
|
|
1246
|
+
} else {
|
|
1247
|
+
resGet2.ibGibs?.forEach(x => payloadIbGibsDomain!.push(x));
|
|
1248
|
+
const addrsFailed = domainAddrs.filter(x =>
|
|
1249
|
+
!payloadIbGibsDomain!.map(pid => getIbGibAddr({ ibGib: pid })).includes(x)
|
|
1250
|
+
);
|
|
1251
|
+
throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
// we have now populated payloadIbGibsDomain
|
|
1094
1255
|
}
|
|
1095
1256
|
|
|
1096
1257
|
return {
|
|
1097
1258
|
frame: ackFrame,
|
|
1098
|
-
|
|
1099
|
-
// payloadIbGibsDomain,
|
|
1259
|
+
payloadIbGibsDomain,
|
|
1100
1260
|
};
|
|
1101
1261
|
|
|
1102
1262
|
} catch (error) {
|
|
@@ -1109,19 +1269,20 @@ export class SyncSagaCoordinator {
|
|
|
1109
1269
|
|
|
1110
1270
|
/**
|
|
1111
1271
|
* Handles the `Ack` frame.
|
|
1112
|
-
*
|
|
1272
|
+
*
|
|
1113
1273
|
* @remarks
|
|
1114
1274
|
* **Execution Context**: **Sender (Local)**.
|
|
1115
|
-
*
|
|
1275
|
+
*
|
|
1116
1276
|
* The Sender reacts to the Receiver's requirements:
|
|
1117
1277
|
* 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
|
|
1118
1278
|
* 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
|
|
1119
|
-
*
|
|
1279
|
+
*
|
|
1120
1280
|
* Returns a `Delta` frame.
|
|
1121
1281
|
*/
|
|
1122
1282
|
protected async handleAckFrame({
|
|
1123
1283
|
sagaIbGib,
|
|
1124
1284
|
srcGraph,
|
|
1285
|
+
initDomainGraph,
|
|
1125
1286
|
destSpace,
|
|
1126
1287
|
tempSpace,
|
|
1127
1288
|
metaspace,
|
|
@@ -1129,11 +1290,18 @@ export class SyncSagaCoordinator {
|
|
|
1129
1290
|
}: {
|
|
1130
1291
|
sagaIbGib: SyncIbGib_V1,
|
|
1131
1292
|
srcGraph: { [addr: string]: IbGib_V1 },
|
|
1293
|
+
/**
|
|
1294
|
+
* This is the initial dependency graph of all domain ibgibs passed in
|
|
1295
|
+
* to the original {@link sync} call.
|
|
1296
|
+
*
|
|
1297
|
+
* if we're executing on the sender, this will be populated
|
|
1298
|
+
*/
|
|
1299
|
+
initDomainGraph: FlatIbGibGraph,
|
|
1132
1300
|
destSpace: IbGibSpaceAny,
|
|
1133
1301
|
tempSpace: IbGibSpaceAny,
|
|
1134
1302
|
metaspace: MetaspaceService,
|
|
1135
1303
|
identity?: KeystoneIbGib_V1,
|
|
1136
|
-
}): Promise<
|
|
1304
|
+
}): Promise<NextSagaFrameInfo> {
|
|
1137
1305
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
1138
1306
|
try {
|
|
1139
1307
|
if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
|
|
@@ -1161,14 +1329,15 @@ export class SyncSagaCoordinator {
|
|
|
1161
1329
|
console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
|
|
1162
1330
|
// Terminal failure. Sender should probably Commit(Fail) or just Abort.
|
|
1163
1331
|
// For now, throw to trigger abort.
|
|
1164
|
-
throw new Error(`${lc} Peer reported terminal conflicts. (E:
|
|
1332
|
+
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1165
1333
|
}
|
|
1166
1334
|
|
|
1167
|
-
|
|
1335
|
+
// at this point, if we have conflicts, they are non-terminal
|
|
1336
|
+
|
|
1168
1337
|
const mergeDeltaReqs: string[] = []; // Additional requests for merging
|
|
1169
1338
|
|
|
1170
|
-
if (
|
|
1171
|
-
console.log(`${lc} [CONFLICT DEBUG] Processing ${
|
|
1339
|
+
if (conflicts.length > 0) {
|
|
1340
|
+
console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
|
|
1172
1341
|
// We need to resolve these.
|
|
1173
1342
|
// Strategy:
|
|
1174
1343
|
// 1. Analyze Divergence (Sender vs Receiver)
|
|
@@ -1189,7 +1358,7 @@ export class SyncSagaCoordinator {
|
|
|
1189
1358
|
// Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
|
|
1190
1359
|
|
|
1191
1360
|
// If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
|
|
1192
|
-
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1361
|
+
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1193
1362
|
// AND we piggyback our own requests?
|
|
1194
1363
|
// OR: We treat the Conflict Resolution as a sub-saga or side-effect?
|
|
1195
1364
|
|
|
@@ -1201,126 +1370,131 @@ export class SyncSagaCoordinator {
|
|
|
1201
1370
|
|
|
1202
1371
|
// Let's analyze and pull immediately.
|
|
1203
1372
|
|
|
1204
|
-
for (const conflict of
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
//
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
//
|
|
1213
|
-
// We
|
|
1214
|
-
//
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
// We need
|
|
1218
|
-
//
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
//
|
|
1224
|
-
//
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1373
|
+
for (const conflict of conflicts) {
|
|
1374
|
+
// todo: integrate conflict strategies into this point...this whole block needs to be redone, but I want to check the fast-forward/backward simpler case tests first
|
|
1375
|
+
|
|
1376
|
+
throw new Error(`conflicts not (re)implemented yet (E: 3b7d0819f83842a6de3ae988819bc826)`);
|
|
1377
|
+
|
|
1378
|
+
// const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
|
|
1379
|
+
|
|
1380
|
+
// // Sender History
|
|
1381
|
+
// // We need our own history for this timeline.
|
|
1382
|
+
// // We know the 'senderTip' (remoteAddr in Ack).
|
|
1383
|
+
// // Sender should verify it has this tip.
|
|
1384
|
+
|
|
1385
|
+
// // Compute Diffs
|
|
1386
|
+
// // We need to find `receiverOnly` addrs.
|
|
1387
|
+
// // Receiver sent us `timelineAddrs` (Full History).
|
|
1388
|
+
// const receiverHistorySet = new Set(timelineAddrs);
|
|
1389
|
+
|
|
1390
|
+
// // We need our execution context's history for this senderTip.
|
|
1391
|
+
// // We can fetch valid 'past' from space.
|
|
1392
|
+
// const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
|
|
1393
|
+
// const senderTipIbGib = resSenderTip.ibGibs?.[0];
|
|
1394
|
+
// if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
|
|
1395
|
+
|
|
1396
|
+
// // Basic Diff: Find what Receiver has that we don't.
|
|
1397
|
+
// // Actually, we need to traverse OUR past to find commonality.
|
|
1398
|
+
// const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
|
|
1399
|
+
|
|
1400
|
+
// const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
|
|
1401
|
+
|
|
1402
|
+
// if (receiverOnlyAddrs.length > 0) {
|
|
1403
|
+
// console.log(`${lc} [CONFLICT DEBUG] Found ${receiverOnlyAddrs.length} receiver-only frames - need to pull for merge`);
|
|
1404
|
+
// console.log(`${lc} [CONFLICT DEBUG] Receiver-only addrs:`, receiverOnlyAddrs);
|
|
1405
|
+
// // PULL these frames from Peer into Local Space
|
|
1406
|
+
// // (Validation: We trust peer for now / verification happens on put)
|
|
1407
|
+
// for (const addr of receiverOnlyAddrs) {
|
|
1408
|
+
// console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
|
|
1409
|
+
// }
|
|
1410
|
+
|
|
1411
|
+
// // Compute DELTA dependencies for each receiver-only frame
|
|
1412
|
+
// // Find LCA to determine what dependencies we already have
|
|
1413
|
+
// const lcaAddr = timelineAddrs.find(addr => senderHistory.includes(addr));
|
|
1414
|
+
// console.log(`${lc} [CONFLICT DEBUG] LCA: ${lcaAddr || 'NONE'}`);
|
|
1415
|
+
|
|
1416
|
+
// const skipAddrsSet = new Set<string>();
|
|
1417
|
+
// if (lcaAddr) {
|
|
1418
|
+
// try {
|
|
1419
|
+
// const lcaRes = await getFromSpace({ addr: lcaAddr, space: destSpace });
|
|
1420
|
+
// const lcaIbGib = lcaRes.ibGibs?.[0];
|
|
1421
|
+
// if (lcaIbGib) {
|
|
1422
|
+
// const lcaDeps = await getDependencyGraph({ ibGib: lcaIbGib, space: destSpace });
|
|
1423
|
+
// if (lcaDeps) Object.keys(lcaDeps).forEach(a => skipAddrsSet.add(a));
|
|
1424
|
+
// console.log(`${lc} [CONFLICT DEBUG] LCA deps to skip: ${skipAddrsSet.size}`);
|
|
1425
|
+
// }
|
|
1426
|
+
// } catch (e) {
|
|
1427
|
+
// console.warn(`${lc} Error getting LCA deps: ${extractErrorMsg(e)}`);
|
|
1428
|
+
// }
|
|
1429
|
+
// }
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
// // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
|
|
1433
|
+
// for (const addr of receiverOnlyAddrs) {
|
|
1434
|
+
// // Add the frame itself first
|
|
1435
|
+
// if (!mergeDeltaReqs.includes(addr)) {
|
|
1436
|
+
// mergeDeltaReqs.push(addr);
|
|
1437
|
+
// }
|
|
1438
|
+
|
|
1439
|
+
// // Get the frame's delta dependencies (skip LCA's deps)
|
|
1440
|
+
// try {
|
|
1441
|
+
// const frameRes = await getFromSpace({ addr, space: destSpace });
|
|
1442
|
+
// const frameIbGib = frameRes.ibGibs?.[0];
|
|
1443
|
+
|
|
1444
|
+
// if (frameIbGib) {
|
|
1445
|
+
// // Get dependency graph, skipping all LCA dependencies
|
|
1446
|
+
// const frameDeltaDeps = await getDependencyGraph({
|
|
1447
|
+
// ibGib: frameIbGib,
|
|
1448
|
+
// space: destSpace,
|
|
1449
|
+
// skipAddrs: Array.from(skipAddrsSet), // Skip entire LCA dep graph
|
|
1450
|
+
// });
|
|
1451
|
+
|
|
1452
|
+
// if (frameDeltaDeps) {
|
|
1453
|
+
// // Add all delta dependencies (Object.keys gives us the addresses)
|
|
1454
|
+
// Object.keys(frameDeltaDeps).forEach(depAddr => {
|
|
1455
|
+
// if (!mergeDeltaReqs.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
|
|
1456
|
+
// mergeDeltaReqs.push(depAddr);
|
|
1457
|
+
// }
|
|
1458
|
+
// });
|
|
1459
|
+
// }
|
|
1460
|
+
// }
|
|
1461
|
+
// } catch (depError) {
|
|
1462
|
+
// console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1463
|
+
// }
|
|
1464
|
+
// }
|
|
1465
|
+
|
|
1466
|
+
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${mergeDeltaReqs.length}`);
|
|
1467
|
+
// } else {
|
|
1468
|
+
// console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1469
|
+
// }
|
|
1297
1470
|
}
|
|
1298
1471
|
|
|
1299
|
-
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${
|
|
1472
|
+
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. mergeDeltaReqs: ${mergeDeltaReqs.length}`);
|
|
1300
1473
|
} else {
|
|
1301
1474
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1302
1475
|
}
|
|
1303
1476
|
|
|
1304
1477
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1305
1478
|
|
|
1306
|
-
const deltaReqAddrs = ackData.
|
|
1307
|
-
const pushOfferAddrs = ackData.
|
|
1479
|
+
const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
|
|
1480
|
+
const pushOfferAddrs = ackData.pushOfferInfos || [];
|
|
1308
1481
|
|
|
1309
1482
|
// 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
|
|
1310
1483
|
const pullReqAddrs: string[] = [];
|
|
1311
1484
|
for (const addr of pushOfferAddrs) {
|
|
1312
|
-
const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1313
|
-
if (!existing) {
|
|
1314
|
-
|
|
1315
|
-
}
|
|
1485
|
+
// const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1486
|
+
// if (!existing) {
|
|
1487
|
+
// pullReqAddrs.push(addr);
|
|
1488
|
+
// }
|
|
1316
1489
|
}
|
|
1317
1490
|
|
|
1318
1491
|
// 2. Process Delta Requests (Push Payload)
|
|
1319
1492
|
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1493
|
+
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1320
1494
|
const skipAddrs = new Set<string>();
|
|
1321
1495
|
if (ackData.knowledgeVector) {
|
|
1322
1496
|
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1323
|
-
addrs.forEach(a => skipAddrs.add(a));
|
|
1497
|
+
// addrs.forEach(a => skipAddrs.add(a));
|
|
1324
1498
|
});
|
|
1325
1499
|
}
|
|
1326
1500
|
|
|
@@ -1328,24 +1502,24 @@ export class SyncSagaCoordinator {
|
|
|
1328
1502
|
// Gather all tips to sync first
|
|
1329
1503
|
const tipsToSync: IbGib_V1[] = [];
|
|
1330
1504
|
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
|
-
}
|
|
1505
|
+
// let ibGib = srcGraph[addr];
|
|
1506
|
+
// if (!ibGib) {
|
|
1507
|
+
// const res = await getFromSpace({ addr, space: destSpace });
|
|
1508
|
+
// if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1509
|
+
// ibGib = res.ibGibs[0];
|
|
1510
|
+
// }
|
|
1511
|
+
// }
|
|
1512
|
+
// if (ibGib) {
|
|
1513
|
+
// tipsToSync.push(ibGib);
|
|
1514
|
+
// } else {
|
|
1515
|
+
// throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
|
|
1516
|
+
// }
|
|
1343
1517
|
}
|
|
1344
1518
|
|
|
1345
1519
|
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1346
1520
|
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1347
1521
|
// `getDependencyGraph` takes a single ibGib.
|
|
1348
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1522
|
+
// We can optimize by doing it for each tip and unioning the result?
|
|
1349
1523
|
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1350
1524
|
// We will loop.
|
|
1351
1525
|
|
|
@@ -1420,7 +1594,8 @@ export class SyncSagaCoordinator {
|
|
|
1420
1594
|
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1421
1595
|
|
|
1422
1596
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1423
|
-
return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1597
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1598
|
+
throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
|
|
1424
1599
|
} catch (error) {
|
|
1425
1600
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1426
1601
|
throw error;
|
|
@@ -1431,10 +1606,10 @@ export class SyncSagaCoordinator {
|
|
|
1431
1606
|
|
|
1432
1607
|
/**
|
|
1433
1608
|
* Handles the `Delta` frame.
|
|
1434
|
-
*
|
|
1609
|
+
*
|
|
1435
1610
|
* @remarks
|
|
1436
1611
|
* **Execution Context**: **Both**.
|
|
1437
|
-
*
|
|
1612
|
+
*
|
|
1438
1613
|
* 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
1614
|
* 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
|
|
1440
1615
|
* 3. **Completion**: If no more requests, transitions to `Commit`.
|
|
@@ -1453,7 +1628,7 @@ export class SyncSagaCoordinator {
|
|
|
1453
1628
|
tempSpace: IbGibSpaceAny,
|
|
1454
1629
|
metaspace: MetaspaceService,
|
|
1455
1630
|
identity?: KeystoneIbGib_V1,
|
|
1456
|
-
}): Promise<
|
|
1631
|
+
}): Promise<NextSagaFrameInfo> {
|
|
1457
1632
|
const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
|
|
1458
1633
|
if (logalot) { console.log(`${lc} starting...`); }
|
|
1459
1634
|
|
|
@@ -1466,7 +1641,7 @@ export class SyncSagaCoordinator {
|
|
|
1466
1641
|
if (deltaData.stage !== SyncStage.delta) {
|
|
1467
1642
|
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1468
1643
|
}
|
|
1469
|
-
if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I:
|
|
1644
|
+
if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
|
|
1470
1645
|
|
|
1471
1646
|
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1472
1647
|
|
|
@@ -1477,8 +1652,8 @@ export class SyncSagaCoordinator {
|
|
|
1477
1652
|
// 1. Process Received Payload (Ingest)
|
|
1478
1653
|
const receivedPayloadIbGibs: IbGib_V1[] = [];
|
|
1479
1654
|
if (payloadAddrs.length > 0) {
|
|
1480
|
-
// We use `payloadAddrs` as the manifest.
|
|
1481
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1655
|
+
// We use `payloadAddrs` as the manifest.
|
|
1656
|
+
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1482
1657
|
// assuming the "Transport" layer put them there implicitly?
|
|
1483
1658
|
// OR, if we are local-only, we just get them.
|
|
1484
1659
|
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
@@ -1506,7 +1681,7 @@ export class SyncSagaCoordinator {
|
|
|
1506
1681
|
// Get the requested ibGib
|
|
1507
1682
|
let ibGib = srcGraph[addr];
|
|
1508
1683
|
if (!ibGib) {
|
|
1509
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1684
|
+
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1510
1685
|
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1511
1686
|
ibGib = res.ibGibs[0];
|
|
1512
1687
|
}
|
|
@@ -1602,7 +1777,7 @@ export class SyncSagaCoordinator {
|
|
|
1602
1777
|
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1603
1778
|
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1604
1779
|
// We have the tip!
|
|
1605
|
-
// Do we have the full history?
|
|
1780
|
+
// Do we have the full history?
|
|
1606
1781
|
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1607
1782
|
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1608
1783
|
|
|
@@ -1648,9 +1823,9 @@ export class SyncSagaCoordinator {
|
|
|
1648
1823
|
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1649
1824
|
requests: hasMyRequests ? myRequests : undefined,
|
|
1650
1825
|
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.
|
|
1826
|
+
// Wait. If we send data, we are NOT committing yet.
|
|
1652
1827
|
// We are sending data. The OTHER side must ingest it.
|
|
1653
|
-
// So proposeCommit = true?
|
|
1828
|
+
// So proposeCommit = true?
|
|
1654
1829
|
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1655
1830
|
// Yes.
|
|
1656
1831
|
};
|
|
@@ -1680,7 +1855,8 @@ export class SyncSagaCoordinator {
|
|
|
1680
1855
|
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1681
1856
|
|
|
1682
1857
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1683
|
-
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1858
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1859
|
+
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1684
1860
|
|
|
1685
1861
|
} else {
|
|
1686
1862
|
// We have nothing to send.
|
|
@@ -1712,7 +1888,8 @@ export class SyncSagaCoordinator {
|
|
|
1712
1888
|
if (identity) { commitCtrlPayloads.push(identity); }
|
|
1713
1889
|
|
|
1714
1890
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1715
|
-
return { frame: commitFrame, };
|
|
1891
|
+
// return { frame: commitFrame, };
|
|
1892
|
+
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1716
1893
|
|
|
1717
1894
|
} else {
|
|
1718
1895
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1770,7 +1947,8 @@ export class SyncSagaCoordinator {
|
|
|
1770
1947
|
if (identity) { commitCtrlPayloads2.push(identity); }
|
|
1771
1948
|
|
|
1772
1949
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1773
|
-
return { frame: commitFrame, };
|
|
1950
|
+
// return { frame: commitFrame, };
|
|
1951
|
+
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1774
1952
|
}
|
|
1775
1953
|
|
|
1776
1954
|
// Build control payloads for delta propose
|
|
@@ -1778,7 +1956,8 @@ export class SyncSagaCoordinator {
|
|
|
1778
1956
|
if (identity) { deltaCtrlPayloads.push(identity); }
|
|
1779
1957
|
|
|
1780
1958
|
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1781
|
-
return { frame: deltaFrame, };
|
|
1959
|
+
// return { frame: deltaFrame, };
|
|
1960
|
+
throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
|
|
1782
1961
|
}
|
|
1783
1962
|
}
|
|
1784
1963
|
}
|
|
@@ -1796,7 +1975,7 @@ export class SyncSagaCoordinator {
|
|
|
1796
1975
|
tempSpace: IbGibSpaceAny,
|
|
1797
1976
|
metaspace: MetaspaceService,
|
|
1798
1977
|
identity?: KeystoneIbGib_V1,
|
|
1799
|
-
}): Promise<
|
|
1978
|
+
}): Promise<NextSagaFrameInfo> {
|
|
1800
1979
|
const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
|
|
1801
1980
|
if (logalot) { console.log(`${lc} Commit received.`); }
|
|
1802
1981
|
|
|
@@ -1811,7 +1990,8 @@ export class SyncSagaCoordinator {
|
|
|
1811
1990
|
// Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
|
|
1812
1991
|
|
|
1813
1992
|
if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
|
|
1814
|
-
return
|
|
1993
|
+
// return { responseWasNull: true };
|
|
1994
|
+
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1815
1995
|
}
|
|
1816
1996
|
|
|
1817
1997
|
// #endregion Handlers
|
|
@@ -1909,7 +2089,7 @@ export class SyncSagaCoordinator {
|
|
|
1909
2089
|
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1910
2090
|
}
|
|
1911
2091
|
|
|
1912
|
-
// remove the existing sync msg stones' addrs
|
|
2092
|
+
// remove the existing sync msg stones' addrs
|
|
1913
2093
|
if (!prevSagaIbGib.rel8ns) { throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`); }
|
|
1914
2094
|
if (!prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] || prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME].length === 0) {
|
|
1915
2095
|
throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] falsy/empty? (E: 15156baad26fcccda80aa3a31718c726)`);
|