@ibgib/core-gib 0.1.26 → 0.1.27
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 +16 -1
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +101 -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 +29 -16
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +378 -392
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +30 -1
- 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 +92 -4
- 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 +408 -432
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +18 -18
- package/src/sync/sync-types.mts +38 -1
|
@@ -3,7 +3,7 @@ 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
9
|
import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.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)`);
|
|
@@ -585,7 +585,9 @@ export class SyncSagaCoordinator {
|
|
|
585
585
|
metaspace,
|
|
586
586
|
localSpace,
|
|
587
587
|
});
|
|
588
|
-
|
|
588
|
+
if (logalot) {
|
|
589
|
+
console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`);
|
|
590
|
+
}
|
|
589
591
|
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
590
592
|
}
|
|
591
593
|
catch (error) {
|
|
@@ -691,7 +693,7 @@ export class SyncSagaCoordinator {
|
|
|
691
693
|
console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
|
|
692
694
|
}
|
|
693
695
|
// Get Stage from Stone (or Frame for Init fallback)
|
|
694
|
-
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space:
|
|
696
|
+
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
695
697
|
if (logalot) {
|
|
696
698
|
console.log(`${lc} handling frame stage: ${stage}`);
|
|
697
699
|
}
|
|
@@ -705,11 +707,8 @@ export class SyncSagaCoordinator {
|
|
|
705
707
|
nextFrameInfo = await this.handleInitFrame({
|
|
706
708
|
sagaIbGib,
|
|
707
709
|
messageData: messageData,
|
|
708
|
-
metaspace,
|
|
709
|
-
|
|
710
|
-
myTempSpace: myTempSpace,
|
|
711
|
-
identity,
|
|
712
|
-
identitySecret
|
|
710
|
+
metaspace, mySpace, myTempSpace,
|
|
711
|
+
identity, identitySecret
|
|
713
712
|
});
|
|
714
713
|
break;
|
|
715
714
|
case SyncStage.ack:
|
|
@@ -717,24 +716,33 @@ export class SyncSagaCoordinator {
|
|
|
717
716
|
throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`);
|
|
718
717
|
}
|
|
719
718
|
nextFrameInfo = await this.handleAckFrame({
|
|
719
|
+
sagaContext,
|
|
720
720
|
sagaIbGib,
|
|
721
|
-
srcGraph,
|
|
722
721
|
initDomainGraph,
|
|
723
|
-
metaspace,
|
|
724
|
-
destSpace: mySpace,
|
|
725
|
-
tempSpace: myTempSpace,
|
|
722
|
+
metaspace, mySpace, myTempSpace,
|
|
726
723
|
identity,
|
|
727
724
|
});
|
|
728
725
|
break;
|
|
729
726
|
case SyncStage.delta:
|
|
730
|
-
nextFrameInfo = await this.handleDeltaFrame({
|
|
727
|
+
nextFrameInfo = await this.handleDeltaFrame({
|
|
728
|
+
sagaContext,
|
|
729
|
+
sagaIbGib,
|
|
730
|
+
srcGraph,
|
|
731
|
+
metaspace,
|
|
732
|
+
mySpace,
|
|
733
|
+
myTempSpace,
|
|
734
|
+
identity,
|
|
735
|
+
});
|
|
731
736
|
break;
|
|
732
737
|
case SyncStage.commit:
|
|
733
|
-
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace,
|
|
738
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
|
|
734
739
|
break;
|
|
735
740
|
default:
|
|
736
741
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
737
742
|
}
|
|
743
|
+
if (logalot) {
|
|
744
|
+
console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`);
|
|
745
|
+
}
|
|
738
746
|
return { errorMsg: undefined, nextFrameInfo, };
|
|
739
747
|
}
|
|
740
748
|
catch (error) {
|
|
@@ -758,7 +766,7 @@ export class SyncSagaCoordinator {
|
|
|
758
766
|
* The Receiver performs Gap Analysis here:
|
|
759
767
|
* 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
|
|
760
768
|
* 2. Identifies what Sender needs (`pushOfferAddrs`).
|
|
761
|
-
* 3. Identifies what Receiver needs (`
|
|
769
|
+
* 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
|
|
762
770
|
* 4. Returns an `Ack` frame containing these lists.
|
|
763
771
|
*/
|
|
764
772
|
async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
|
|
@@ -995,7 +1003,7 @@ export class SyncSagaCoordinator {
|
|
|
995
1003
|
}
|
|
996
1004
|
const ackStone = await this.createSyncMsgStone({
|
|
997
1005
|
data: ackData,
|
|
998
|
-
localSpace:
|
|
1006
|
+
localSpace: mySpace,
|
|
999
1007
|
metaspace,
|
|
1000
1008
|
});
|
|
1001
1009
|
if (logalot) {
|
|
@@ -1092,19 +1100,23 @@ export class SyncSagaCoordinator {
|
|
|
1092
1100
|
* **Execution Context**: **Sender (Local)**.
|
|
1093
1101
|
*
|
|
1094
1102
|
* The Sender reacts to the Receiver's requirements:
|
|
1095
|
-
* 1.
|
|
1096
|
-
*
|
|
1103
|
+
* 1. `deltaRequestAddrInfos`: Receiver wants this data. Sender takes this
|
|
1104
|
+
* into account, plus gathers it from `initDomainGraph` and puts them in
|
|
1105
|
+
* `payloadIbGibs` (for next frame).
|
|
1106
|
+
* 2. `pushOfferAddrs`: Receiver has newer data. these should have been
|
|
1107
|
+
* included in the incoming context from the receiver..
|
|
1097
1108
|
*
|
|
1098
1109
|
* Returns a `Delta` frame.
|
|
1099
1110
|
*/
|
|
1100
|
-
async handleAckFrame({
|
|
1111
|
+
async handleAckFrame({ sagaContext, sagaIbGib, initDomainGraph, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1101
1112
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
1102
1113
|
try {
|
|
1103
1114
|
if (logalot) {
|
|
1104
1115
|
console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
|
|
1105
1116
|
}
|
|
1106
|
-
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space:
|
|
1117
|
+
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
1107
1118
|
const ackData = messageData;
|
|
1119
|
+
// #region sanity/validation
|
|
1108
1120
|
if (!ackData) {
|
|
1109
1121
|
throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
|
|
1110
1122
|
}
|
|
@@ -1114,6 +1126,10 @@ export class SyncSagaCoordinator {
|
|
|
1114
1126
|
if (logalot) {
|
|
1115
1127
|
console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`);
|
|
1116
1128
|
}
|
|
1129
|
+
if (!sagaIbGib.data) {
|
|
1130
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`);
|
|
1131
|
+
}
|
|
1132
|
+
// #region sanity/validation
|
|
1117
1133
|
// 1. Check for Conflicts
|
|
1118
1134
|
const conflicts = ackData.conflicts || [];
|
|
1119
1135
|
console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`);
|
|
@@ -1128,7 +1144,12 @@ export class SyncSagaCoordinator {
|
|
|
1128
1144
|
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1129
1145
|
}
|
|
1130
1146
|
// at this point, if we have conflicts, they are non-terminal
|
|
1131
|
-
|
|
1147
|
+
/**
|
|
1148
|
+
* at this point, we only request ibgibs for conflicted timelines.
|
|
1149
|
+
* If the receiver had known of any ibgibs this sender needed, it
|
|
1150
|
+
* would have been in the push offer.
|
|
1151
|
+
*/
|
|
1152
|
+
const outgoingDeltaAddrRequestInfos = []; // Additional requests for merging
|
|
1132
1153
|
if (conflicts.length > 0) {
|
|
1133
1154
|
console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
|
|
1134
1155
|
// We need to resolve these.
|
|
@@ -1138,8 +1159,8 @@ export class SyncSagaCoordinator {
|
|
|
1138
1159
|
// 3. Request that data (as Delta Reqs)
|
|
1139
1160
|
// 4. (Later in Delta Phase) Perform Merge.
|
|
1140
1161
|
// BUT: The Delta Phase is usually generic "Send me these Addrs".
|
|
1141
|
-
// If we just add to `
|
|
1142
|
-
// wait. `ackData.
|
|
1162
|
+
// If we just add to `deltaRequestAddrInfos` (which are requests for Sender to send to Receiver?),
|
|
1163
|
+
// wait. `ackData.deltaRequestAddrInfos` are what RECEIVER wants from SENDER.
|
|
1143
1164
|
// We (Sender) are processing the Ack.
|
|
1144
1165
|
// We need to request data FROM Receiver.
|
|
1145
1166
|
// But the protocol 'Ack' step typically leads to 'Delta' (Sender sending data).
|
|
@@ -1206,8 +1227,8 @@ export class SyncSagaCoordinator {
|
|
|
1206
1227
|
// // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
|
|
1207
1228
|
// for (const addr of receiverOnlyAddrs) {
|
|
1208
1229
|
// // Add the frame itself first
|
|
1209
|
-
// if (!
|
|
1210
|
-
//
|
|
1230
|
+
// if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
|
|
1231
|
+
// outgoingDeltaAddrRequestInfos.push(addr);
|
|
1211
1232
|
// }
|
|
1212
1233
|
// // Get the frame's delta dependencies (skip LCA's deps)
|
|
1213
1234
|
// try {
|
|
@@ -1223,8 +1244,8 @@ export class SyncSagaCoordinator {
|
|
|
1223
1244
|
// if (frameDeltaDeps) {
|
|
1224
1245
|
// // Add all delta dependencies (Object.keys gives us the addresses)
|
|
1225
1246
|
// Object.keys(frameDeltaDeps).forEach(depAddr => {
|
|
1226
|
-
// if (!
|
|
1227
|
-
//
|
|
1247
|
+
// if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
|
|
1248
|
+
// outgoingDeltaAddrRequestInfos.push(depAddr);
|
|
1228
1249
|
// }
|
|
1229
1250
|
// });
|
|
1230
1251
|
// }
|
|
@@ -1233,117 +1254,63 @@ export class SyncSagaCoordinator {
|
|
|
1233
1254
|
// console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1234
1255
|
// }
|
|
1235
1256
|
// }
|
|
1236
|
-
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${
|
|
1257
|
+
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
|
|
1237
1258
|
// } else {
|
|
1238
1259
|
// console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1239
1260
|
// }
|
|
1240
1261
|
}
|
|
1241
|
-
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts.
|
|
1262
|
+
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
|
|
1242
1263
|
}
|
|
1243
1264
|
else {
|
|
1244
1265
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1245
1266
|
}
|
|
1246
1267
|
// 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
|
-
}
|
|
1268
|
+
/**
|
|
1269
|
+
* these were requested addrs on the INCOMING frame (the ack data).
|
|
1270
|
+
*
|
|
1271
|
+
* This is in contrast to any OUTGOING requests we make in this
|
|
1272
|
+
* method.
|
|
1273
|
+
*/
|
|
1274
|
+
const payloadIbGibsDomain = await this.getPayloadsForRequestedInfos({
|
|
1275
|
+
deltaRequestAddrInfos: ackData.deltaRequestAddrInfos || [],
|
|
1276
|
+
mySpace,
|
|
1277
|
+
});
|
|
1326
1278
|
// 3. Create Delta Frame
|
|
1327
|
-
const sagaId = ackData.sagaId;
|
|
1328
1279
|
const deltaData = {
|
|
1329
1280
|
sagaId: sagaIbGib.data.uuid,
|
|
1330
1281
|
stage: SyncStage.delta,
|
|
1331
|
-
|
|
1332
|
-
|
|
1282
|
+
/**
|
|
1283
|
+
* we're sending these domain ibgibs as payload (they were
|
|
1284
|
+
* requested by receiver)
|
|
1285
|
+
*/
|
|
1286
|
+
payloadAddrsDomain: payloadIbGibsDomain.length > 0 ?
|
|
1287
|
+
payloadIbGibsDomain.map(x => getIbGibAddr({ ibGib: x })) :
|
|
1288
|
+
undefined,
|
|
1289
|
+
/**
|
|
1290
|
+
* we're asking for these addrs
|
|
1291
|
+
*/
|
|
1292
|
+
deltaRequestAddrInfos: outgoingDeltaAddrRequestInfos.length > 0 ?
|
|
1293
|
+
outgoingDeltaAddrRequestInfos :
|
|
1294
|
+
undefined,
|
|
1295
|
+
/**
|
|
1296
|
+
* if we have no changes and request none, propose commit to
|
|
1297
|
+
* finish the sync transaction.
|
|
1298
|
+
*/
|
|
1299
|
+
proposeCommit: payloadIbGibsDomain.length === 0 && outgoingDeltaAddrRequestInfos.length === 0,
|
|
1333
1300
|
};
|
|
1334
1301
|
if (logalot) {
|
|
1335
1302
|
console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`);
|
|
1336
1303
|
}
|
|
1337
1304
|
const deltaStone = await this.createSyncMsgStone({
|
|
1338
1305
|
data: deltaData,
|
|
1339
|
-
localSpace:
|
|
1306
|
+
localSpace: mySpace,
|
|
1340
1307
|
metaspace,
|
|
1341
1308
|
});
|
|
1342
1309
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1343
1310
|
prevSagaIbGib: sagaIbGib,
|
|
1344
1311
|
msgStones: [deltaStone],
|
|
1345
1312
|
sessionIdentity: identity,
|
|
1346
|
-
localSpace:
|
|
1313
|
+
localSpace: mySpace,
|
|
1347
1314
|
metaspace,
|
|
1348
1315
|
});
|
|
1349
1316
|
if (logalot) {
|
|
@@ -1354,9 +1321,70 @@ export class SyncSagaCoordinator {
|
|
|
1354
1321
|
if (identity) {
|
|
1355
1322
|
payloadIbGibsControl.push(identity);
|
|
1356
1323
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1324
|
+
return { frame: deltaFrame, payloadIbGibsDomain, };
|
|
1325
|
+
}
|
|
1326
|
+
catch (error) {
|
|
1327
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1328
|
+
throw error;
|
|
1329
|
+
}
|
|
1330
|
+
finally {
|
|
1331
|
+
if (logalot) {
|
|
1332
|
+
console.log(`${lc} complete.`);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
async getPayloadsForRequestedInfos({ deltaRequestAddrInfos, mySpace, }) {
|
|
1337
|
+
const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
|
|
1338
|
+
try {
|
|
1339
|
+
if (logalot) {
|
|
1340
|
+
console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`);
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* graph of ibgibs we will send to the receiver. addr-based, so will
|
|
1344
|
+
* already be unique (no need to call `unique` on the domains
|
|
1345
|
+
* ibgibs)
|
|
1346
|
+
*/
|
|
1347
|
+
const outgoingPayloadIbGibsDomainGraph = {};
|
|
1348
|
+
for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
|
|
1349
|
+
let deltaDepGraph;
|
|
1350
|
+
if (latestAddrAlreadyHave) {
|
|
1351
|
+
// already has some, so only get the delta
|
|
1352
|
+
// remember: if we didn't have the other's latest addr, then
|
|
1353
|
+
// this would be in the conflicts not requested addrs
|
|
1354
|
+
deltaDepGraph = await getDeltaDependencyGraph({
|
|
1355
|
+
ibGibAddr: addr,
|
|
1356
|
+
latestCommonFrameAddr: latestAddrAlreadyHave,
|
|
1357
|
+
space: mySpace,
|
|
1358
|
+
live: true,
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
else {
|
|
1362
|
+
// doesn't have anything, so get the entire dependency graph
|
|
1363
|
+
// INEFFICIENT: we've already gotten all of the domain
|
|
1364
|
+
// dependencies in initDomainGraph, but getDependencyGraph
|
|
1365
|
+
// only works against a space so we are going to rerun this.
|
|
1366
|
+
// an optimization would be to adapt/create a new
|
|
1367
|
+
// getDependencyGraph to work against an existing flat ibgib
|
|
1368
|
+
// map.
|
|
1369
|
+
deltaDepGraph = await getDependencyGraph({
|
|
1370
|
+
ibGibAddr: addr,
|
|
1371
|
+
space: mySpace,
|
|
1372
|
+
live: true,
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
const depGraphSize = Object.keys(deltaDepGraph).length;
|
|
1376
|
+
if (depGraphSize === 0) {
|
|
1377
|
+
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)`);
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
// we have dependencies!
|
|
1381
|
+
Object.values(deltaDepGraph).forEach(x => {
|
|
1382
|
+
outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
const result = Object.values(outgoingPayloadIbGibsDomainGraph);
|
|
1387
|
+
return result;
|
|
1360
1388
|
}
|
|
1361
1389
|
catch (error) {
|
|
1362
1390
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1378,275 +1406,165 @@ export class SyncSagaCoordinator {
|
|
|
1378
1406
|
* 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
|
|
1379
1407
|
* 3. **Completion**: If no more requests, transitions to `Commit`.
|
|
1380
1408
|
*/
|
|
1381
|
-
async handleDeltaFrame({ sagaIbGib, srcGraph,
|
|
1409
|
+
async handleDeltaFrame({ sagaContext, sagaIbGib, srcGraph, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1382
1410
|
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.
|
|
1417
|
-
}
|
|
1418
|
-
else {
|
|
1419
|
-
console.warn(`${lc} Failed to retrieve payloads listed in delta: ${payloadAddrs.join(', ')}`);
|
|
1411
|
+
try {
|
|
1412
|
+
if (logalot) {
|
|
1413
|
+
console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`);
|
|
1420
1414
|
}
|
|
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
|
-
}
|
|
1415
|
+
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
1416
|
+
const deltaData = messageData;
|
|
1417
|
+
// #region validate/sanity
|
|
1418
|
+
if (!deltaData) {
|
|
1419
|
+
throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
|
|
1434
1420
|
}
|
|
1435
|
-
if (
|
|
1436
|
-
|
|
1437
|
-
const ibGibAddr = getIbGibAddr({ ibGib });
|
|
1438
|
-
if (!outgoingAddrsSet.has(ibGibAddr)) {
|
|
1439
|
-
outgoingPayload.push(ibGib);
|
|
1440
|
-
outgoingAddrsSet.add(ibGibAddr);
|
|
1441
|
-
}
|
|
1442
|
-
// Expand to include full dependency graph for this ibGib
|
|
1443
|
-
// (Receiver needs all deps to properly process/merge)
|
|
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
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
catch (depError) {
|
|
1460
|
-
console.warn(`${lc} [CONFLICT DEBUG] Error expanding deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1461
|
-
}
|
|
1421
|
+
if (deltaData.stage !== SyncStage.delta) {
|
|
1422
|
+
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1462
1423
|
}
|
|
1463
|
-
|
|
1464
|
-
console.
|
|
1424
|
+
if (logalot) {
|
|
1425
|
+
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1465
1426
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1427
|
+
// #endregion validate/sanity
|
|
1428
|
+
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1429
|
+
const peerProposesCommit = deltaData.proposeCommit || false;
|
|
1430
|
+
/**
|
|
1431
|
+
* these are already in the local temp space
|
|
1432
|
+
*/
|
|
1433
|
+
const receivedPayloadIbGibs = sagaContext.payloadIbGibsDomain ?? [];
|
|
1434
|
+
// 2. Fulfill Peer Requests (Outgoing Payload with Delta Dependencies)
|
|
1435
|
+
console.log(`${lc} [CONFLICT DEBUG] Fulfilling ${(deltaData.deltaRequestAddrInfos || []).length} peer requests`);
|
|
1436
|
+
const outgoingPayload = await this.getPayloadsForRequestedInfos({
|
|
1437
|
+
deltaRequestAddrInfos: deltaData.deltaRequestAddrInfos || [],
|
|
1438
|
+
mySpace,
|
|
1439
|
+
});
|
|
1440
|
+
console.log(`${lc} [CONFLICT DEBUG] Outgoing payload size (with deps): ${outgoingPayload.length}`);
|
|
1441
|
+
// 3. Execute Merges (If applicable)
|
|
1442
|
+
// Check if we have pending conflicts that we CAN resolve now that we have data.
|
|
1443
|
+
// We look at the Saga History (Ack Frame) to find conflicts.
|
|
1444
|
+
// Optimization: Do this only if we received payloads.
|
|
1445
|
+
const mergeResultIbGibs = [];
|
|
1446
|
+
console.log(`${lc} [CONFLICT DEBUG] Checking for merge. receivedPayloadIbGibs.length: ${receivedPayloadIbGibs.length}`);
|
|
1447
|
+
if (receivedPayloadIbGibs.length > 0) {
|
|
1448
|
+
console.log(`${lc} [TEST DEBUG] Received Payloads (${receivedPayloadIbGibs.length}). Checking for conflicts/merges...`);
|
|
1449
|
+
// Find the Ack frame in history to get conflicts
|
|
1450
|
+
// Optimization: Batch fetch history from `sagaIbGib.rel8ns.past`
|
|
1451
|
+
// V1 timelines carry full history in `past`.
|
|
1452
|
+
const pastAddrs = sagaIbGib.rel8ns?.past || [];
|
|
1453
|
+
console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
|
|
1454
|
+
let ackData;
|
|
1455
|
+
if (pastAddrs.length > 0) {
|
|
1456
|
+
// Batch fetch all past frames
|
|
1457
|
+
const resPast = await getFromSpace({ addrs: pastAddrs, space: myTempSpace });
|
|
1458
|
+
if (resPast.success && resPast.ibGibs) {
|
|
1459
|
+
// Iterate backwards (most recent first) to find the latest Ack
|
|
1460
|
+
for (let i = resPast.ibGibs.length - 1; i >= 0; i--) {
|
|
1461
|
+
const pastFrame = resPast.ibGibs[i];
|
|
1462
|
+
const messageStone = await getSyncSagaMessageFromFrame({
|
|
1463
|
+
frameIbGib: pastFrame,
|
|
1464
|
+
space: myTempSpace
|
|
1465
|
+
});
|
|
1466
|
+
if (messageStone?.data?.stage === SyncStage.ack) {
|
|
1467
|
+
ackData = messageStone.data;
|
|
1468
|
+
console.log(`${lc} [TEST DEBUG] Found Ack Frame. Conflicts: ${ackData.conflicts?.length || 0}`);
|
|
1469
|
+
break;
|
|
1470
|
+
}
|
|
1497
1471
|
}
|
|
1498
1472
|
}
|
|
1499
1473
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1474
|
+
if (ackData && ackData.conflicts) {
|
|
1475
|
+
const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
|
|
1476
|
+
for (const conflict of optimisticConflicts) {
|
|
1477
|
+
const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
|
|
1478
|
+
// We are Sender (usually) here if we are merging.
|
|
1479
|
+
// Check if we have the history needed (timelineAddrs).
|
|
1480
|
+
// Specifically, we needed the `receiverOnly` parts.
|
|
1481
|
+
// We blindly attempt merge if we have both tips accessible?
|
|
1482
|
+
// We need `receiverTip` (localAddr in Ack) and `senderTip` (remoteAddr).
|
|
1483
|
+
// Check if we have receiverTip in space
|
|
1484
|
+
console.log(`${lc} [CONFLICT DEBUG] Attempting merge for conflict. ReceiverTip: ${receiverTip}, SenderTip: ${senderTip}`);
|
|
1485
|
+
const resRecTip = await getFromSpace({ addr: receiverTip, space: myTempSpace }); // Check myTempSpace for incoming data
|
|
1486
|
+
console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in myTempSpace: ${!!resRecTip.ibGibs?.[0]}`);
|
|
1487
|
+
if (resRecTip.success && resRecTip.ibGibs?.[0]) {
|
|
1488
|
+
// We have the tip!
|
|
1489
|
+
// Do we have the full history?
|
|
1490
|
+
// `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
|
|
1491
|
+
// If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
|
|
1492
|
+
// Perform Merge!
|
|
1493
|
+
try {
|
|
1494
|
+
const mergeResult = await mergeDivergentTimelines({
|
|
1495
|
+
tipA: (await getFromSpace({ addr: senderTip, space: mySpace })).ibGibs[0], // Our tip from destSpace
|
|
1496
|
+
tipB: resRecTip.ibGibs[0], // Their tip (from myTempSpace)
|
|
1497
|
+
space: myTempSpace, // Merge uses myTempSpace
|
|
1498
|
+
metaspace,
|
|
1499
|
+
});
|
|
1500
|
+
if (mergeResult) {
|
|
1501
|
+
console.log(`${lc} [TEST DEBUG] Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
|
|
1502
|
+
if (logalot) {
|
|
1503
|
+
console.log(`${lc} Merge success! New Tip: ${getIbGibAddr({ ibGib: mergeResult })}`);
|
|
1504
|
+
}
|
|
1505
|
+
mergeResultIbGibs.push(mergeResult);
|
|
1506
|
+
outgoingPayload.push(mergeResult); // Send result to peer
|
|
1531
1507
|
}
|
|
1532
|
-
mergeResultIbGibs.push(mergeResult);
|
|
1533
|
-
outgoingPayload.push(mergeResult); // Send result to peer
|
|
1534
1508
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1509
|
+
catch (e) {
|
|
1510
|
+
console.error(`${lc} Merge failed: ${e}`);
|
|
1511
|
+
// If merge fails, we might Abort or just continue?
|
|
1512
|
+
}
|
|
1539
1513
|
}
|
|
1540
1514
|
}
|
|
1541
1515
|
}
|
|
1542
1516
|
}
|
|
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.
|
|
1517
|
+
// 4. Determine Next Action
|
|
1518
|
+
// We have `outgoingPayload` (Requests + Merge Results).
|
|
1519
|
+
// Does Peer have outstanding requests? No, we fulfilled `peerRequests`.
|
|
1520
|
+
// Do WE have outstanding requests?
|
|
1521
|
+
// We might if `mergeResult` requires further sync? Usually no, result is complete.
|
|
1522
|
+
const myRequests = []; // If we had more needs (e.g. partial payload), we'd add here.
|
|
1523
|
+
const hasOutgoing = outgoingPayload.length > 0;
|
|
1524
|
+
const hasMyRequests = myRequests.length > 0;
|
|
1525
|
+
if (hasOutgoing || hasMyRequests) {
|
|
1526
|
+
// We have business to attend to -> Send Delta
|
|
1625
1527
|
const responseDeltaData = {
|
|
1626
1528
|
sagaId: deltaData.sagaId,
|
|
1627
1529
|
stage: SyncStage.delta,
|
|
1628
|
-
|
|
1629
|
-
|
|
1530
|
+
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1531
|
+
requests: hasMyRequests ? myRequests : undefined,
|
|
1532
|
+
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1533
|
+
// Wait. If we send data, we are NOT committing yet.
|
|
1534
|
+
// We are sending data. The OTHER side must ingest it.
|
|
1535
|
+
// So proposeCommit = true?
|
|
1536
|
+
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1537
|
+
// Yes.
|
|
1630
1538
|
};
|
|
1539
|
+
// BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
|
|
1540
|
+
// We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
|
|
1541
|
+
// So yes, proposeCommit = true.
|
|
1542
|
+
responseDeltaData.proposeCommit = true;
|
|
1631
1543
|
const deltaStone = await this.createSyncMsgStone({
|
|
1632
1544
|
data: responseDeltaData,
|
|
1633
|
-
localSpace:
|
|
1634
|
-
metaspace
|
|
1545
|
+
localSpace: mySpace,
|
|
1546
|
+
metaspace,
|
|
1635
1547
|
});
|
|
1636
1548
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1637
1549
|
prevSagaIbGib: sagaIbGib,
|
|
1638
1550
|
msgStones: [deltaStone],
|
|
1639
1551
|
sessionIdentity: identity,
|
|
1640
|
-
localSpace:
|
|
1641
|
-
metaspace
|
|
1552
|
+
localSpace: mySpace,
|
|
1553
|
+
metaspace,
|
|
1642
1554
|
});
|
|
1643
|
-
//
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1555
|
+
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1556
|
+
const payloadIbGibsControl = [deltaFrame, deltaStone];
|
|
1557
|
+
if (identity) {
|
|
1558
|
+
payloadIbGibsControl.push(identity);
|
|
1559
|
+
}
|
|
1560
|
+
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1561
|
+
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1562
|
+
// throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1563
|
+
}
|
|
1564
|
+
else {
|
|
1565
|
+
// We have nothing to send.
|
|
1566
|
+
if (peerProposesCommit) {
|
|
1567
|
+
// Peer is done. We are done. -> Commit.
|
|
1650
1568
|
const commitData = {
|
|
1651
1569
|
sagaId: deltaData.sagaId,
|
|
1652
1570
|
stage: SyncStage.commit,
|
|
@@ -1654,54 +1572,122 @@ export class SyncSagaCoordinator {
|
|
|
1654
1572
|
};
|
|
1655
1573
|
const commitStone = await this.createSyncMsgStone({
|
|
1656
1574
|
data: commitData,
|
|
1657
|
-
localSpace:
|
|
1575
|
+
localSpace: mySpace,
|
|
1658
1576
|
metaspace
|
|
1659
1577
|
});
|
|
1660
1578
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1661
|
-
prevSagaIbGib:
|
|
1579
|
+
prevSagaIbGib: sagaIbGib,
|
|
1662
1580
|
msgStones: [commitStone],
|
|
1663
1581
|
sessionIdentity: identity,
|
|
1664
|
-
localSpace:
|
|
1582
|
+
localSpace: mySpace,
|
|
1665
1583
|
metaspace
|
|
1666
1584
|
});
|
|
1667
1585
|
// Build control payloads for commit
|
|
1668
|
-
const
|
|
1586
|
+
const commitCtrlPayloads = [commitFrame, commitStone];
|
|
1669
1587
|
if (identity) {
|
|
1670
|
-
|
|
1588
|
+
commitCtrlPayloads.push(identity);
|
|
1671
1589
|
}
|
|
1672
|
-
|
|
1673
|
-
// return { frame: commitFrame, };
|
|
1674
|
-
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1590
|
+
return { frame: commitFrame, };
|
|
1675
1591
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1592
|
+
else {
|
|
1593
|
+
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
1594
|
+
// But we are empty.
|
|
1595
|
+
// So WE propose commit.
|
|
1596
|
+
const responseDeltaData = {
|
|
1597
|
+
sagaId: deltaData.sagaId,
|
|
1598
|
+
stage: SyncStage.delta,
|
|
1599
|
+
proposeCommit: true,
|
|
1600
|
+
};
|
|
1601
|
+
const deltaStone = await this.createSyncMsgStone({
|
|
1602
|
+
data: responseDeltaData,
|
|
1603
|
+
localSpace: mySpace,
|
|
1604
|
+
metaspace
|
|
1605
|
+
});
|
|
1606
|
+
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1607
|
+
prevSagaIbGib: sagaIbGib,
|
|
1608
|
+
msgStones: [deltaStone],
|
|
1609
|
+
sessionIdentity: identity,
|
|
1610
|
+
localSpace: mySpace,
|
|
1611
|
+
metaspace
|
|
1612
|
+
});
|
|
1613
|
+
// Check if PEER proposed commit
|
|
1614
|
+
if (deltaData.proposeCommit) {
|
|
1615
|
+
if (logalot) {
|
|
1616
|
+
console.log(`${lc} Peer proposed commit. Accepting & Committing.`);
|
|
1617
|
+
}
|
|
1618
|
+
// Peer wants to commit and has no more requests.
|
|
1619
|
+
// We should Commit.
|
|
1620
|
+
const commitData = {
|
|
1621
|
+
sagaId: deltaData.sagaId,
|
|
1622
|
+
stage: SyncStage.commit,
|
|
1623
|
+
success: true,
|
|
1624
|
+
};
|
|
1625
|
+
const commitStone = await this.createSyncMsgStone({
|
|
1626
|
+
data: commitData,
|
|
1627
|
+
localSpace: mySpace,
|
|
1628
|
+
metaspace
|
|
1629
|
+
});
|
|
1630
|
+
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1631
|
+
prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
|
|
1632
|
+
msgStones: [commitStone],
|
|
1633
|
+
sessionIdentity: identity,
|
|
1634
|
+
localSpace: mySpace,
|
|
1635
|
+
metaspace
|
|
1636
|
+
});
|
|
1637
|
+
// Build control payloads for commit
|
|
1638
|
+
const commitCtrlPayloads2 = [commitFrame, commitStone];
|
|
1639
|
+
if (identity) {
|
|
1640
|
+
commitCtrlPayloads2.push(identity);
|
|
1641
|
+
}
|
|
1642
|
+
return { frame: commitFrame, };
|
|
1643
|
+
}
|
|
1644
|
+
// Build control payloads for delta propose
|
|
1645
|
+
const deltaCtrlPayloads = [deltaFrame, deltaStone];
|
|
1646
|
+
if (identity) {
|
|
1647
|
+
deltaCtrlPayloads.push(identity);
|
|
1648
|
+
}
|
|
1649
|
+
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1680
1650
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
catch (error) {
|
|
1654
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1655
|
+
throw error;
|
|
1656
|
+
}
|
|
1657
|
+
finally {
|
|
1658
|
+
if (logalot) {
|
|
1659
|
+
console.log(`${lc} complete.`);
|
|
1684
1660
|
}
|
|
1685
1661
|
}
|
|
1686
1662
|
}
|
|
1687
|
-
async handleCommitFrame({ sagaIbGib,
|
|
1663
|
+
async handleCommitFrame({ sagaIbGib, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1688
1664
|
const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
|
|
1689
|
-
|
|
1690
|
-
|
|
1665
|
+
try {
|
|
1666
|
+
if (logalot) {
|
|
1667
|
+
console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`);
|
|
1668
|
+
}
|
|
1669
|
+
// Sender Logic (Finalizing):
|
|
1670
|
+
// If we are here, we received a Commit frame from the Peer.
|
|
1671
|
+
// This implies the Peer has successfully committed.
|
|
1672
|
+
// We should now:
|
|
1673
|
+
// 1. Validate (implicitly done by receiving valid frame)
|
|
1674
|
+
// 2. Perform our own cleanup (Temp -> Dest, if applicable)
|
|
1675
|
+
// 3. Return null to signal saga completion.
|
|
1676
|
+
// Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
|
|
1677
|
+
if (logalot) {
|
|
1678
|
+
console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
|
|
1679
|
+
}
|
|
1680
|
+
return { responseWasNull: true };
|
|
1691
1681
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
if (logalot) {
|
|
1701
|
-
console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`);
|
|
1682
|
+
catch (error) {
|
|
1683
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1684
|
+
throw error;
|
|
1685
|
+
}
|
|
1686
|
+
finally {
|
|
1687
|
+
if (logalot) {
|
|
1688
|
+
console.log(`${lc} complete.`);
|
|
1689
|
+
}
|
|
1702
1690
|
}
|
|
1703
|
-
// return { responseWasNull: true };
|
|
1704
|
-
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1705
1691
|
}
|
|
1706
1692
|
// #endregion Handlers
|
|
1707
1693
|
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|