@ibgib/core-gib 0.1.26 → 0.1.28
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/sync/sync-helpers.d.mts +23 -2
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +121 -4
- package/dist/sync/sync-helpers.mjs.map +1 -1
- 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 +30 -9
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +1 -0
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -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 +17 -3
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +8 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +30 -153
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +585 -406
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +13 -18
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +36 -6
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +32 -0
- package/dist/sync/sync-types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/sync/sync-helpers.mts +113 -6
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +30 -8
- package/src/sync/sync-peer/sync-peer-v1.mts +2 -0
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +14 -2
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +13 -5
- package/src/sync/sync-saga-coordinator.mts +656 -463
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +27 -32
- package/src/sync/sync-types.mts +45 -8
|
@@ -3,10 +3,10 @@ pretty, delay, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
|
3
3
|
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
4
4
|
import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
|
|
5
5
|
import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space-helper.mjs";
|
|
6
|
-
import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
|
|
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
8
|
import { SyncConflictStrategy, SyncMode, SYNC_CONFLICT_STRATEGY_VALID_VALUES, } from "./sync-types.mjs";
|
|
9
|
-
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
9
|
+
import { getFullSyncSagaHistory, getSyncIb, getTempSpaceName, isPastFrame, putInSpace_dnasThenNonDnas, validateFullSyncSagaHistory } from "./sync-helpers.mjs";
|
|
10
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";
|
|
@@ -17,7 +17,7 @@ import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
|
|
|
17
17
|
import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
18
18
|
import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
|
|
19
19
|
// const logalot = GLOBAL_LOG_A_LOT || true;
|
|
20
|
-
const logalot =
|
|
20
|
+
const logalot = true;
|
|
21
21
|
const logalotControlDomain = true;
|
|
22
22
|
const lcControlDomain = '[ControlDomain]';
|
|
23
23
|
/**
|
|
@@ -153,8 +153,8 @@ export class SyncSagaCoordinator {
|
|
|
153
153
|
* @returns next context result if another round, else if commit returns
|
|
154
154
|
* null
|
|
155
155
|
*/
|
|
156
|
-
async
|
|
157
|
-
const lc = `${this.lc}[${this.
|
|
156
|
+
async continueSync({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
157
|
+
const lc = `${this.lc}[${this.continueSync.name}]`;
|
|
158
158
|
try {
|
|
159
159
|
if (logalot) {
|
|
160
160
|
console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`);
|
|
@@ -171,7 +171,12 @@ export class SyncSagaCoordinator {
|
|
|
171
171
|
if (logalot) {
|
|
172
172
|
console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`);
|
|
173
173
|
}
|
|
174
|
-
|
|
174
|
+
// does this ever hit now?
|
|
175
|
+
return null; /* <<<< returns early */
|
|
176
|
+
}
|
|
177
|
+
else if (contextResult.nextFrameInfo?.sagaComplete) {
|
|
178
|
+
// this is the current proper workflow I believe as of 01/22/2026
|
|
179
|
+
return null; /* <<<< returns early */
|
|
175
180
|
}
|
|
176
181
|
// #region error conditions throw
|
|
177
182
|
if (contextResult.errorMsg) {
|
|
@@ -180,9 +185,6 @@ export class SyncSagaCoordinator {
|
|
|
180
185
|
else if (!contextResult.nextFrameInfo) {
|
|
181
186
|
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
|
|
182
187
|
}
|
|
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
188
|
// #endregion error conditions throw
|
|
187
189
|
// create the return context
|
|
188
190
|
const { frame, payloadIbGibsDomain } = contextResult.nextFrameInfo;
|
|
@@ -417,8 +419,8 @@ export class SyncSagaCoordinator {
|
|
|
417
419
|
else if (!contextResult.nextFrameInfo) {
|
|
418
420
|
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
419
421
|
}
|
|
420
|
-
else if (contextResult.nextFrameInfo?.
|
|
421
|
-
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.
|
|
422
|
+
else if (contextResult.nextFrameInfo?.sagaComplete) {
|
|
423
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.sagaComplete? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
422
424
|
}
|
|
423
425
|
// #endregion error conditions throw
|
|
424
426
|
// we have another frame to process and send out, with possibly
|
|
@@ -585,7 +587,9 @@ export class SyncSagaCoordinator {
|
|
|
585
587
|
metaspace,
|
|
586
588
|
localSpace,
|
|
587
589
|
});
|
|
588
|
-
|
|
590
|
+
if (logalot) {
|
|
591
|
+
console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`);
|
|
592
|
+
}
|
|
589
593
|
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
590
594
|
}
|
|
591
595
|
catch (error) {
|
|
@@ -691,7 +695,7 @@ export class SyncSagaCoordinator {
|
|
|
691
695
|
console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
|
|
692
696
|
}
|
|
693
697
|
// Get Stage from Stone (or Frame for Init fallback)
|
|
694
|
-
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space:
|
|
698
|
+
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
695
699
|
if (logalot) {
|
|
696
700
|
console.log(`${lc} handling frame stage: ${stage}`);
|
|
697
701
|
}
|
|
@@ -705,11 +709,8 @@ export class SyncSagaCoordinator {
|
|
|
705
709
|
nextFrameInfo = await this.handleInitFrame({
|
|
706
710
|
sagaIbGib,
|
|
707
711
|
messageData: messageData,
|
|
708
|
-
metaspace,
|
|
709
|
-
|
|
710
|
-
myTempSpace: myTempSpace,
|
|
711
|
-
identity,
|
|
712
|
-
identitySecret
|
|
712
|
+
metaspace, mySpace, myTempSpace,
|
|
713
|
+
identity, identitySecret
|
|
713
714
|
});
|
|
714
715
|
break;
|
|
715
716
|
case SyncStage.ack:
|
|
@@ -717,24 +718,33 @@ export class SyncSagaCoordinator {
|
|
|
717
718
|
throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`);
|
|
718
719
|
}
|
|
719
720
|
nextFrameInfo = await this.handleAckFrame({
|
|
721
|
+
sagaContext,
|
|
720
722
|
sagaIbGib,
|
|
721
|
-
srcGraph,
|
|
722
723
|
initDomainGraph,
|
|
723
|
-
metaspace,
|
|
724
|
-
destSpace: mySpace,
|
|
725
|
-
tempSpace: myTempSpace,
|
|
724
|
+
metaspace, mySpace, myTempSpace,
|
|
726
725
|
identity,
|
|
727
726
|
});
|
|
728
727
|
break;
|
|
729
728
|
case SyncStage.delta:
|
|
730
|
-
nextFrameInfo = await this.handleDeltaFrame({
|
|
729
|
+
nextFrameInfo = await this.handleDeltaFrame({
|
|
730
|
+
sagaContext,
|
|
731
|
+
sagaIbGib,
|
|
732
|
+
srcGraph,
|
|
733
|
+
metaspace,
|
|
734
|
+
mySpace,
|
|
735
|
+
myTempSpace,
|
|
736
|
+
identity,
|
|
737
|
+
});
|
|
731
738
|
break;
|
|
732
739
|
case SyncStage.commit:
|
|
733
|
-
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace,
|
|
740
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
|
|
734
741
|
break;
|
|
735
742
|
default:
|
|
736
743
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
737
744
|
}
|
|
745
|
+
if (logalot) {
|
|
746
|
+
console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`);
|
|
747
|
+
}
|
|
738
748
|
return { errorMsg: undefined, nextFrameInfo, };
|
|
739
749
|
}
|
|
740
750
|
catch (error) {
|
|
@@ -758,7 +768,7 @@ export class SyncSagaCoordinator {
|
|
|
758
768
|
* The Receiver performs Gap Analysis here:
|
|
759
769
|
* 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
|
|
760
770
|
* 2. Identifies what Sender needs (`pushOfferAddrs`).
|
|
761
|
-
* 3. Identifies what Receiver needs (`
|
|
771
|
+
* 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
|
|
762
772
|
* 4. Returns an `Ack` frame containing these lists.
|
|
763
773
|
*/
|
|
764
774
|
async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
|
|
@@ -995,7 +1005,7 @@ export class SyncSagaCoordinator {
|
|
|
995
1005
|
}
|
|
996
1006
|
const ackStone = await this.createSyncMsgStone({
|
|
997
1007
|
data: ackData,
|
|
998
|
-
localSpace:
|
|
1008
|
+
localSpace: mySpace,
|
|
999
1009
|
metaspace,
|
|
1000
1010
|
});
|
|
1001
1011
|
if (logalot) {
|
|
@@ -1092,19 +1102,23 @@ export class SyncSagaCoordinator {
|
|
|
1092
1102
|
* **Execution Context**: **Sender (Local)**.
|
|
1093
1103
|
*
|
|
1094
1104
|
* The Sender reacts to the Receiver's requirements:
|
|
1095
|
-
* 1.
|
|
1096
|
-
*
|
|
1105
|
+
* 1. `deltaRequestAddrInfos`: Receiver wants this data. Sender takes this
|
|
1106
|
+
* into account, plus gathers it from `initDomainGraph` and puts them in
|
|
1107
|
+
* `payloadIbGibs` (for next frame).
|
|
1108
|
+
* 2. `pushOfferAddrs`: Receiver has newer data. these should have been
|
|
1109
|
+
* included in the incoming context from the receiver..
|
|
1097
1110
|
*
|
|
1098
1111
|
* Returns a `Delta` frame.
|
|
1099
1112
|
*/
|
|
1100
|
-
async handleAckFrame({
|
|
1113
|
+
async handleAckFrame({ sagaContext, sagaIbGib, initDomainGraph, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1101
1114
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
1102
1115
|
try {
|
|
1103
1116
|
if (logalot) {
|
|
1104
1117
|
console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
|
|
1105
1118
|
}
|
|
1106
|
-
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space:
|
|
1119
|
+
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
1107
1120
|
const ackData = messageData;
|
|
1121
|
+
// #region sanity/validation
|
|
1108
1122
|
if (!ackData) {
|
|
1109
1123
|
throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
|
|
1110
1124
|
}
|
|
@@ -1114,6 +1128,10 @@ export class SyncSagaCoordinator {
|
|
|
1114
1128
|
if (logalot) {
|
|
1115
1129
|
console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`);
|
|
1116
1130
|
}
|
|
1131
|
+
if (!sagaIbGib.data) {
|
|
1132
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`);
|
|
1133
|
+
}
|
|
1134
|
+
// #region sanity/validation
|
|
1117
1135
|
// 1. Check for Conflicts
|
|
1118
1136
|
const conflicts = ackData.conflicts || [];
|
|
1119
1137
|
console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`);
|
|
@@ -1128,7 +1146,12 @@ export class SyncSagaCoordinator {
|
|
|
1128
1146
|
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1129
1147
|
}
|
|
1130
1148
|
// at this point, if we have conflicts, they are non-terminal
|
|
1131
|
-
|
|
1149
|
+
/**
|
|
1150
|
+
* at this point, we only request ibgibs for conflicted timelines.
|
|
1151
|
+
* If the receiver had known of any ibgibs this sender needed, it
|
|
1152
|
+
* would have been in the push offer.
|
|
1153
|
+
*/
|
|
1154
|
+
const outgoingDeltaAddrRequestInfos = []; // Additional requests for merging
|
|
1132
1155
|
if (conflicts.length > 0) {
|
|
1133
1156
|
console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
|
|
1134
1157
|
// We need to resolve these.
|
|
@@ -1138,8 +1161,8 @@ export class SyncSagaCoordinator {
|
|
|
1138
1161
|
// 3. Request that data (as Delta Reqs)
|
|
1139
1162
|
// 4. (Later in Delta Phase) Perform Merge.
|
|
1140
1163
|
// BUT: The Delta Phase is usually generic "Send me these Addrs".
|
|
1141
|
-
// If we just add to `
|
|
1142
|
-
// wait. `ackData.
|
|
1164
|
+
// If we just add to `deltaRequestAddrInfos` (which are requests for Sender to send to Receiver?),
|
|
1165
|
+
// wait. `ackData.deltaRequestAddrInfos` are what RECEIVER wants from SENDER.
|
|
1143
1166
|
// We (Sender) are processing the Ack.
|
|
1144
1167
|
// We need to request data FROM Receiver.
|
|
1145
1168
|
// But the protocol 'Ack' step typically leads to 'Delta' (Sender sending data).
|
|
@@ -1206,8 +1229,8 @@ export class SyncSagaCoordinator {
|
|
|
1206
1229
|
// // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
|
|
1207
1230
|
// for (const addr of receiverOnlyAddrs) {
|
|
1208
1231
|
// // Add the frame itself first
|
|
1209
|
-
// if (!
|
|
1210
|
-
//
|
|
1232
|
+
// if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
|
|
1233
|
+
// outgoingDeltaAddrRequestInfos.push(addr);
|
|
1211
1234
|
// }
|
|
1212
1235
|
// // Get the frame's delta dependencies (skip LCA's deps)
|
|
1213
1236
|
// try {
|
|
@@ -1223,8 +1246,8 @@ export class SyncSagaCoordinator {
|
|
|
1223
1246
|
// if (frameDeltaDeps) {
|
|
1224
1247
|
// // Add all delta dependencies (Object.keys gives us the addresses)
|
|
1225
1248
|
// Object.keys(frameDeltaDeps).forEach(depAddr => {
|
|
1226
|
-
// if (!
|
|
1227
|
-
//
|
|
1249
|
+
// if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
|
|
1250
|
+
// outgoingDeltaAddrRequestInfos.push(depAddr);
|
|
1228
1251
|
// }
|
|
1229
1252
|
// });
|
|
1230
1253
|
// }
|
|
@@ -1233,117 +1256,63 @@ export class SyncSagaCoordinator {
|
|
|
1233
1256
|
// console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1234
1257
|
// }
|
|
1235
1258
|
// }
|
|
1236
|
-
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${
|
|
1259
|
+
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
|
|
1237
1260
|
// } else {
|
|
1238
1261
|
// console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1239
1262
|
// }
|
|
1240
1263
|
}
|
|
1241
|
-
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts.
|
|
1264
|
+
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
|
|
1242
1265
|
}
|
|
1243
1266
|
else {
|
|
1244
1267
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1245
1268
|
}
|
|
1246
1269
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
}
|
|
1257
|
-
// 2. Process Delta Requests (Push Payload)
|
|
1258
|
-
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1259
|
-
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1260
|
-
const skipAddrs = new Set();
|
|
1261
|
-
if (ackData.knowledgeVector) {
|
|
1262
|
-
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1263
|
-
// addrs.forEach(a => skipAddrs.add(a));
|
|
1264
|
-
});
|
|
1265
|
-
}
|
|
1266
|
-
const payloadIbGibs = [];
|
|
1267
|
-
// Gather all tips to sync first
|
|
1268
|
-
const tipsToSync = [];
|
|
1269
|
-
for (const addr of deltaReqAddrs) {
|
|
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
|
-
// }
|
|
1282
|
-
}
|
|
1283
|
-
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1284
|
-
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1285
|
-
// `getDependencyGraph` takes a single ibGib.
|
|
1286
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1287
|
-
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1288
|
-
// We will loop.
|
|
1289
|
-
const allDepsSet = new Set();
|
|
1290
|
-
for (const tip of tipsToSync) {
|
|
1291
|
-
// Always include the tip itself
|
|
1292
|
-
const tipAddr = getIbGibAddr({ ibGib: tip });
|
|
1293
|
-
// Only process if not skipped (though deltaReq implies they barely just asked for it)
|
|
1294
|
-
// But detailed deps might be skipped.
|
|
1295
|
-
// Get Graph with Skips
|
|
1296
|
-
// Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
|
|
1297
|
-
const deps = await getDependencyGraph({
|
|
1298
|
-
ibGib: tip,
|
|
1299
|
-
space: destSpace,
|
|
1300
|
-
skipAddrs: Array.from(skipAddrs)
|
|
1301
|
-
});
|
|
1302
|
-
// [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
|
|
1303
|
-
let tipIncluded = false;
|
|
1304
|
-
if (deps) {
|
|
1305
|
-
Object.values(deps).forEach(d => {
|
|
1306
|
-
const dAddr = getIbGibAddr({ ibGib: d });
|
|
1307
|
-
if (!allDepsSet.has(dAddr)) {
|
|
1308
|
-
allDepsSet.add(dAddr);
|
|
1309
|
-
payloadIbGibs.push(d);
|
|
1310
|
-
}
|
|
1311
|
-
if (dAddr === tipAddr) {
|
|
1312
|
-
tipIncluded = true;
|
|
1313
|
-
}
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
if (!tipIncluded && !skipAddrs.has(tipAddr)) {
|
|
1317
|
-
if (logalot) {
|
|
1318
|
-
console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`);
|
|
1319
|
-
}
|
|
1320
|
-
if (!allDepsSet.has(tipAddr)) {
|
|
1321
|
-
allDepsSet.add(tipAddr);
|
|
1322
|
-
payloadIbGibs.push(tip);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1270
|
+
/**
|
|
1271
|
+
* these were requested addrs on the INCOMING frame (the ack data).
|
|
1272
|
+
*
|
|
1273
|
+
* This is in contrast to any OUTGOING requests we make in this
|
|
1274
|
+
* method.
|
|
1275
|
+
*/
|
|
1276
|
+
const payloadIbGibsDomain = await this.getPayloadsForRequestedInfos({
|
|
1277
|
+
deltaRequestAddrInfos: ackData.deltaRequestAddrInfos || [],
|
|
1278
|
+
mySpace,
|
|
1279
|
+
});
|
|
1326
1280
|
// 3. Create Delta Frame
|
|
1327
|
-
const sagaId = ackData.sagaId;
|
|
1328
1281
|
const deltaData = {
|
|
1329
1282
|
sagaId: sagaIbGib.data.uuid,
|
|
1330
1283
|
stage: SyncStage.delta,
|
|
1331
|
-
|
|
1332
|
-
|
|
1284
|
+
/**
|
|
1285
|
+
* we're sending these domain ibgibs as payload (they were
|
|
1286
|
+
* requested by receiver)
|
|
1287
|
+
*/
|
|
1288
|
+
payloadAddrsDomain: payloadIbGibsDomain.length > 0 ?
|
|
1289
|
+
payloadIbGibsDomain.map(x => getIbGibAddr({ ibGib: x })) :
|
|
1290
|
+
undefined,
|
|
1291
|
+
/**
|
|
1292
|
+
* we're asking for these addrs
|
|
1293
|
+
*/
|
|
1294
|
+
deltaRequestAddrInfos: outgoingDeltaAddrRequestInfos.length > 0 ?
|
|
1295
|
+
outgoingDeltaAddrRequestInfos :
|
|
1296
|
+
undefined,
|
|
1297
|
+
/**
|
|
1298
|
+
* if we have no changes and request none, propose commit to
|
|
1299
|
+
* finish the sync transaction.
|
|
1300
|
+
*/
|
|
1301
|
+
proposeCommit: payloadIbGibsDomain.length === 0 && outgoingDeltaAddrRequestInfos.length === 0,
|
|
1333
1302
|
};
|
|
1334
1303
|
if (logalot) {
|
|
1335
1304
|
console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`);
|
|
1336
1305
|
}
|
|
1337
1306
|
const deltaStone = await this.createSyncMsgStone({
|
|
1338
1307
|
data: deltaData,
|
|
1339
|
-
localSpace:
|
|
1308
|
+
localSpace: mySpace,
|
|
1340
1309
|
metaspace,
|
|
1341
1310
|
});
|
|
1342
1311
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1343
1312
|
prevSagaIbGib: sagaIbGib,
|
|
1344
1313
|
msgStones: [deltaStone],
|
|
1345
1314
|
sessionIdentity: identity,
|
|
1346
|
-
localSpace:
|
|
1315
|
+
localSpace: mySpace,
|
|
1347
1316
|
metaspace,
|
|
1348
1317
|
});
|
|
1349
1318
|
if (logalot) {
|
|
@@ -1354,9 +1323,7 @@ export class SyncSagaCoordinator {
|
|
|
1354
1323
|
if (identity) {
|
|
1355
1324
|
payloadIbGibsControl.push(identity);
|
|
1356
1325
|
}
|
|
1357
|
-
|
|
1358
|
-
// return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
|
|
1359
|
-
throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
|
|
1326
|
+
return { frame: deltaFrame, payloadIbGibsDomain, };
|
|
1360
1327
|
}
|
|
1361
1328
|
catch (error) {
|
|
1362
1329
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1378,330 +1345,479 @@ export class SyncSagaCoordinator {
|
|
|
1378
1345
|
* 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
|
|
1379
1346
|
* 3. **Completion**: If no more requests, transitions to `Commit`.
|
|
1380
1347
|
*/
|
|
1381
|
-
async handleDeltaFrame({ sagaIbGib, srcGraph,
|
|
1348
|
+
async handleDeltaFrame({ sagaContext, sagaIbGib, srcGraph, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1382
1349
|
const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
1387
|
-
const deltaData = messageData;
|
|
1388
|
-
if (!deltaData) {
|
|
1389
|
-
throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
|
|
1390
|
-
}
|
|
1391
|
-
if (deltaData.stage !== SyncStage.delta) {
|
|
1392
|
-
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1393
|
-
}
|
|
1394
|
-
if (logalot) {
|
|
1395
|
-
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1396
|
-
}
|
|
1397
|
-
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1398
|
-
const payloadAddrs = deltaData.payloadAddrs || [];
|
|
1399
|
-
const peerRequests = deltaData.requests || [];
|
|
1400
|
-
const peerProposesCommit = deltaData.proposeCommit || false;
|
|
1401
|
-
// 1. Process Received Payload (Ingest)
|
|
1402
|
-
const receivedPayloadIbGibs = [];
|
|
1403
|
-
if (payloadAddrs.length > 0) {
|
|
1404
|
-
// We use `payloadAddrs` as the manifest.
|
|
1405
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1406
|
-
// assuming the "Transport" layer put them there implicitly?
|
|
1407
|
-
// OR, if we are local-only, we just get them.
|
|
1408
|
-
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
1409
|
-
const res = await getFromSpace({
|
|
1410
|
-
addrs: payloadAddrs,
|
|
1411
|
-
space: tempSpace, // Incoming data is in tempSpace
|
|
1412
|
-
});
|
|
1413
|
-
if (res.ibGibs) {
|
|
1414
|
-
receivedPayloadIbGibs.push(...res.ibGibs);
|
|
1415
|
-
// Also put them? `getFromSpace` retrieves. If they are in space, they are persisted.
|
|
1416
|
-
// If this is a Temp Space, they are safe.
|
|
1350
|
+
try {
|
|
1351
|
+
if (logalot) {
|
|
1352
|
+
console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`);
|
|
1417
1353
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1354
|
+
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
1355
|
+
const deltaData = messageData;
|
|
1356
|
+
// #region validate/sanity
|
|
1357
|
+
if (!deltaData) {
|
|
1358
|
+
throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
|
|
1420
1359
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
for (const addr of peerRequests) {
|
|
1427
|
-
// Get the requested ibGib
|
|
1428
|
-
let ibGib = srcGraph[addr];
|
|
1429
|
-
if (!ibGib) {
|
|
1430
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1431
|
-
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1432
|
-
ibGib = res.ibGibs[0];
|
|
1433
|
-
}
|
|
1360
|
+
if (deltaData.stage !== SyncStage.delta) {
|
|
1361
|
+
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1362
|
+
}
|
|
1363
|
+
if (logalot) {
|
|
1364
|
+
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1434
1365
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1366
|
+
// #endregion validate/sanity
|
|
1367
|
+
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1368
|
+
const peerProposesCommit = deltaData.proposeCommit || false;
|
|
1369
|
+
/**
|
|
1370
|
+
* these are already in the local temp space
|
|
1371
|
+
*/
|
|
1372
|
+
const receivedPayloadIbGibs = sagaContext.payloadIbGibsDomain ?? [];
|
|
1373
|
+
// 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
|
|
1374
|
+
console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`);
|
|
1375
|
+
const outgoingPayload = await this.getPayloadsForRequestedInfos({
|
|
1376
|
+
deltaRequestAddrInfos: deltaData.deltaRequestAddrInfos || [],
|
|
1377
|
+
mySpace,
|
|
1378
|
+
});
|
|
1379
|
+
console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
|
|
1380
|
+
// 3. Execute Merges (If applicable)
|
|
1381
|
+
// Check if we have pending conflicts that we CAN resolve now that we have data.
|
|
1382
|
+
// We look at the Saga History (Ack Frame) to find conflicts.
|
|
1383
|
+
// Optimization: Do this only if we received payloads.
|
|
1384
|
+
const mergeResultIbGibs = [];
|
|
1385
|
+
console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
|
|
1386
|
+
if (receivedPayloadIbGibs.length > 0) {
|
|
1387
|
+
console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
|
|
1388
|
+
// Find the Ack frame in history to get conflicts
|
|
1389
|
+
// Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
|
|
1390
|
+
// V1 timelines carry full history in `past`.
|
|
1391
|
+
const pastAddrs = sagaIbGib.rel8ns?.past || [];
|
|
1392
|
+
console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
|
|
1393
|
+
const sagaHistory = await getFullSyncSagaHistory({
|
|
1394
|
+
sagaIbGib,
|
|
1395
|
+
space: mySpace,
|
|
1396
|
+
});
|
|
1397
|
+
// #region validate/sanity sagaHistory
|
|
1398
|
+
if (sagaHistory.length === 0) {
|
|
1399
|
+
throw new Error(`(UNEXPECTED) sagaHistory.length is 0? We're in handleDeltaFrame. So we should have at least init and ack. (E: 815735c9cf756b275719bf434f180826)`);
|
|
1441
1400
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
try {
|
|
1445
|
-
const deps = await getDependencyGraph({
|
|
1446
|
-
ibGib,
|
|
1447
|
-
space: destSpace,
|
|
1448
|
-
});
|
|
1449
|
-
if (deps) {
|
|
1450
|
-
Object.values(deps).forEach(depIbGib => {
|
|
1451
|
-
const depAddr = getIbGibAddr({ ibGib: depIbGib });
|
|
1452
|
-
if (!outgoingAddrsSet.has(depAddr)) {
|
|
1453
|
-
outgoingPayload.push(depIbGib);
|
|
1454
|
-
outgoingAddrsSet.add(depAddr);
|
|
1455
|
-
}
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1401
|
+
if (sagaHistory.length === 1) {
|
|
1402
|
+
throw new Error(`(UNEXPECTED) sagaHistory.length is 1? We're in handleDeltaFrame. So we should have at least init and ack. (E: 0e4ef8e3ed088e83b2cdcd18ecea9826)`);
|
|
1458
1403
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1404
|
+
// #endregion validate/sanity sagaHistory
|
|
1405
|
+
const ackGraphs = sagaHistory.filter(x => x.msgStones.at(0).data.stage === SyncStage.ack);
|
|
1406
|
+
// #region validate/sanity ackGraphs
|
|
1407
|
+
if (ackGraphs.length > 1) {
|
|
1408
|
+
throw new Error(`(UNEXPECTED) more than one ack stage found in sagaHistory? we're expecting exactly one. (E: 7150983bb3a841caf8acd60826ba4f26)`);
|
|
1461
1409
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
console.warn(`${lc} Requested addr not found during delta fulfillment: ${addr}`);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
|
|
1468
|
-
// 3. Execute Merges (If applicable)
|
|
1469
|
-
// Check if we have pending conflicts that we CAN resolve now that we have data.
|
|
1470
|
-
// We look at the Saga History (Ack Frame) to find conflicts.
|
|
1471
|
-
// Optimization: Do this only if we received payloads.
|
|
1472
|
-
const mergeResultIbGibs = [];
|
|
1473
|
-
console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
|
|
1474
|
-
if (receivedPayloadIbGibs.length > 0) {
|
|
1475
|
-
console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
|
|
1476
|
-
// Find the Ack frame in history to get conflicts
|
|
1477
|
-
// Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
|
|
1478
|
-
// V1 timelines carry full history in `past`.
|
|
1479
|
-
const pastAddrs = sagaIbGib.rel8ns?.past || [];
|
|
1480
|
-
console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
|
|
1481
|
-
let ackData;
|
|
1482
|
-
if (pastAddrs.length > 0) {
|
|
1483
|
-
// Batch fetch all past frames
|
|
1484
|
-
const resPast = await getFromSpace({ addrs: pastAddrs, space: tempSpace });
|
|
1485
|
-
if (resPast.success && resPast.ibGibs) {
|
|
1486
|
-
// Iterate backwards (most recent first) to find the latest Ack
|
|
1487
|
-
for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
|
|
1488
|
-
const pastFrame = resPast.ibGibs[i];
|
|
1489
|
-
const messageStone = await getSyncSagaMessageFromFrame({
|
|
1490
|
-
frameIbGib: pastFrame,
|
|
1491
|
-
space: tempSpace
|
|
1492
|
-
});
|
|
1493
|
-
if (messageStone?.data?.stage === SyncStage.ack) {
|
|
1494
|
-
ackData = messageStone.data;
|
|
1495
|
-
console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
|
|
1496
|
-
break;
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1410
|
+
else if (ackGraphs.length === 0) {
|
|
1411
|
+
throw new Error(`(UNEXPECTED) couldn't find ack stage in sagaHistory? (E: 7d2da859196b86de28e7c8183af1e826)`);
|
|
1499
1412
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
// We have
|
|
1516
|
-
//
|
|
1517
|
-
//
|
|
1518
|
-
|
|
1519
|
-
//
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1413
|
+
if (ackGraphs[0].msgStones.length !== 1) {
|
|
1414
|
+
throw new Error(`(UNEXPECTED) ackGraph has more than one msg stone? only one expected right now. (E: f07d388abff8d26f98cbf6e8d3932826)`);
|
|
1415
|
+
}
|
|
1416
|
+
// #endregion validate/sanity ackGraphs
|
|
1417
|
+
const ackData = ackGraphs[0].msgStones[0].data;
|
|
1418
|
+
if (!ackData) {
|
|
1419
|
+
throw new Error(`(UNEXPECTED) ackData falsy? (E: f9af88427f66a8db1ba868b810e8b826)`);
|
|
1420
|
+
}
|
|
1421
|
+
if (ackData && ackData.conflicts) {
|
|
1422
|
+
const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
|
|
1423
|
+
for (const conflict of optimisticConflicts) {
|
|
1424
|
+
const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
|
|
1425
|
+
// We are Sender (usually) here if we are merging.
|
|
1426
|
+
// Check if we have the history needed (timelineAddrs).
|
|
1427
|
+
// Specifically, we needed the `receiverOnly` parts.
|
|
1428
|
+
// We blindly attempt merge if we have both tips accessible?
|
|
1429
|
+
// We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
|
|
1430
|
+
// Check if we have receiverTip in space
|
|
1431
|
+
console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
|
|
1432
|
+
const resRecTip = await getFromSpace({ addr: receiverTip, space: myTempSpace }); // Check myTempSpace for incoming data
|
|
1433
|
+
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1434
|
+
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1435
|
+
// We have the tip!
|
|
1436
|
+
// Do we have the full history?
|
|
1437
|
+
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1438
|
+
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1439
|
+
// Perform Merge!
|
|
1440
|
+
try {
|
|
1441
|
+
const mergeResult = await mergeDivergentTimelines({
|
|
1442
|
+
tipA: (await getFromSpace({ addr: senderTip, space: mySpace })).ibGibs[0], // Our tip from destSpace
|
|
1443
|
+
tipB: resRecTip.ibGibs[0], // Their tip (from myTempSpace)
|
|
1444
|
+
space: myTempSpace, // Merge uses myTempSpace
|
|
1445
|
+
metaspace,
|
|
1446
|
+
});
|
|
1447
|
+
if (mergeResult) {
|
|
1448
|
+
console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
|
|
1449
|
+
if (logalot) {
|
|
1450
|
+
console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
|
|
1451
|
+
}
|
|
1452
|
+
mergeResultIbGibs.push(mergeResult);
|
|
1453
|
+
outgoingPayload.push(mergeResult); // Send result to peer
|
|
1531
1454
|
}
|
|
1532
|
-
mergeResultIbGibs.push(mergeResult);
|
|
1533
|
-
outgoingPayload.push(mergeResult); // Send result to peer
|
|
1534
1455
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1456
|
+
catch (e) {
|
|
1457
|
+
console.error(`${lc} Merge failed: ${e}`);
|
|
1458
|
+
// If merge fails, we might Abort or just continue?
|
|
1459
|
+
}
|
|
1539
1460
|
}
|
|
1540
1461
|
}
|
|
1541
1462
|
}
|
|
1542
1463
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
// We have business to attend to -> Send Delta
|
|
1554
|
-
const responseDeltaData = {
|
|
1555
|
-
sagaId: deltaData.sagaId,
|
|
1556
|
-
stage: SyncStage.delta,
|
|
1557
|
-
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1558
|
-
requests: hasMyRequests ? myRequests : undefined,
|
|
1559
|
-
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1560
|
-
// Wait. If we send data, we are NOT committing yet.
|
|
1561
|
-
// We are sending data. The OTHER side must ingest it.
|
|
1562
|
-
// So proposeCommit = true?
|
|
1563
|
-
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1564
|
-
// Yes.
|
|
1565
|
-
};
|
|
1566
|
-
// BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
|
|
1567
|
-
// We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
|
|
1568
|
-
// So yes, proposeCommit = true.
|
|
1569
|
-
responseDeltaData.proposeCommit = true;
|
|
1570
|
-
const deltaStone = await this.createSyncMsgStone({
|
|
1571
|
-
data: responseDeltaData,
|
|
1572
|
-
localSpace: tempSpace,
|
|
1573
|
-
metaspace
|
|
1574
|
-
});
|
|
1575
|
-
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1576
|
-
prevSagaIbGib: sagaIbGib,
|
|
1577
|
-
msgStones: [deltaStone],
|
|
1578
|
-
sessionIdentity: identity,
|
|
1579
|
-
localSpace: tempSpace,
|
|
1580
|
-
metaspace
|
|
1581
|
-
});
|
|
1582
|
-
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1583
|
-
const payloadIbGibsControl = [deltaFrame, deltaStone];
|
|
1584
|
-
if (identity) {
|
|
1585
|
-
payloadIbGibsControl.push(identity);
|
|
1586
|
-
}
|
|
1587
|
-
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1588
|
-
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1589
|
-
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1590
|
-
}
|
|
1591
|
-
else {
|
|
1592
|
-
// We have nothing to send.
|
|
1593
|
-
if (peerProposesCommit) {
|
|
1594
|
-
// Peer is done. We are done. -> Commit.
|
|
1595
|
-
const commitData = {
|
|
1596
|
-
sagaId: deltaData.sagaId,
|
|
1597
|
-
stage: SyncStage.commit,
|
|
1598
|
-
success: true,
|
|
1599
|
-
};
|
|
1600
|
-
const commitStone = await this.createSyncMsgStone({
|
|
1601
|
-
data: commitData,
|
|
1602
|
-
localSpace: tempSpace,
|
|
1603
|
-
metaspace
|
|
1604
|
-
});
|
|
1605
|
-
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1606
|
-
prevSagaIbGib: sagaIbGib,
|
|
1607
|
-
msgStones: [commitStone],
|
|
1608
|
-
sessionIdentity: identity,
|
|
1609
|
-
localSpace: tempSpace,
|
|
1610
|
-
metaspace
|
|
1611
|
-
});
|
|
1612
|
-
// Build control payloads for commit
|
|
1613
|
-
const commitCtrlPayloads = [commitFrame, commitStone];
|
|
1614
|
-
if (identity) {
|
|
1615
|
-
commitCtrlPayloads.push(identity);
|
|
1616
|
-
}
|
|
1617
|
-
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1618
|
-
// return { frame: commitFrame, };
|
|
1619
|
-
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1620
|
-
}
|
|
1621
|
-
else {
|
|
1622
|
-
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
1623
|
-
// But we are empty.
|
|
1624
|
-
// So WE propose commit.
|
|
1464
|
+
// 4. Determine Next Action
|
|
1465
|
+
// We have `outgoingPayload` (Requests + Merge Results).
|
|
1466
|
+
// Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
|
|
1467
|
+
// Do WE have outstanding requests?
|
|
1468
|
+
// We might if `mergeResult` requires further sync? Usually no, result is complete.
|
|
1469
|
+
const myRequests = []; // If we had more needs (e.g. partial payload), we'd add here.
|
|
1470
|
+
const hasOutgoing = outgoingPayload.length > 0;
|
|
1471
|
+
const hasMyRequests = myRequests.length > 0;
|
|
1472
|
+
if (hasOutgoing || hasMyRequests) {
|
|
1473
|
+
// We have business to attend to -> Send Delta
|
|
1625
1474
|
const responseDeltaData = {
|
|
1626
1475
|
sagaId: deltaData.sagaId,
|
|
1627
1476
|
stage: SyncStage.delta,
|
|
1628
|
-
|
|
1629
|
-
|
|
1477
|
+
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1478
|
+
requests: hasMyRequests ? myRequests : undefined,
|
|
1630
1479
|
};
|
|
1631
1480
|
const deltaStone = await this.createSyncMsgStone({
|
|
1632
1481
|
data: responseDeltaData,
|
|
1633
|
-
localSpace:
|
|
1634
|
-
metaspace
|
|
1482
|
+
localSpace: mySpace,
|
|
1483
|
+
metaspace,
|
|
1635
1484
|
});
|
|
1636
1485
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1637
1486
|
prevSagaIbGib: sagaIbGib,
|
|
1638
1487
|
msgStones: [deltaStone],
|
|
1639
1488
|
sessionIdentity: identity,
|
|
1640
|
-
localSpace:
|
|
1641
|
-
metaspace
|
|
1489
|
+
localSpace: mySpace,
|
|
1490
|
+
metaspace,
|
|
1642
1491
|
});
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1492
|
+
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1493
|
+
}
|
|
1494
|
+
else {
|
|
1495
|
+
// We have nothing to send.
|
|
1496
|
+
if (peerProposesCommit) {
|
|
1497
|
+
// Peer is done. We are done. -> Commit.
|
|
1498
|
+
// one last validate entire history?
|
|
1499
|
+
const history = await getFullSyncSagaHistory({
|
|
1500
|
+
sagaIbGib,
|
|
1501
|
+
space: mySpace,
|
|
1502
|
+
});
|
|
1503
|
+
const validationErrors = await validateFullSyncSagaHistory({
|
|
1504
|
+
history,
|
|
1505
|
+
});
|
|
1506
|
+
if (validationErrors.length > 0) {
|
|
1507
|
+
const errorCommitFrame = await this.createCommitFrame({
|
|
1508
|
+
sagaIbGib,
|
|
1509
|
+
errors: validationErrors,
|
|
1510
|
+
metaspace, mySpace,
|
|
1511
|
+
identity,
|
|
1512
|
+
});
|
|
1513
|
+
return { frame: errorCommitFrame, }; /* <<<< returns early */
|
|
1647
1514
|
}
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1515
|
+
await this.executeLocalCommit({
|
|
1516
|
+
sagaHistory: history,
|
|
1517
|
+
deltaFrame: sagaIbGib,
|
|
1518
|
+
localTempSpace: myTempSpace,
|
|
1519
|
+
localSpace: mySpace,
|
|
1520
|
+
metaspace,
|
|
1521
|
+
});
|
|
1522
|
+
const commitFrame = await this.createCommitFrame({
|
|
1523
|
+
sagaIbGib,
|
|
1524
|
+
errors: undefined,
|
|
1525
|
+
metaspace,
|
|
1526
|
+
mySpace,
|
|
1527
|
+
identity,
|
|
1528
|
+
});
|
|
1529
|
+
return { frame: commitFrame, };
|
|
1530
|
+
}
|
|
1531
|
+
else {
|
|
1532
|
+
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
1533
|
+
// But we are empty.
|
|
1534
|
+
// So WE propose commit.
|
|
1535
|
+
const responseDeltaData = {
|
|
1651
1536
|
sagaId: deltaData.sagaId,
|
|
1652
|
-
stage: SyncStage.
|
|
1653
|
-
|
|
1537
|
+
stage: SyncStage.delta,
|
|
1538
|
+
proposeCommit: true,
|
|
1654
1539
|
};
|
|
1655
|
-
const
|
|
1656
|
-
data:
|
|
1657
|
-
localSpace:
|
|
1540
|
+
const deltaStone = await this.createSyncMsgStone({
|
|
1541
|
+
data: responseDeltaData,
|
|
1542
|
+
localSpace: mySpace,
|
|
1658
1543
|
metaspace
|
|
1659
1544
|
});
|
|
1660
|
-
const
|
|
1661
|
-
prevSagaIbGib:
|
|
1662
|
-
msgStones: [
|
|
1545
|
+
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1546
|
+
prevSagaIbGib: sagaIbGib,
|
|
1547
|
+
msgStones: [deltaStone],
|
|
1663
1548
|
sessionIdentity: identity,
|
|
1664
|
-
localSpace:
|
|
1549
|
+
localSpace: mySpace,
|
|
1665
1550
|
metaspace
|
|
1666
1551
|
});
|
|
1667
|
-
//
|
|
1668
|
-
|
|
1552
|
+
// Check if PEER proposed commit
|
|
1553
|
+
if (deltaData.proposeCommit) {
|
|
1554
|
+
if (logalot) {
|
|
1555
|
+
console.log(`${lc} Peer proposed commit. Accepting & Committing.`);
|
|
1556
|
+
}
|
|
1557
|
+
// Peer wants to commit and has no more requests.
|
|
1558
|
+
// We should Commit.
|
|
1559
|
+
const commitData = {
|
|
1560
|
+
sagaId: deltaData.sagaId,
|
|
1561
|
+
stage: SyncStage.commit,
|
|
1562
|
+
success: true,
|
|
1563
|
+
};
|
|
1564
|
+
const commitStone = await this.createSyncMsgStone({
|
|
1565
|
+
data: commitData,
|
|
1566
|
+
localSpace: mySpace,
|
|
1567
|
+
metaspace
|
|
1568
|
+
});
|
|
1569
|
+
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1570
|
+
prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
|
|
1571
|
+
msgStones: [commitStone],
|
|
1572
|
+
sessionIdentity: identity,
|
|
1573
|
+
localSpace: mySpace,
|
|
1574
|
+
metaspace
|
|
1575
|
+
});
|
|
1576
|
+
// Build control payloads for commit
|
|
1577
|
+
const commitCtrlPayloads2 = [commitFrame, commitStone];
|
|
1578
|
+
if (identity) {
|
|
1579
|
+
commitCtrlPayloads2.push(identity);
|
|
1580
|
+
}
|
|
1581
|
+
return { frame: commitFrame, };
|
|
1582
|
+
}
|
|
1583
|
+
// Build control payloads for delta propose
|
|
1584
|
+
const deltaCtrlPayloads = [deltaFrame, deltaStone];
|
|
1669
1585
|
if (identity) {
|
|
1670
|
-
|
|
1586
|
+
deltaCtrlPayloads.push(identity);
|
|
1671
1587
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1588
|
+
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
catch (error) {
|
|
1593
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1596
|
+
finally {
|
|
1597
|
+
if (logalot) {
|
|
1598
|
+
console.log(`${lc} complete.`);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* should throw if fails
|
|
1604
|
+
*/
|
|
1605
|
+
async executeLocalCommit({ deltaFrame, sagaHistory, metaspace, localSpace, localTempSpace, identity, }) {
|
|
1606
|
+
const lc = `${this.lc}[${this.executeLocalCommit.name}]`;
|
|
1607
|
+
try {
|
|
1608
|
+
if (logalot) {
|
|
1609
|
+
console.log(`${lc} starting... (I: 6734980446b86a63c1af6e2e206de826)`);
|
|
1610
|
+
}
|
|
1611
|
+
// #region validate/sanity
|
|
1612
|
+
if (!deltaFrame.data) {
|
|
1613
|
+
throw new Error(`(UNEXPECTED) deltaFrame.data falsy? (E: a8be68d48668d93d992d793834823826)`);
|
|
1614
|
+
}
|
|
1615
|
+
// #endregion validate/sanity
|
|
1616
|
+
// * move all payload addrs from temp space to local space
|
|
1617
|
+
// * register each and every iteration of each timeline (including
|
|
1618
|
+
// stones, which are like their own sealed timeline of length 1)
|
|
1619
|
+
const allPayloadAddrsDomainTransferred = [];
|
|
1620
|
+
const fnAddPayloadAddr = (addr) => {
|
|
1621
|
+
if (!allPayloadAddrsDomainTransferred.includes(addr)) {
|
|
1622
|
+
allPayloadAddrsDomainTransferred.push(addr);
|
|
1675
1623
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
if (
|
|
1679
|
-
|
|
1624
|
+
};
|
|
1625
|
+
sagaHistory.forEach(x => {
|
|
1626
|
+
if (!x.sagaIbGib.data) {
|
|
1627
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 34d7f8cdee14717ce828878d98f89826)`);
|
|
1680
1628
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1629
|
+
x.msgStones.forEach(msgStone => {
|
|
1630
|
+
if (!msgStone.data) {
|
|
1631
|
+
throw new Error(`(UNEXPECTED) msgStone.data falsy? (E: 21406101f91847514cc759c8a7382f26)`);
|
|
1632
|
+
}
|
|
1633
|
+
if (msgStone.data.stage === SyncStage.ack) {
|
|
1634
|
+
const ackData = msgStone.data;
|
|
1635
|
+
if (ackData.pushOfferInfos && ackData.pushOfferInfos.length > 0) {
|
|
1636
|
+
ackData.pushOfferInfos.forEach(info => {
|
|
1637
|
+
info.addrs.forEach(addr => fnAddPayloadAddr(addr));
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
else if (msgStone.data.stage === SyncStage.delta) {
|
|
1642
|
+
const deltaData = msgStone.data;
|
|
1643
|
+
if (deltaData.payloadAddrsDomain && deltaData.payloadAddrsDomain.length > 0) {
|
|
1644
|
+
deltaData.payloadAddrsDomain.forEach(addr => fnAddPayloadAddr(addr));
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
});
|
|
1649
|
+
// at this point, we have a list of ALL payload addrs retrieved.
|
|
1650
|
+
let allPayloadIbGibsDomainTransferred = [];
|
|
1651
|
+
const resGetAllTransferred = await getFromSpace({ addrs: allPayloadAddrsDomainTransferred, space: localTempSpace });
|
|
1652
|
+
if (resGetAllTransferred.success && resGetAllTransferred.ibGibs && resGetAllTransferred.ibGibs.length === allPayloadAddrsDomainTransferred.length) {
|
|
1653
|
+
allPayloadIbGibsDomainTransferred = resGetAllTransferred.ibGibs.concat();
|
|
1654
|
+
}
|
|
1655
|
+
else {
|
|
1656
|
+
// errored out, gather info
|
|
1657
|
+
if (!resGetAllTransferred.rawResultIbGib) {
|
|
1658
|
+
throw new Error(`(UNEXPECTED) !resGetAllTransferred.rawResultIbGib falsy? (E: dc6cf8729668f4fbe8c024f887d97a26)`);
|
|
1659
|
+
}
|
|
1660
|
+
const { addrsNotFound, addrsErrored, errors } = resGetAllTransferred.rawResultIbGib.data;
|
|
1661
|
+
throw new Error(`(UNEXPECTED) we couldn't get all addrs transferred throughout the saga from the tempspace (${localTempSpace.ib})? addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. errors: ${errors ?? []}(E: 222e341e634862b4d88ae7282584d826)`);
|
|
1662
|
+
}
|
|
1663
|
+
// now we have all ibgibs transferred, first put them all the local space
|
|
1664
|
+
const { payload_Dnas, payload_NonDnas } = await putInSpace_dnasThenNonDnas({
|
|
1665
|
+
ibGibs: allPayloadIbGibsDomainTransferred,
|
|
1666
|
+
space: localSpace
|
|
1667
|
+
});
|
|
1668
|
+
const { mapWithTjp_NoDna, mapWithTjp_YesDna, mapWithoutTjps } = splitPerTjpAndOrDna({ ibGibs: payload_NonDnas, filterPrimitives: true });
|
|
1669
|
+
// first register all non-tjp stones
|
|
1670
|
+
const nontjps = Object.values(mapWithoutTjps);
|
|
1671
|
+
for (const nontjp of nontjps) {
|
|
1672
|
+
await metaspace.registerNewIbGib({
|
|
1673
|
+
ibGib: nontjp,
|
|
1674
|
+
space: localSpace,
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
// next register each timeline in order...
|
|
1678
|
+
// ...first the ones without dna...
|
|
1679
|
+
const timelinesByTjpAddr_NoDna = getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_NoDna) });
|
|
1680
|
+
const tjpAddrs_NoDna = Object.keys(timelinesByTjpAddr_NoDna);
|
|
1681
|
+
for (const tjpAddr_NoDna of tjpAddrs_NoDna) {
|
|
1682
|
+
const timelineIbGibs = timelinesByTjpAddr_NoDna[tjpAddr_NoDna];
|
|
1683
|
+
for (const ibGib of timelineIbGibs) {
|
|
1684
|
+
await metaspace.registerNewIbGib({
|
|
1685
|
+
ibGib,
|
|
1686
|
+
space: localSpace,
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
// ...then the ones WITH dna.
|
|
1691
|
+
const timelinesByTjpAddr_YesDna = getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_YesDna) });
|
|
1692
|
+
const tjpAddrs_YesDna = Object.keys(timelinesByTjpAddr_YesDna);
|
|
1693
|
+
for (const tjpAddr_YesDna of tjpAddrs_YesDna) {
|
|
1694
|
+
const timelineIbGibs = timelinesByTjpAddr_YesDna[tjpAddr_YesDna];
|
|
1695
|
+
for (const ibGib of timelineIbGibs) {
|
|
1696
|
+
await metaspace.registerNewIbGib({
|
|
1697
|
+
ibGib,
|
|
1698
|
+
space: localSpace,
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
catch (error) {
|
|
1704
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1705
|
+
throw error;
|
|
1706
|
+
}
|
|
1707
|
+
finally {
|
|
1708
|
+
if (logalot) {
|
|
1709
|
+
console.log(`${lc} complete.`);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
async createCommitFrame({ sagaIbGib, errors, metaspace, mySpace, identity, }) {
|
|
1714
|
+
const lc = `[${this.createCommitFrame.name}]`;
|
|
1715
|
+
try {
|
|
1716
|
+
if (logalot) {
|
|
1717
|
+
console.log(`${lc} starting... (I: 48fc57c023f80122135a284855757526)`);
|
|
1718
|
+
}
|
|
1719
|
+
const commitData = errors && errors.length > 0 ?
|
|
1720
|
+
{
|
|
1721
|
+
// errored
|
|
1722
|
+
sagaId: sagaIbGib.data.uuid,
|
|
1723
|
+
stage: SyncStage.commit,
|
|
1724
|
+
success: false,
|
|
1725
|
+
errors,
|
|
1726
|
+
} :
|
|
1727
|
+
{
|
|
1728
|
+
// not errored
|
|
1729
|
+
sagaId: sagaIbGib.data.uuid,
|
|
1730
|
+
stage: SyncStage.commit,
|
|
1731
|
+
success: true,
|
|
1732
|
+
};
|
|
1733
|
+
const commitStone = await this.createSyncMsgStone({
|
|
1734
|
+
data: commitData,
|
|
1735
|
+
localSpace: mySpace,
|
|
1736
|
+
metaspace
|
|
1737
|
+
});
|
|
1738
|
+
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1739
|
+
prevSagaIbGib: sagaIbGib,
|
|
1740
|
+
msgStones: [commitStone],
|
|
1741
|
+
sessionIdentity: identity,
|
|
1742
|
+
localSpace: mySpace,
|
|
1743
|
+
metaspace,
|
|
1744
|
+
});
|
|
1745
|
+
return commitFrame;
|
|
1746
|
+
}
|
|
1747
|
+
catch (error) {
|
|
1748
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1749
|
+
throw error;
|
|
1750
|
+
}
|
|
1751
|
+
finally {
|
|
1752
|
+
if (logalot) {
|
|
1753
|
+
console.log(`${lc} complete.`);
|
|
1684
1754
|
}
|
|
1685
1755
|
}
|
|
1686
1756
|
}
|
|
1687
|
-
async handleCommitFrame({ sagaIbGib,
|
|
1757
|
+
async handleCommitFrame({ sagaIbGib, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1688
1758
|
const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
|
|
1689
|
-
|
|
1690
|
-
|
|
1759
|
+
try {
|
|
1760
|
+
if (logalot) {
|
|
1761
|
+
console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`);
|
|
1762
|
+
}
|
|
1763
|
+
let resNextSagaFrameInfo;
|
|
1764
|
+
// Sender Logic (Finalizing):
|
|
1765
|
+
// If we are here, we received a Commit frame from the Peer.
|
|
1766
|
+
// This implies the Peer has successfully committed.
|
|
1767
|
+
// We should now:
|
|
1768
|
+
// 1. Validate (implicitly done by receiving valid frame)
|
|
1769
|
+
// 2. Perform our own cleanup (Temp -> Dest, if applicable)
|
|
1770
|
+
// 3. Return saga completion.
|
|
1771
|
+
// one last validate entire history?
|
|
1772
|
+
const history = await getFullSyncSagaHistory({
|
|
1773
|
+
sagaIbGib,
|
|
1774
|
+
space: mySpace,
|
|
1775
|
+
});
|
|
1776
|
+
const validationErrors = await validateFullSyncSagaHistory({
|
|
1777
|
+
history,
|
|
1778
|
+
});
|
|
1779
|
+
if (validationErrors.length > 0) {
|
|
1780
|
+
const errorCommitFrame = await this.createCommitFrame({
|
|
1781
|
+
sagaIbGib,
|
|
1782
|
+
metaspace,
|
|
1783
|
+
mySpace,
|
|
1784
|
+
identity,
|
|
1785
|
+
errors: validationErrors,
|
|
1786
|
+
});
|
|
1787
|
+
return { frame: errorCommitFrame, }; /* <<<< returns early */
|
|
1788
|
+
}
|
|
1789
|
+
await this.executeLocalCommit({
|
|
1790
|
+
deltaFrame: sagaIbGib,
|
|
1791
|
+
sagaHistory: history,
|
|
1792
|
+
localSpace: mySpace,
|
|
1793
|
+
localTempSpace: myTempSpace,
|
|
1794
|
+
metaspace,
|
|
1795
|
+
});
|
|
1796
|
+
// todo: implement explicit cleanup logic here and in peer
|
|
1797
|
+
console.error(`${lc} NAG ERROR (NOT THROWN): implement cleanup logic, including add a cleanup method to the peer (E: 3a9a24befb98a981a88fbdbf52920e26)`);
|
|
1798
|
+
if (logalot) {
|
|
1799
|
+
console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
|
|
1800
|
+
}
|
|
1801
|
+
// the holy grail!
|
|
1802
|
+
return { sagaComplete: true };
|
|
1691
1803
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1804
|
+
catch (error) {
|
|
1805
|
+
const emsg = `${lc} ${extractErrorMsg(error)}`;
|
|
1806
|
+
console.error(emsg);
|
|
1807
|
+
const errorCommitFrame = await this.createCommitFrame({
|
|
1808
|
+
sagaIbGib,
|
|
1809
|
+
errors: [emsg],
|
|
1810
|
+
metaspace,
|
|
1811
|
+
mySpace,
|
|
1812
|
+
identity,
|
|
1813
|
+
});
|
|
1814
|
+
return { frame: errorCommitFrame };
|
|
1815
|
+
}
|
|
1816
|
+
finally {
|
|
1817
|
+
if (logalot) {
|
|
1818
|
+
console.log(`${lc} complete.`);
|
|
1819
|
+
}
|
|
1702
1820
|
}
|
|
1703
|
-
// return { responseWasNull: true };
|
|
1704
|
-
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1705
1821
|
}
|
|
1706
1822
|
// #endregion Handlers
|
|
1707
1823
|
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|
|
@@ -1734,6 +1850,69 @@ export class SyncSagaCoordinator {
|
|
|
1734
1850
|
}
|
|
1735
1851
|
}
|
|
1736
1852
|
}
|
|
1853
|
+
async getPayloadsForRequestedInfos({ deltaRequestAddrInfos, mySpace, }) {
|
|
1854
|
+
const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
|
|
1855
|
+
try {
|
|
1856
|
+
if (logalot) {
|
|
1857
|
+
console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`);
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* graph of ibgibs we will send to the receiver. addr-based, so will
|
|
1861
|
+
* already be unique (no need to call `unique` on the domains
|
|
1862
|
+
* ibgibs)
|
|
1863
|
+
*/
|
|
1864
|
+
const outgoingPayloadIbGibsDomainGraph = {};
|
|
1865
|
+
for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
|
|
1866
|
+
let deltaDepGraph;
|
|
1867
|
+
if (latestAddrAlreadyHave) {
|
|
1868
|
+
// already has some, so only get the delta
|
|
1869
|
+
// remember: if we didn't have the other's latest addr, then
|
|
1870
|
+
// this would be in the conflicts not requested addrs
|
|
1871
|
+
deltaDepGraph = await getDeltaDependencyGraph({
|
|
1872
|
+
ibGibAddr: addr,
|
|
1873
|
+
latestCommonFrameAddr: latestAddrAlreadyHave,
|
|
1874
|
+
space: mySpace,
|
|
1875
|
+
live: true,
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
else {
|
|
1879
|
+
// doesn't have anything, so get the entire dependency graph
|
|
1880
|
+
// INEFFICIENT: we've already gotten all of the domain
|
|
1881
|
+
// dependencies in initDomainGraph, but getDependencyGraph
|
|
1882
|
+
// only works against a space so we are going to rerun this.
|
|
1883
|
+
// an optimization would be to adapt/create a new
|
|
1884
|
+
// getDependencyGraph to work against an existing flat ibgib
|
|
1885
|
+
// map.
|
|
1886
|
+
deltaDepGraph = await getDependencyGraph({
|
|
1887
|
+
ibGibAddr: addr,
|
|
1888
|
+
space: mySpace,
|
|
1889
|
+
live: true,
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
const depGraphSize = Object.keys(deltaDepGraph).length;
|
|
1893
|
+
if (depGraphSize === 0) {
|
|
1894
|
+
throw new Error(`(UNEXPECTED) couldn't get requested addrs in mySpace (${mySpace.ib})? How did the receiver know to ask for these addrs if they weren't in our original graph? (E: 281eaebcdf77dd73e8245b2872100826)`);
|
|
1895
|
+
}
|
|
1896
|
+
else {
|
|
1897
|
+
// we have dependencies!
|
|
1898
|
+
Object.values(deltaDepGraph).forEach(x => {
|
|
1899
|
+
outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
const result = Object.values(outgoingPayloadIbGibsDomainGraph);
|
|
1904
|
+
return result;
|
|
1905
|
+
}
|
|
1906
|
+
catch (error) {
|
|
1907
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1908
|
+
throw error;
|
|
1909
|
+
}
|
|
1910
|
+
finally {
|
|
1911
|
+
if (logalot) {
|
|
1912
|
+
console.log(`${lc} complete.`);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1737
1916
|
/**
|
|
1738
1917
|
* Evolves the saga timeline with a new frame.
|
|
1739
1918
|
*/
|