@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
|
@@ -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)`);
|
|
@@ -143,6 +142,75 @@ export class SyncSagaCoordinator {
|
|
|
143
142
|
done
|
|
144
143
|
};
|
|
145
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* This is what the receiving side of the sync calls to drive the FSM to the
|
|
147
|
+
* next stage.
|
|
148
|
+
*
|
|
149
|
+
* So whereas the sender executes a saga loop and drives the entire process,
|
|
150
|
+
* this is a reactive one-off that drives just the single step that the
|
|
151
|
+
* receiver does in that saga.
|
|
152
|
+
*
|
|
153
|
+
* @returns next context result if another round, else if commit returns
|
|
154
|
+
* null
|
|
155
|
+
*/
|
|
156
|
+
async receiverContinueSync({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
157
|
+
const lc = `${this.lc}[${this.receiverContinueSync.name}]`;
|
|
158
|
+
try {
|
|
159
|
+
if (logalot) {
|
|
160
|
+
console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`);
|
|
161
|
+
}
|
|
162
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
163
|
+
sagaContext,
|
|
164
|
+
mySpace,
|
|
165
|
+
myTempSpace,
|
|
166
|
+
identity,
|
|
167
|
+
identitySecret,
|
|
168
|
+
metaspace,
|
|
169
|
+
});
|
|
170
|
+
if (!contextResult) {
|
|
171
|
+
if (logalot) {
|
|
172
|
+
console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
// #region error conditions throw
|
|
177
|
+
if (contextResult.errorMsg) {
|
|
178
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
|
|
179
|
+
}
|
|
180
|
+
else if (!contextResult.nextFrameInfo) {
|
|
181
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
|
|
182
|
+
}
|
|
183
|
+
else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
184
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
|
|
185
|
+
}
|
|
186
|
+
// #endregion error conditions throw
|
|
187
|
+
// create the return context
|
|
188
|
+
const { frame, payloadIbGibsDomain } = contextResult.nextFrameInfo;
|
|
189
|
+
const responseCtx = await createSyncSagaContext({
|
|
190
|
+
sagaFrame: frame,
|
|
191
|
+
localSpace: mySpace,
|
|
192
|
+
payloadIbGibsDomain,
|
|
193
|
+
// todo: we need to thoroughly go through the identity per each step after getting basic merging
|
|
194
|
+
sessionKeystones: identity ? [identity] : undefined, // ??
|
|
195
|
+
});
|
|
196
|
+
const immediateValidationErrors = await validateContextAndSagaFrame({
|
|
197
|
+
context: responseCtx,
|
|
198
|
+
});
|
|
199
|
+
if (immediateValidationErrors.length > 0) {
|
|
200
|
+
throw new Error(`(UNEXPECTED) just created sync saga context () and there were immediateValidationErrors? immediateValidationErrors: ${immediateValidationErrors} (E: c120e8e0aa98673d685267a8a36e5826)`);
|
|
201
|
+
}
|
|
202
|
+
return responseCtx;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
if (logalot) {
|
|
210
|
+
console.log(`${lc} complete.`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
146
214
|
async getSessionIdentity({ sagaId, metaspace, tempSpace, }) {
|
|
147
215
|
const lc = `${this.lc}[${this.getSessionIdentity.name}]`;
|
|
148
216
|
try {
|
|
@@ -189,45 +257,23 @@ export class SyncSagaCoordinator {
|
|
|
189
257
|
* the NEXT request context.
|
|
190
258
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
191
259
|
*/
|
|
192
|
-
async executeSagaLoop({
|
|
260
|
+
async executeSagaLoop({ initFrame, initDomainGraph, peer, sessionIdentity, updates$, localSpace, tempSpace, metaspace }) {
|
|
193
261
|
const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
|
|
194
|
-
|
|
195
|
-
let currentFrame =
|
|
196
|
-
|
|
262
|
+
/** The current frame we just generated (e.g., Init or Delta Request) */
|
|
263
|
+
let currentFrame = initFrame;
|
|
264
|
+
/** The **domain** payload we need to transmit (data we are sending) */
|
|
197
265
|
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
266
|
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)
|
|
267
|
+
// first, prepare for context transmission...
|
|
268
|
+
/**
|
|
269
|
+
* this is used later in pollForDomainPayloads **iif** payloads are
|
|
270
|
+
* received **in the response**, i.e., on the return trip. So if we
|
|
271
|
+
* request addrs, the response should have these addrs and their
|
|
272
|
+
* dependencies. The ibgibs corresponding to these addrs will be
|
|
273
|
+
* streamed and placed into this map.
|
|
274
|
+
*/
|
|
230
275
|
const domainPayloadsMap = new Map();
|
|
276
|
+
// #region set up peer observable for any domainPayloadsMap
|
|
231
277
|
const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
|
|
232
278
|
const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
|
|
233
279
|
next: async (ibgib) => {
|
|
@@ -256,38 +302,45 @@ export class SyncSagaCoordinator {
|
|
|
256
302
|
await subscription.unsubscribe();
|
|
257
303
|
},
|
|
258
304
|
}));
|
|
259
|
-
//
|
|
305
|
+
// #endregion set up peer observable for any domainPayloadsMap
|
|
306
|
+
// ...create/compose the Request Context itself...
|
|
260
307
|
const requestCtx = await createSyncSagaContext({
|
|
261
308
|
sagaFrame: currentFrame,
|
|
262
309
|
sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
|
|
263
310
|
/**
|
|
264
311
|
* init frame: empty
|
|
265
|
-
*
|
|
312
|
+
* ack frame: possible push offers
|
|
313
|
+
* delta frame: requested ibgibs or commit offer
|
|
314
|
+
* commit frame: empty
|
|
266
315
|
*/
|
|
267
|
-
payloadIbGibsDomain:
|
|
316
|
+
payloadIbGibsDomain: nextDomainIbGibs,
|
|
268
317
|
localSpace,
|
|
269
318
|
});
|
|
270
|
-
// Log what we're sending
|
|
319
|
+
// #region Log what we're sending
|
|
271
320
|
if (logalotControlDomain) {
|
|
272
|
-
const domainAddrs =
|
|
273
|
-
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I:
|
|
321
|
+
const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
322
|
+
console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
|
|
274
323
|
console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
|
|
275
324
|
console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
276
325
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
|
|
277
326
|
}
|
|
327
|
+
// #endregion Log what we're sending
|
|
278
328
|
// update our saga listeners...
|
|
279
329
|
// if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
|
|
280
|
-
updates$.next(requestCtx); // spins off
|
|
281
|
-
// ...And send the context
|
|
330
|
+
updates$.next(requestCtx); // spins off for saga UI updates
|
|
331
|
+
// ...And send the context.
|
|
332
|
+
peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
|
|
282
333
|
const responseCtx = await peer.witness(requestCtx);
|
|
283
|
-
//
|
|
334
|
+
// the send returned, but a peer can return a falsy responseCtx, if
|
|
335
|
+
// we just sent a commit to them and they're done. If so, there will
|
|
336
|
+
// necessarily be no payload to wait to receive
|
|
284
337
|
if (!responseCtx) {
|
|
285
338
|
if (currentFrame) {
|
|
286
339
|
// Check for Commit (Peer silence expected)
|
|
287
340
|
const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
|
|
288
341
|
if (msg?.data?.stage === SyncStage.commit) {
|
|
289
342
|
if (logalot) {
|
|
290
|
-
console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete
|
|
343
|
+
console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`);
|
|
291
344
|
}
|
|
292
345
|
currentFrame = null;
|
|
293
346
|
break;
|
|
@@ -300,19 +353,23 @@ export class SyncSagaCoordinator {
|
|
|
300
353
|
throw new Error(`(UNEXPECTED) no response and currentFrame falsy? (E: 8d1085ea2f28cfc3f9c922649864a826)`);
|
|
301
354
|
}
|
|
302
355
|
}
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
// ---------------------------------------------------------------------
|
|
306
|
-
// at this point, we have received the response context ibgib, but
|
|
356
|
+
// at this point, we did indeed receive a response to analyze. BUT!
|
|
357
|
+
// we have only necessarily received the response context ibgib.
|
|
307
358
|
// if there were payloads expected to be transferred, they may not
|
|
308
359
|
// be done yet.
|
|
309
360
|
if (!responseCtx.data) {
|
|
310
361
|
throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
|
|
311
362
|
}
|
|
312
|
-
updates$.next(responseCtx); // spins off
|
|
363
|
+
updates$.next(responseCtx); // spins off for saga UI updating
|
|
364
|
+
// immediately validate context/saga frame (but not payloads because
|
|
365
|
+
// we may not have those yet)
|
|
366
|
+
const contextAndSagaFrameValidationErrors = await validateContextAndSagaFrame({ context: responseCtx });
|
|
367
|
+
if (contextAndSagaFrameValidationErrors.length > 0) {
|
|
368
|
+
throw new Error(`contextAndSagaFrameValidationErrors: ${contextAndSagaFrameValidationErrors} (E: 6eebe8e7fa437c00a8cde3ada3c66826)`);
|
|
369
|
+
}
|
|
313
370
|
// Extract expected domain addresses from response context
|
|
314
371
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
|
|
315
|
-
// Poll for them if needed
|
|
372
|
+
// Poll for them if needed. see above jsdocs for domainPayloadsMap
|
|
316
373
|
if (responsePayloadAddrsDomain.length > 0) {
|
|
317
374
|
responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
|
|
318
375
|
expectedAddrs: responsePayloadAddrsDomain,
|
|
@@ -321,49 +378,62 @@ export class SyncSagaCoordinator {
|
|
|
321
378
|
tempSpace,
|
|
322
379
|
});
|
|
323
380
|
}
|
|
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
|
|
381
|
+
// #region Log what we received back
|
|
328
382
|
if (!responseCtx.sagaFrame) {
|
|
329
383
|
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
384
|
}
|
|
331
385
|
if (logalotControlDomain) {
|
|
332
386
|
const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] || [];
|
|
333
|
-
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I:
|
|
387
|
+
console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
|
|
334
388
|
console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
|
|
335
389
|
console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
|
|
336
390
|
console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
|
|
337
391
|
console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
|
|
338
392
|
}
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
|
|
393
|
+
// #endregion Log what we received back
|
|
394
|
+
// at this point, we have received the context AND **all** of the
|
|
395
|
+
// domain payloads (if applicable), so we are ready to do the next
|
|
396
|
+
// iteration in the saga loop
|
|
397
|
+
// this is the part that drives the FSM forward, i.e., when we
|
|
398
|
+
// evolve the sync saga ibgib itself to the next frame if we aren't
|
|
399
|
+
// finished/errored out.
|
|
400
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
346
401
|
sagaContext: responseCtx,
|
|
402
|
+
initDomainGraph,
|
|
347
403
|
mySpace: localSpace,
|
|
348
404
|
myTempSpace: tempSpace,
|
|
349
405
|
metaspace,
|
|
350
406
|
});
|
|
351
|
-
if (!
|
|
407
|
+
if (!contextResult) {
|
|
352
408
|
if (logalot) {
|
|
353
|
-
console.log(`${lc} Handler returned null (Saga End)
|
|
409
|
+
console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`);
|
|
354
410
|
}
|
|
355
411
|
break;
|
|
356
412
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
413
|
+
// #region error conditions throw
|
|
414
|
+
if (contextResult.errorMsg) {
|
|
415
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
|
|
416
|
+
}
|
|
417
|
+
else if (!contextResult.nextFrameInfo) {
|
|
418
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
419
|
+
}
|
|
420
|
+
else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
421
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
422
|
+
}
|
|
423
|
+
// #endregion error conditions throw
|
|
424
|
+
// we have another frame to process and send out, with possibly
|
|
425
|
+
// payload domain ibgibs as well
|
|
426
|
+
const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
|
|
427
|
+
currentFrame = frame;
|
|
428
|
+
nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
|
|
429
|
+
// #region Log handler output for next iteration
|
|
361
430
|
if (logalotControlDomain) {
|
|
362
431
|
const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
|
|
363
|
-
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I:
|
|
432
|
+
console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
|
|
364
433
|
console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
|
|
365
434
|
console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
|
|
366
435
|
}
|
|
436
|
+
// #endregion Log handler output for next iteration
|
|
367
437
|
}
|
|
368
438
|
}
|
|
369
439
|
/**
|
|
@@ -423,7 +493,7 @@ export class SyncSagaCoordinator {
|
|
|
423
493
|
}
|
|
424
494
|
const res = await getLatestAddrs({ space, tjpAddrs: tjps });
|
|
425
495
|
if (!res.data || !res.data.latestAddrsMap) {
|
|
426
|
-
throw new Error(`${lc} Failed to get latest addrs. (E:
|
|
496
|
+
throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
|
|
427
497
|
}
|
|
428
498
|
// if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
|
|
429
499
|
return res.data.latestAddrsMap;
|
|
@@ -477,7 +547,7 @@ export class SyncSagaCoordinator {
|
|
|
477
547
|
}
|
|
478
548
|
// Analyze Timelines & Stones
|
|
479
549
|
const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
|
|
480
|
-
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
550
|
+
// if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
|
|
481
551
|
const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
|
|
482
552
|
// we need to store the fullGraph in our tempSpace for later...
|
|
483
553
|
await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
|
|
@@ -516,7 +586,7 @@ export class SyncSagaCoordinator {
|
|
|
516
586
|
localSpace,
|
|
517
587
|
});
|
|
518
588
|
// if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
|
|
519
|
-
return { sagaFrame,
|
|
589
|
+
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
520
590
|
}
|
|
521
591
|
catch (error) {
|
|
522
592
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -588,6 +658,7 @@ export class SyncSagaCoordinator {
|
|
|
588
658
|
}
|
|
589
659
|
/**
|
|
590
660
|
* This is the heart of the "ping pong" transaction, where we send a context
|
|
661
|
+
*
|
|
591
662
|
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
592
663
|
* a whole.
|
|
593
664
|
*
|
|
@@ -606,8 +677,8 @@ export class SyncSagaCoordinator {
|
|
|
606
677
|
*
|
|
607
678
|
* This is a one-off on the receiver.
|
|
608
679
|
*/
|
|
609
|
-
async
|
|
610
|
-
const lc = `${this.lc}[${this.
|
|
680
|
+
async handleResponseSagaContext({ sagaContext, initDomainGraph, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
681
|
+
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
611
682
|
try {
|
|
612
683
|
if (logalot) {
|
|
613
684
|
console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`);
|
|
@@ -628,9 +699,10 @@ export class SyncSagaCoordinator {
|
|
|
628
699
|
* don't like this name, need to refactor
|
|
629
700
|
*/
|
|
630
701
|
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
702
|
+
let nextFrameInfo;
|
|
631
703
|
switch (stage) {
|
|
632
704
|
case SyncStage.init:
|
|
633
|
-
|
|
705
|
+
nextFrameInfo = await this.handleInitFrame({
|
|
634
706
|
sagaIbGib,
|
|
635
707
|
messageData: messageData,
|
|
636
708
|
metaspace,
|
|
@@ -639,19 +711,36 @@ export class SyncSagaCoordinator {
|
|
|
639
711
|
identity,
|
|
640
712
|
identitySecret
|
|
641
713
|
});
|
|
714
|
+
break;
|
|
642
715
|
case SyncStage.ack:
|
|
643
|
-
|
|
716
|
+
if (!initDomainGraph) {
|
|
717
|
+
throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`);
|
|
718
|
+
}
|
|
719
|
+
nextFrameInfo = await this.handleAckFrame({
|
|
720
|
+
sagaIbGib,
|
|
721
|
+
srcGraph,
|
|
722
|
+
initDomainGraph,
|
|
723
|
+
metaspace,
|
|
724
|
+
destSpace: mySpace,
|
|
725
|
+
tempSpace: myTempSpace,
|
|
726
|
+
identity,
|
|
727
|
+
});
|
|
728
|
+
break;
|
|
644
729
|
case SyncStage.delta:
|
|
645
|
-
|
|
730
|
+
nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
731
|
+
break;
|
|
646
732
|
case SyncStage.commit:
|
|
647
|
-
|
|
733
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
|
|
734
|
+
break;
|
|
648
735
|
default:
|
|
649
736
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
650
737
|
}
|
|
738
|
+
return { errorMsg: undefined, nextFrameInfo, };
|
|
651
739
|
}
|
|
652
740
|
catch (error) {
|
|
653
|
-
|
|
654
|
-
|
|
741
|
+
const errorMsg = `${lc} ${extractErrorMsg(error)}`;
|
|
742
|
+
console.error(errorMsg);
|
|
743
|
+
return { errorMsg };
|
|
655
744
|
}
|
|
656
745
|
finally {
|
|
657
746
|
if (logalot) {
|
|
@@ -680,14 +769,11 @@ export class SyncSagaCoordinator {
|
|
|
680
769
|
if (logalot) {
|
|
681
770
|
console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`);
|
|
682
771
|
}
|
|
683
|
-
console.log(`${lc} [TEST DEBUG]
|
|
684
|
-
if (logalot) {
|
|
685
|
-
console.log(`${lc} starting...`);
|
|
686
|
-
}
|
|
772
|
+
console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
|
|
687
773
|
// Extract Init Data
|
|
688
774
|
const initData = messageData; // Using renamed variable for clarity
|
|
689
775
|
if (initData.stage !== SyncStage.init) {
|
|
690
|
-
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E:
|
|
776
|
+
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
|
|
691
777
|
}
|
|
692
778
|
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
693
779
|
if (!initData || !initData.knowledgeVector) {
|
|
@@ -697,8 +783,8 @@ export class SyncSagaCoordinator {
|
|
|
697
783
|
const conflictStrategy = sagaIbGib.data.conflictStrategy || SyncConflictStrategy.abort;
|
|
698
784
|
// 2. Gap Analysis
|
|
699
785
|
const conflicts = [];
|
|
700
|
-
const
|
|
701
|
-
const
|
|
786
|
+
const deltaRequestAddrInfos = [];
|
|
787
|
+
const pushOfferInfos = [];
|
|
702
788
|
// First do the consant stones (Non-TJPs)
|
|
703
789
|
const stones = initData.stones || [];
|
|
704
790
|
if (stones.length > 0) {
|
|
@@ -717,14 +803,14 @@ export class SyncSagaCoordinator {
|
|
|
717
803
|
console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`);
|
|
718
804
|
}
|
|
719
805
|
addrsNotFound.forEach(addr => {
|
|
720
|
-
if (!
|
|
721
|
-
|
|
806
|
+
if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
|
|
807
|
+
deltaRequestAddrInfos.push({ addr }); // no tjpAddr
|
|
722
808
|
}
|
|
723
809
|
});
|
|
724
810
|
}
|
|
725
811
|
}
|
|
726
812
|
/**
|
|
727
|
-
* "remote"
|
|
813
|
+
* "remote" is sender in this case
|
|
728
814
|
*/
|
|
729
815
|
const remoteKV = initData.knowledgeVector;
|
|
730
816
|
if (logalot) {
|
|
@@ -736,7 +822,7 @@ export class SyncSagaCoordinator {
|
|
|
736
822
|
console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`);
|
|
737
823
|
}
|
|
738
824
|
// 1. Get Local Latest Addrs for all TJPs
|
|
739
|
-
let
|
|
825
|
+
let localLatestAddrsMap = {};
|
|
740
826
|
if (remoteTjps.length > 0) {
|
|
741
827
|
// Batch get latest addrs for the TJPs
|
|
742
828
|
const resGetLatestAddrs = await getLatestAddrs({
|
|
@@ -749,52 +835,88 @@ export class SyncSagaCoordinator {
|
|
|
749
835
|
if (!resGetLatestAddrs.data.latestAddrsMap) {
|
|
750
836
|
throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`);
|
|
751
837
|
}
|
|
752
|
-
|
|
753
|
-
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(
|
|
838
|
+
localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
|
|
839
|
+
console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
|
|
754
840
|
if (logalot) {
|
|
755
|
-
console.log(`${lc} localKV: ${pretty(
|
|
841
|
+
console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`);
|
|
756
842
|
}
|
|
757
843
|
}
|
|
758
844
|
// 2. Gap Analysis
|
|
759
845
|
for (const tjp of remoteTjps) {
|
|
760
846
|
const remoteAddr = remoteKV[tjp];
|
|
761
|
-
const localAddr =
|
|
847
|
+
const localAddr = localLatestAddrsMap[tjp];
|
|
762
848
|
if (!localAddr) {
|
|
763
849
|
// We (Receiver) don't have this timeline at all. Request it.
|
|
764
850
|
console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
|
|
765
|
-
|
|
851
|
+
deltaRequestAddrInfos.push({
|
|
852
|
+
addr: remoteAddr,
|
|
853
|
+
tjpAddr: tjp,
|
|
854
|
+
// we don't have this timeline at all
|
|
855
|
+
// latestAddrAlreadyHave: undefined
|
|
856
|
+
});
|
|
766
857
|
continue;
|
|
767
858
|
}
|
|
859
|
+
// we do have this timeline...
|
|
768
860
|
if (localAddr === remoteAddr) {
|
|
769
|
-
//
|
|
861
|
+
// ...already synced
|
|
770
862
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
|
|
771
863
|
continue;
|
|
772
864
|
}
|
|
773
865
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
|
|
774
|
-
//
|
|
775
|
-
//
|
|
776
|
-
|
|
866
|
+
// we have this timeline but it's not synced...
|
|
867
|
+
// We're executing on receiver. Check if Remote (Sender) is in
|
|
868
|
+
// our past, and if so, we are ahead and need to push the delta
|
|
869
|
+
// to remote
|
|
870
|
+
const remoteIsInPast = await isPastFrame({
|
|
777
871
|
olderAddr: remoteAddr,
|
|
778
872
|
newerAddr: localAddr,
|
|
779
873
|
space: mySpace,
|
|
780
874
|
});
|
|
781
|
-
if (
|
|
782
|
-
|
|
783
|
-
|
|
875
|
+
if (remoteIsInPast) {
|
|
876
|
+
// we're ahead, so push the delta of what the sender doesn't
|
|
877
|
+
// have (we have full knowledge)
|
|
878
|
+
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
|
|
879
|
+
const deltaGraph = await getDeltaDependencyGraph({
|
|
880
|
+
ibGibAddr: localAddr,
|
|
881
|
+
live: false, // always live: false right?
|
|
882
|
+
latestCommonFrameAddr: remoteAddr,
|
|
883
|
+
space: mySpace,
|
|
884
|
+
});
|
|
885
|
+
pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
|
|
784
886
|
}
|
|
785
887
|
else {
|
|
786
|
-
// Remote is not in our past.
|
|
787
|
-
//
|
|
888
|
+
// Remote tip is not in our past. So, either Remote is
|
|
889
|
+
// ahead (we're in THEIR past, i.e., Fast-Backward) OR
|
|
890
|
+
// Diverged/conflict (we've both made edits).
|
|
788
891
|
// Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
892
|
+
/**
|
|
893
|
+
* we could first check for existence of remoteAddr in
|
|
894
|
+
* mySpace, but this is quick and easy. If it throws, we
|
|
895
|
+
* don't have it so it can't be in the past.
|
|
896
|
+
*/
|
|
897
|
+
let localIsInPast = false;
|
|
898
|
+
try {
|
|
899
|
+
// we could
|
|
900
|
+
localIsInPast = await isPastFrame({
|
|
901
|
+
olderAddr: localAddr,
|
|
902
|
+
newerAddr: remoteAddr,
|
|
903
|
+
space: mySpace,
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
catch (error) {
|
|
907
|
+
// couldn't get one of them, so localIsInPast remains false.
|
|
908
|
+
if (logalot) {
|
|
909
|
+
console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (localIsInPast) {
|
|
795
913
|
// Fast-Forward: We update to remote's tip.
|
|
796
914
|
console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
|
|
797
|
-
|
|
915
|
+
deltaRequestAddrInfos.push({
|
|
916
|
+
addr: remoteAddr,
|
|
917
|
+
tjpAddr: tjp,
|
|
918
|
+
latestAddrAlreadyHave: localAddr,
|
|
919
|
+
});
|
|
798
920
|
}
|
|
799
921
|
else {
|
|
800
922
|
// DIVERGENCE: Both have changes the other doesn't know about.
|
|
@@ -814,7 +936,7 @@ export class SyncSagaCoordinator {
|
|
|
814
936
|
});
|
|
815
937
|
}
|
|
816
938
|
else if (conflictStrategy === 'optimistic') {
|
|
817
|
-
// Optimistic: We want
|
|
939
|
+
// Optimistic: We want resolve this.
|
|
818
940
|
// We need to send our history to the Sender so they can Merge.
|
|
819
941
|
// Fetch Full History for Local Timeline
|
|
820
942
|
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
@@ -825,11 +947,11 @@ export class SyncSagaCoordinator {
|
|
|
825
947
|
// We have localAddr.
|
|
826
948
|
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
827
949
|
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
828
|
-
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E:
|
|
950
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
829
951
|
}
|
|
830
952
|
const localTip = resLocalTip.ibGibs[0];
|
|
831
953
|
if (!localTip) {
|
|
832
|
-
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E:
|
|
954
|
+
throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`);
|
|
833
955
|
}
|
|
834
956
|
const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
|
|
835
957
|
conflicts.push({
|
|
@@ -842,7 +964,7 @@ export class SyncSagaCoordinator {
|
|
|
842
964
|
});
|
|
843
965
|
}
|
|
844
966
|
else {
|
|
845
|
-
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E:
|
|
967
|
+
throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
|
|
846
968
|
}
|
|
847
969
|
}
|
|
848
970
|
}
|
|
@@ -856,51 +978,17 @@ export class SyncSagaCoordinator {
|
|
|
856
978
|
}
|
|
857
979
|
throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
|
|
858
980
|
}
|
|
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
981
|
// 3. Create Ack Frame
|
|
982
|
+
if (!sagaIbGib.data) {
|
|
983
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`);
|
|
984
|
+
}
|
|
896
985
|
const sagaId = sagaIbGib.data.uuid;
|
|
897
986
|
// Create Payload Stone
|
|
898
987
|
const ackData = {
|
|
899
988
|
sagaId,
|
|
900
989
|
stage: SyncStage.ack,
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
knowledgeVector,
|
|
990
|
+
deltaRequestAddrInfos,
|
|
991
|
+
pushOfferInfos,
|
|
904
992
|
};
|
|
905
993
|
if (conflicts?.length > 0) {
|
|
906
994
|
ackData.conflicts = conflicts;
|
|
@@ -926,13 +1014,65 @@ export class SyncSagaCoordinator {
|
|
|
926
1014
|
* we want to push ibgibs to the remote/sender if we have push
|
|
927
1015
|
* offers. an ack frame's payloads, if any, are those push offers
|
|
928
1016
|
*/
|
|
929
|
-
|
|
930
|
-
if (
|
|
1017
|
+
let payloadIbGibsDomain;
|
|
1018
|
+
if (pushOfferInfos.length > 0) {
|
|
1019
|
+
const searchSecondSpaceAddrs = [];
|
|
1020
|
+
payloadIbGibsDomain = [];
|
|
1021
|
+
const domainAddrs = pushOfferInfos.flatMap(x => x.addrs);
|
|
1022
|
+
const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
|
|
1023
|
+
const resGetRawData = resGet.rawResultIbGib?.data;
|
|
1024
|
+
if (!resGetRawData) {
|
|
1025
|
+
throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`);
|
|
1026
|
+
}
|
|
1027
|
+
const addrsNotFound = resGetRawData.addrsNotFound ?? [];
|
|
1028
|
+
const addrsErrored = resGetRawData.addrsErrored ?? [];
|
|
1029
|
+
if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
|
|
1030
|
+
// found all of them
|
|
1031
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain.push(x); });
|
|
1032
|
+
}
|
|
1033
|
+
else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
|
|
1034
|
+
// found some of them
|
|
1035
|
+
resGet.ibGibs.forEach(x => { payloadIbGibsDomain.push(x); });
|
|
1036
|
+
const foundPlusNotFoundCount = payloadIbGibsDomain.length +
|
|
1037
|
+
addrsNotFound.length +
|
|
1038
|
+
addrsErrored.length;
|
|
1039
|
+
if (foundPlusNotFoundCount === domainAddrs.length) {
|
|
1040
|
+
// we can still conceivably get the remaining addrs
|
|
1041
|
+
addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1042
|
+
addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1043
|
+
}
|
|
1044
|
+
else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
|
|
1045
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
|
|
1046
|
+
}
|
|
1047
|
+
else {
|
|
1048
|
+
throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
// found none of them(?)
|
|
1053
|
+
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)`);
|
|
1054
|
+
domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
|
|
1055
|
+
}
|
|
1056
|
+
if (searchSecondSpaceAddrs.length > 0) {
|
|
1057
|
+
if (logalot) {
|
|
1058
|
+
console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`);
|
|
1059
|
+
}
|
|
1060
|
+
const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
|
|
1061
|
+
if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
|
|
1062
|
+
// got them all the second try
|
|
1063
|
+
resGet2.ibGibs.forEach(x => payloadIbGibsDomain.push(x));
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
resGet2.ibGibs?.forEach(x => payloadIbGibsDomain.push(x));
|
|
1067
|
+
const addrsFailed = domainAddrs.filter(x => !payloadIbGibsDomain.map(pid => getIbGibAddr({ ibGib: pid })).includes(x));
|
|
1068
|
+
throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
// we have now populated payloadIbGibsDomain
|
|
931
1072
|
}
|
|
932
1073
|
return {
|
|
933
1074
|
frame: ackFrame,
|
|
934
|
-
|
|
935
|
-
// payloadIbGibsDomain,
|
|
1075
|
+
payloadIbGibsDomain,
|
|
936
1076
|
};
|
|
937
1077
|
}
|
|
938
1078
|
catch (error) {
|
|
@@ -957,7 +1097,7 @@ export class SyncSagaCoordinator {
|
|
|
957
1097
|
*
|
|
958
1098
|
* Returns a `Delta` frame.
|
|
959
1099
|
*/
|
|
960
|
-
async handleAckFrame({ sagaIbGib, srcGraph, destSpace, tempSpace, metaspace, identity, }) {
|
|
1100
|
+
async handleAckFrame({ sagaIbGib, srcGraph, initDomainGraph, destSpace, tempSpace, metaspace, identity, }) {
|
|
961
1101
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
962
1102
|
try {
|
|
963
1103
|
if (logalot) {
|
|
@@ -985,12 +1125,12 @@ export class SyncSagaCoordinator {
|
|
|
985
1125
|
console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
|
|
986
1126
|
// Terminal failure. Sender should probably Commit(Fail) or just Abort.
|
|
987
1127
|
// For now, throw to trigger abort.
|
|
988
|
-
throw new Error(`${lc} Peer reported terminal conflicts. (E:
|
|
1128
|
+
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
989
1129
|
}
|
|
990
|
-
|
|
1130
|
+
// at this point, if we have conflicts, they are non-terminal
|
|
991
1131
|
const mergeDeltaReqs = []; // Additional requests for merging
|
|
992
|
-
if (
|
|
993
|
-
console.log(`${lc} [CONFLICT DEBUG] Processing ${
|
|
1132
|
+
if (conflicts.length > 0) {
|
|
1133
|
+
console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
|
|
994
1134
|
// We need to resolve these.
|
|
995
1135
|
// Strategy:
|
|
996
1136
|
// 1. Analyze Divergence (Sender vs Receiver)
|
|
@@ -1007,7 +1147,7 @@ export class SyncSagaCoordinator {
|
|
|
1007
1147
|
// Or can we send a 'Delta Request' frame?
|
|
1008
1148
|
// Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
|
|
1009
1149
|
// If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
|
|
1010
|
-
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1150
|
+
// Or we just proceed to Delta (sending what Receiver wants),
|
|
1011
1151
|
// AND we piggyback our own requests?
|
|
1012
1152
|
// OR: We treat the Conflict Resolution as a sub-saga or side-effect?
|
|
1013
1153
|
// SIMPLIFICATION for V1:
|
|
@@ -1016,138 +1156,134 @@ export class SyncSagaCoordinator {
|
|
|
1016
1156
|
// `peer.pull(addr)`?
|
|
1017
1157
|
// Yes! The Coordinator has the `peer`.
|
|
1018
1158
|
// Let's analyze and pull immediately.
|
|
1019
|
-
for (const conflict of
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
//
|
|
1023
|
-
//
|
|
1024
|
-
//
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1029
|
-
//
|
|
1030
|
-
//
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
}
|
|
1036
|
-
// Basic Diff: Find what Receiver has that we don't.
|
|
1037
|
-
// Actually, we need to traverse OUR past to find commonality.
|
|
1038
|
-
const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
|
|
1039
|
-
const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
|
|
1040
|
-
if (receiverOnlyAddrs.length > 0) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1101
|
-
else {
|
|
1102
|
-
console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1103
|
-
}
|
|
1159
|
+
for (const conflict of conflicts) {
|
|
1160
|
+
// 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
|
|
1161
|
+
throw new Error(`conflicts not (re)implemented yet (E: 3b7d0819f83842a6de3ae988819bc826)`);
|
|
1162
|
+
// const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
|
|
1163
|
+
// // Sender History
|
|
1164
|
+
// // We need our own history for this timeline.
|
|
1165
|
+
// // We know the 'senderTip' (remoteAddr in Ack).
|
|
1166
|
+
// // Sender should verify it has this tip.
|
|
1167
|
+
// // Compute Diffs
|
|
1168
|
+
// // We need to find `receiverOnly` addrs.
|
|
1169
|
+
// // Receiver sent us `timelineAddrs` (Full History).
|
|
1170
|
+
// const receiverHistorySet = new Set(timelineAddrs);
|
|
1171
|
+
// // We need our execution context's history for this senderTip.
|
|
1172
|
+
// // We can fetch valid 'past' from space.
|
|
1173
|
+
// const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
|
|
1174
|
+
// const senderTipIbGib = resSenderTip.ibGibs?.[0];
|
|
1175
|
+
// if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
|
|
1176
|
+
// // Basic Diff: Find what Receiver has that we don't.
|
|
1177
|
+
// // Actually, we need to traverse OUR past to find commonality.
|
|
1178
|
+
// const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
|
|
1179
|
+
// const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
|
|
1180
|
+
// if (receiverOnlyAddrs.length > 0) {
|
|
1181
|
+
// console.log(`${lc} [CONFLICT DEBUG] Found ${receiverOnlyAddrs.length} receiver-only frames - need to pull for merge`);
|
|
1182
|
+
// console.log(`${lc} [CONFLICT DEBUG] Receiver-only addrs:`, receiverOnlyAddrs);
|
|
1183
|
+
// // PULL these frames from Peer into Local Space
|
|
1184
|
+
// // (Validation: We trust peer for now / verification happens on put)
|
|
1185
|
+
// for (const addr of receiverOnlyAddrs) {
|
|
1186
|
+
// console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
|
|
1187
|
+
// }
|
|
1188
|
+
// // Compute DELTA dependencies for each receiver-only frame
|
|
1189
|
+
// // Find LCA to determine what dependencies we already have
|
|
1190
|
+
// const lcaAddr = timelineAddrs.find(addr => senderHistory.includes(addr));
|
|
1191
|
+
// console.log(`${lc} [CONFLICT DEBUG] LCA: ${lcaAddr || 'NONE'}`);
|
|
1192
|
+
// const skipAddrsSet = new Set<string>();
|
|
1193
|
+
// if (lcaAddr) {
|
|
1194
|
+
// try {
|
|
1195
|
+
// const lcaRes = await getFromSpace({ addr: lcaAddr, space: destSpace });
|
|
1196
|
+
// const lcaIbGib = lcaRes.ibGibs?.[0];
|
|
1197
|
+
// if (lcaIbGib) {
|
|
1198
|
+
// const lcaDeps = await getDependencyGraph({ ibGib: lcaIbGib, space: destSpace });
|
|
1199
|
+
// if (lcaDeps) Object.keys(lcaDeps).forEach(a => skipAddrsSet.add(a));
|
|
1200
|
+
// console.log(`${lc} [CONFLICT DEBUG] LCA deps to skip: ${skipAddrsSet.size}`);
|
|
1201
|
+
// }
|
|
1202
|
+
// } catch (e) {
|
|
1203
|
+
// console.warn(`${lc} Error getting LCA deps: ${extractErrorMsg(e)}`);
|
|
1204
|
+
// }
|
|
1205
|
+
// }
|
|
1206
|
+
// // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
|
|
1207
|
+
// for (const addr of receiverOnlyAddrs) {
|
|
1208
|
+
// // Add the frame itself first
|
|
1209
|
+
// if (!mergeDeltaReqs.includes(addr)) {
|
|
1210
|
+
// mergeDeltaReqs.push(addr);
|
|
1211
|
+
// }
|
|
1212
|
+
// // Get the frame's delta dependencies (skip LCA's deps)
|
|
1213
|
+
// try {
|
|
1214
|
+
// const frameRes = await getFromSpace({ addr, space: destSpace });
|
|
1215
|
+
// const frameIbGib = frameRes.ibGibs?.[0];
|
|
1216
|
+
// if (frameIbGib) {
|
|
1217
|
+
// // Get dependency graph, skipping all LCA dependencies
|
|
1218
|
+
// const frameDeltaDeps = await getDependencyGraph({
|
|
1219
|
+
// ibGib: frameIbGib,
|
|
1220
|
+
// space: destSpace,
|
|
1221
|
+
// skipAddrs: Array.from(skipAddrsSet), // Skip entire LCA dep graph
|
|
1222
|
+
// });
|
|
1223
|
+
// if (frameDeltaDeps) {
|
|
1224
|
+
// // Add all delta dependencies (Object.keys gives us the addresses)
|
|
1225
|
+
// Object.keys(frameDeltaDeps).forEach(depAddr => {
|
|
1226
|
+
// if (!mergeDeltaReqs.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
|
|
1227
|
+
// mergeDeltaReqs.push(depAddr);
|
|
1228
|
+
// }
|
|
1229
|
+
// });
|
|
1230
|
+
// }
|
|
1231
|
+
// }
|
|
1232
|
+
// } catch (depError) {
|
|
1233
|
+
// console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1234
|
+
// }
|
|
1235
|
+
// }
|
|
1236
|
+
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${mergeDeltaReqs.length}`);
|
|
1237
|
+
// } else {
|
|
1238
|
+
// console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1239
|
+
// }
|
|
1104
1240
|
}
|
|
1105
|
-
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${
|
|
1241
|
+
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. mergeDeltaReqs: ${mergeDeltaReqs.length}`);
|
|
1106
1242
|
}
|
|
1107
1243
|
else {
|
|
1108
1244
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1109
1245
|
}
|
|
1110
1246
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1111
|
-
const deltaReqAddrs = ackData.
|
|
1112
|
-
const pushOfferAddrs = ackData.
|
|
1247
|
+
const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
|
|
1248
|
+
const pushOfferAddrs = ackData.pushOfferInfos || [];
|
|
1113
1249
|
// 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
|
|
1114
1250
|
const pullReqAddrs = [];
|
|
1115
1251
|
for (const addr of pushOfferAddrs) {
|
|
1116
|
-
const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1117
|
-
if (!existing) {
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1252
|
+
// const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
|
|
1253
|
+
// if (!existing) {
|
|
1254
|
+
// pullReqAddrs.push(addr);
|
|
1255
|
+
// }
|
|
1120
1256
|
}
|
|
1121
1257
|
// 2. Process Delta Requests (Push Payload)
|
|
1122
1258
|
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1259
|
+
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1123
1260
|
const skipAddrs = new Set();
|
|
1124
1261
|
if (ackData.knowledgeVector) {
|
|
1125
1262
|
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1126
|
-
addrs.forEach(a => skipAddrs.add(a));
|
|
1263
|
+
// addrs.forEach(a => skipAddrs.add(a));
|
|
1127
1264
|
});
|
|
1128
1265
|
}
|
|
1129
1266
|
const payloadIbGibs = [];
|
|
1130
1267
|
// Gather all tips to sync first
|
|
1131
1268
|
const tipsToSync = [];
|
|
1132
1269
|
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
|
-
}
|
|
1270
|
+
// let ibGib = srcGraph[addr];
|
|
1271
|
+
// if (!ibGib) {
|
|
1272
|
+
// const res = await getFromSpace({ addr, space: destSpace });
|
|
1273
|
+
// if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1274
|
+
// ibGib = res.ibGibs[0];
|
|
1275
|
+
// }
|
|
1276
|
+
// }
|
|
1277
|
+
// if (ibGib) {
|
|
1278
|
+
// tipsToSync.push(ibGib);
|
|
1279
|
+
// } else {
|
|
1280
|
+
// throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
|
|
1281
|
+
// }
|
|
1146
1282
|
}
|
|
1147
1283
|
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1148
1284
|
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1149
1285
|
// `getDependencyGraph` takes a single ibGib.
|
|
1150
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1286
|
+
// We can optimize by doing it for each tip and unioning the result?
|
|
1151
1287
|
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1152
1288
|
// We will loop.
|
|
1153
1289
|
const allDepsSet = new Set();
|
|
@@ -1219,7 +1355,8 @@ export class SyncSagaCoordinator {
|
|
|
1219
1355
|
payloadIbGibsControl.push(identity);
|
|
1220
1356
|
}
|
|
1221
1357
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
|
|
1222
|
-
return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1358
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1359
|
+
throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
|
|
1223
1360
|
}
|
|
1224
1361
|
catch (error) {
|
|
1225
1362
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1255,7 +1392,7 @@ export class SyncSagaCoordinator {
|
|
|
1255
1392
|
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1256
1393
|
}
|
|
1257
1394
|
if (logalot) {
|
|
1258
|
-
console.log(`${lc} deltaData: ${pretty(deltaData)} (I:
|
|
1395
|
+
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1259
1396
|
}
|
|
1260
1397
|
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1261
1398
|
const payloadAddrs = deltaData.payloadAddrs || [];
|
|
@@ -1264,8 +1401,8 @@ export class SyncSagaCoordinator {
|
|
|
1264
1401
|
// 1. Process Received Payload (Ingest)
|
|
1265
1402
|
const receivedPayloadIbGibs = [];
|
|
1266
1403
|
if (payloadAddrs.length > 0) {
|
|
1267
|
-
// We use `payloadAddrs` as the manifest.
|
|
1268
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1404
|
+
// We use `payloadAddrs` as the manifest.
|
|
1405
|
+
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1269
1406
|
// assuming the "Transport" layer put them there implicitly?
|
|
1270
1407
|
// OR, if we are local-only, we just get them.
|
|
1271
1408
|
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
@@ -1290,7 +1427,7 @@ export class SyncSagaCoordinator {
|
|
|
1290
1427
|
// Get the requested ibGib
|
|
1291
1428
|
let ibGib = srcGraph[addr];
|
|
1292
1429
|
if (!ibGib) {
|
|
1293
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1430
|
+
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1294
1431
|
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1295
1432
|
ibGib = res.ibGibs[0];
|
|
1296
1433
|
}
|
|
@@ -1376,7 +1513,7 @@ export class SyncSagaCoordinator {
|
|
|
1376
1513
|
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1377
1514
|
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1378
1515
|
// We have the tip!
|
|
1379
|
-
// Do we have the full history?
|
|
1516
|
+
// Do we have the full history?
|
|
1380
1517
|
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1381
1518
|
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1382
1519
|
// Perform Merge!
|
|
@@ -1420,9 +1557,9 @@ export class SyncSagaCoordinator {
|
|
|
1420
1557
|
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1421
1558
|
requests: hasMyRequests ? myRequests : undefined,
|
|
1422
1559
|
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.
|
|
1560
|
+
// Wait. If we send data, we are NOT committing yet.
|
|
1424
1561
|
// We are sending data. The OTHER side must ingest it.
|
|
1425
|
-
// So proposeCommit = true?
|
|
1562
|
+
// So proposeCommit = true?
|
|
1426
1563
|
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1427
1564
|
// Yes.
|
|
1428
1565
|
};
|
|
@@ -1448,7 +1585,8 @@ export class SyncSagaCoordinator {
|
|
|
1448
1585
|
payloadIbGibsControl.push(identity);
|
|
1449
1586
|
}
|
|
1450
1587
|
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1451
|
-
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1588
|
+
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1589
|
+
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1452
1590
|
}
|
|
1453
1591
|
else {
|
|
1454
1592
|
// We have nothing to send.
|
|
@@ -1477,7 +1615,8 @@ export class SyncSagaCoordinator {
|
|
|
1477
1615
|
commitCtrlPayloads.push(identity);
|
|
1478
1616
|
}
|
|
1479
1617
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1480
|
-
return { frame: commitFrame, };
|
|
1618
|
+
// return { frame: commitFrame, };
|
|
1619
|
+
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1481
1620
|
}
|
|
1482
1621
|
else {
|
|
1483
1622
|
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
@@ -1531,7 +1670,8 @@ export class SyncSagaCoordinator {
|
|
|
1531
1670
|
commitCtrlPayloads2.push(identity);
|
|
1532
1671
|
}
|
|
1533
1672
|
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
|
|
1534
|
-
return { frame: commitFrame, };
|
|
1673
|
+
// return { frame: commitFrame, };
|
|
1674
|
+
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1535
1675
|
}
|
|
1536
1676
|
// Build control payloads for delta propose
|
|
1537
1677
|
const deltaCtrlPayloads = [deltaFrame, deltaStone];
|
|
@@ -1539,7 +1679,8 @@ export class SyncSagaCoordinator {
|
|
|
1539
1679
|
deltaCtrlPayloads.push(identity);
|
|
1540
1680
|
}
|
|
1541
1681
|
// return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
|
|
1542
|
-
return { frame: deltaFrame, };
|
|
1682
|
+
// return { frame: deltaFrame, };
|
|
1683
|
+
throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
|
|
1543
1684
|
}
|
|
1544
1685
|
}
|
|
1545
1686
|
}
|
|
@@ -1559,7 +1700,8 @@ export class SyncSagaCoordinator {
|
|
|
1559
1700
|
if (logalot) {
|
|
1560
1701
|
console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
|
|
1561
1702
|
}
|
|
1562
|
-
return
|
|
1703
|
+
// return { responseWasNull: true };
|
|
1704
|
+
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1563
1705
|
}
|
|
1564
1706
|
// #endregion Handlers
|
|
1565
1707
|
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|
|
@@ -1633,7 +1775,7 @@ export class SyncSagaCoordinator {
|
|
|
1633
1775
|
if (sessionIdentity) {
|
|
1634
1776
|
rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
|
|
1635
1777
|
}
|
|
1636
|
-
// remove the existing sync msg stones' addrs
|
|
1778
|
+
// remove the existing sync msg stones' addrs
|
|
1637
1779
|
if (!prevSagaIbGib.rel8ns) {
|
|
1638
1780
|
throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`);
|
|
1639
1781
|
}
|