@ibgib/core-gib 0.1.25 → 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/common/other/ibgib-helper.d.mts +1 -1
- package/dist/common/other/ibgib-helper.d.mts.map +1 -1
- package/dist/common/other/ibgib-helper.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +1 -0
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +1 -0
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +21 -1
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +137 -4
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +0 -2
- 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 +169 -62
- 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 +13 -29
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +18 -57
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +15 -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 +77 -7
- 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 +76 -17
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +545 -480
- 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 +31 -4
- 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/common/other/ibgib-helper.mts +1 -1
- package/src/sync/sync-constants.mts +1 -0
- package/src/sync/sync-helpers.mts +132 -4
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +169 -47
- package/src/sync/sync-peer/sync-peer-v1.mts +33 -74
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +69 -9
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +13 -5
- package/src/sync/sync-saga-coordinator.mts +628 -530
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +18 -18
- package/src/sync/sync-types.mts +40 -3
|
@@ -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
|
/**
|
|
@@ -142,6 +142,75 @@ export class SyncSagaCoordinator {
|
|
|
142
142
|
done
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* This is what the receiving side of the sync calls to drive the FSM to the
|
|
147
|
+
* next stage.
|
|
148
|
+
*
|
|
149
|
+
* So whereas the sender executes a saga loop and drives the entire process,
|
|
150
|
+
* this is a reactive one-off that drives just the single step that the
|
|
151
|
+
* receiver does in that saga.
|
|
152
|
+
*
|
|
153
|
+
* @returns next context result if another round, else if commit returns
|
|
154
|
+
* null
|
|
155
|
+
*/
|
|
156
|
+
async continueSync({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
157
|
+
const lc = `${this.lc}[${this.continueSync.name}]`;
|
|
158
|
+
try {
|
|
159
|
+
if (logalot) {
|
|
160
|
+
console.log(`${lc} starting... (I: f64e08bf77d1425378601f380384ec26)`);
|
|
161
|
+
}
|
|
162
|
+
const contextResult = await this.handleResponseSagaContext({
|
|
163
|
+
sagaContext,
|
|
164
|
+
mySpace,
|
|
165
|
+
myTempSpace,
|
|
166
|
+
identity,
|
|
167
|
+
identitySecret,
|
|
168
|
+
metaspace,
|
|
169
|
+
});
|
|
170
|
+
if (!contextResult) {
|
|
171
|
+
if (logalot) {
|
|
172
|
+
console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
// #region error conditions throw
|
|
177
|
+
if (contextResult.errorMsg) {
|
|
178
|
+
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
|
|
179
|
+
}
|
|
180
|
+
else if (!contextResult.nextFrameInfo) {
|
|
181
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
|
|
182
|
+
}
|
|
183
|
+
else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
184
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
|
|
185
|
+
}
|
|
186
|
+
// #endregion error conditions throw
|
|
187
|
+
// create the return context
|
|
188
|
+
const { frame, payloadIbGibsDomain } = contextResult.nextFrameInfo;
|
|
189
|
+
const responseCtx = await createSyncSagaContext({
|
|
190
|
+
sagaFrame: frame,
|
|
191
|
+
localSpace: mySpace,
|
|
192
|
+
payloadIbGibsDomain,
|
|
193
|
+
// todo: we need to thoroughly go through the identity per each step after getting basic merging
|
|
194
|
+
sessionKeystones: identity ? [identity] : undefined, // ??
|
|
195
|
+
});
|
|
196
|
+
const immediateValidationErrors = await validateContextAndSagaFrame({
|
|
197
|
+
context: responseCtx,
|
|
198
|
+
});
|
|
199
|
+
if (immediateValidationErrors.length > 0) {
|
|
200
|
+
throw new Error(`(UNEXPECTED) just created sync saga context () and there were immediateValidationErrors? immediateValidationErrors: ${immediateValidationErrors} (E: c120e8e0aa98673d685267a8a36e5826)`);
|
|
201
|
+
}
|
|
202
|
+
return responseCtx;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
if (logalot) {
|
|
210
|
+
console.log(`${lc} complete.`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
145
214
|
async getSessionIdentity({ sagaId, metaspace, tempSpace, }) {
|
|
146
215
|
const lc = `${this.lc}[${this.getSessionIdentity.name}]`;
|
|
147
216
|
try {
|
|
@@ -292,8 +361,12 @@ export class SyncSagaCoordinator {
|
|
|
292
361
|
throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
|
|
293
362
|
}
|
|
294
363
|
updates$.next(responseCtx); // spins off for saga UI updating
|
|
295
|
-
// validate context
|
|
296
|
-
|
|
364
|
+
// immediately validate context/saga frame (but not payloads because
|
|
365
|
+
// we may not have those yet)
|
|
366
|
+
const contextAndSagaFrameValidationErrors = await validateContextAndSagaFrame({ context: responseCtx });
|
|
367
|
+
if (contextAndSagaFrameValidationErrors.length > 0) {
|
|
368
|
+
throw new Error(`contextAndSagaFrameValidationErrors: ${contextAndSagaFrameValidationErrors} (E: 6eebe8e7fa437c00a8cde3ada3c66826)`);
|
|
369
|
+
}
|
|
297
370
|
// Extract expected domain addresses from response context
|
|
298
371
|
const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
|
|
299
372
|
// Poll for them if needed. see above jsdocs for domainPayloadsMap
|
|
@@ -326,6 +399,7 @@ export class SyncSagaCoordinator {
|
|
|
326
399
|
// finished/errored out.
|
|
327
400
|
const contextResult = await this.handleResponseSagaContext({
|
|
328
401
|
sagaContext: responseCtx,
|
|
402
|
+
initDomainGraph,
|
|
329
403
|
mySpace: localSpace,
|
|
330
404
|
myTempSpace: tempSpace,
|
|
331
405
|
metaspace,
|
|
@@ -511,7 +585,9 @@ export class SyncSagaCoordinator {
|
|
|
511
585
|
metaspace,
|
|
512
586
|
localSpace,
|
|
513
587
|
});
|
|
514
|
-
|
|
588
|
+
if (logalot) {
|
|
589
|
+
console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`);
|
|
590
|
+
}
|
|
515
591
|
return { initFrame: sagaFrame, initDomainGraph: fullGraph };
|
|
516
592
|
}
|
|
517
593
|
catch (error) {
|
|
@@ -584,6 +660,7 @@ export class SyncSagaCoordinator {
|
|
|
584
660
|
}
|
|
585
661
|
/**
|
|
586
662
|
* This is the heart of the "ping pong" transaction, where we send a context
|
|
663
|
+
*
|
|
587
664
|
* and receive a context. IOW, this drives the FSM of the sync saga ibgib as
|
|
588
665
|
* a whole.
|
|
589
666
|
*
|
|
@@ -602,7 +679,7 @@ export class SyncSagaCoordinator {
|
|
|
602
679
|
*
|
|
603
680
|
* This is a one-off on the receiver.
|
|
604
681
|
*/
|
|
605
|
-
async handleResponseSagaContext({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
682
|
+
async handleResponseSagaContext({ sagaContext, initDomainGraph, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
|
|
606
683
|
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
607
684
|
try {
|
|
608
685
|
if (logalot) {
|
|
@@ -616,7 +693,7 @@ export class SyncSagaCoordinator {
|
|
|
616
693
|
console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
|
|
617
694
|
}
|
|
618
695
|
// Get Stage from Stone (or Frame for Init fallback)
|
|
619
|
-
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space:
|
|
696
|
+
const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
620
697
|
if (logalot) {
|
|
621
698
|
console.log(`${lc} handling frame stage: ${stage}`);
|
|
622
699
|
}
|
|
@@ -630,25 +707,42 @@ export class SyncSagaCoordinator {
|
|
|
630
707
|
nextFrameInfo = await this.handleInitFrame({
|
|
631
708
|
sagaIbGib,
|
|
632
709
|
messageData: messageData,
|
|
633
|
-
metaspace,
|
|
634
|
-
|
|
635
|
-
myTempSpace: myTempSpace,
|
|
636
|
-
identity,
|
|
637
|
-
identitySecret
|
|
710
|
+
metaspace, mySpace, myTempSpace,
|
|
711
|
+
identity, identitySecret
|
|
638
712
|
});
|
|
639
713
|
break;
|
|
640
714
|
case SyncStage.ack:
|
|
641
|
-
|
|
715
|
+
if (!initDomainGraph) {
|
|
716
|
+
throw new Error(`(UNEXPECTED) initDomainGraph falsy on the sender? (E: a3d758ad954829aba88663188eafc826)`);
|
|
717
|
+
}
|
|
718
|
+
nextFrameInfo = await this.handleAckFrame({
|
|
719
|
+
sagaContext,
|
|
720
|
+
sagaIbGib,
|
|
721
|
+
initDomainGraph,
|
|
722
|
+
metaspace, mySpace, myTempSpace,
|
|
723
|
+
identity,
|
|
724
|
+
});
|
|
642
725
|
break;
|
|
643
726
|
case SyncStage.delta:
|
|
644
|
-
nextFrameInfo = await this.handleDeltaFrame({
|
|
727
|
+
nextFrameInfo = await this.handleDeltaFrame({
|
|
728
|
+
sagaContext,
|
|
729
|
+
sagaIbGib,
|
|
730
|
+
srcGraph,
|
|
731
|
+
metaspace,
|
|
732
|
+
mySpace,
|
|
733
|
+
myTempSpace,
|
|
734
|
+
identity,
|
|
735
|
+
});
|
|
645
736
|
break;
|
|
646
737
|
case SyncStage.commit:
|
|
647
|
-
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace,
|
|
738
|
+
nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, mySpace: mySpace, myTempSpace: myTempSpace, identity, });
|
|
648
739
|
break;
|
|
649
740
|
default:
|
|
650
741
|
throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
|
|
651
742
|
}
|
|
743
|
+
if (logalot) {
|
|
744
|
+
console.log(`${lc} nextFrameInfo: ${nextFrameInfo ? pretty(nextFrameInfo) : 'undefined'} (I: a8ad281ca8e89385686d18327a105726)`);
|
|
745
|
+
}
|
|
652
746
|
return { errorMsg: undefined, nextFrameInfo, };
|
|
653
747
|
}
|
|
654
748
|
catch (error) {
|
|
@@ -672,7 +766,7 @@ export class SyncSagaCoordinator {
|
|
|
672
766
|
* The Receiver performs Gap Analysis here:
|
|
673
767
|
* 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
|
|
674
768
|
* 2. Identifies what Sender needs (`pushOfferAddrs`).
|
|
675
|
-
* 3. Identifies what Receiver needs (`
|
|
769
|
+
* 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
|
|
676
770
|
* 4. Returns an `Ack` frame containing these lists.
|
|
677
771
|
*/
|
|
678
772
|
async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
|
|
@@ -909,7 +1003,7 @@ export class SyncSagaCoordinator {
|
|
|
909
1003
|
}
|
|
910
1004
|
const ackStone = await this.createSyncMsgStone({
|
|
911
1005
|
data: ackData,
|
|
912
|
-
localSpace:
|
|
1006
|
+
localSpace: mySpace,
|
|
913
1007
|
metaspace,
|
|
914
1008
|
});
|
|
915
1009
|
if (logalot) {
|
|
@@ -928,7 +1022,6 @@ export class SyncSagaCoordinator {
|
|
|
928
1022
|
* we want to push ibgibs to the remote/sender if we have push
|
|
929
1023
|
* offers. an ack frame's payloads, if any, are those push offers
|
|
930
1024
|
*/
|
|
931
|
-
// let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
|
|
932
1025
|
let payloadIbGibsDomain;
|
|
933
1026
|
if (pushOfferInfos.length > 0) {
|
|
934
1027
|
const searchSecondSpaceAddrs = [];
|
|
@@ -985,12 +1078,10 @@ export class SyncSagaCoordinator {
|
|
|
985
1078
|
}
|
|
986
1079
|
// we have now populated payloadIbGibsDomain
|
|
987
1080
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
// payloadIbGibsDomain,
|
|
993
|
-
// };
|
|
1081
|
+
return {
|
|
1082
|
+
frame: ackFrame,
|
|
1083
|
+
payloadIbGibsDomain,
|
|
1084
|
+
};
|
|
994
1085
|
}
|
|
995
1086
|
catch (error) {
|
|
996
1087
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1009,19 +1100,23 @@ export class SyncSagaCoordinator {
|
|
|
1009
1100
|
* **Execution Context**: **Sender (Local)**.
|
|
1010
1101
|
*
|
|
1011
1102
|
* The Sender reacts to the Receiver's requirements:
|
|
1012
|
-
* 1.
|
|
1013
|
-
*
|
|
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..
|
|
1014
1108
|
*
|
|
1015
1109
|
* Returns a `Delta` frame.
|
|
1016
1110
|
*/
|
|
1017
|
-
async handleAckFrame({ sagaIbGib,
|
|
1111
|
+
async handleAckFrame({ sagaContext, sagaIbGib, initDomainGraph, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1018
1112
|
const lc = `${this.lc}[${this.handleAckFrame.name}]`;
|
|
1019
1113
|
try {
|
|
1020
1114
|
if (logalot) {
|
|
1021
1115
|
console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
|
|
1022
1116
|
}
|
|
1023
|
-
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space:
|
|
1117
|
+
const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: mySpace });
|
|
1024
1118
|
const ackData = messageData;
|
|
1119
|
+
// #region sanity/validation
|
|
1025
1120
|
if (!ackData) {
|
|
1026
1121
|
throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
|
|
1027
1122
|
}
|
|
@@ -1031,6 +1126,10 @@ export class SyncSagaCoordinator {
|
|
|
1031
1126
|
if (logalot) {
|
|
1032
1127
|
console.log(`${lc} ackData: ${pretty(ackData)} (I: 7f8e9d0a1b2c3d4e5f6g7h8i9j0k)`);
|
|
1033
1128
|
}
|
|
1129
|
+
if (!sagaIbGib.data) {
|
|
1130
|
+
throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 385e389610282aa9c5dbe4083adbde26)`);
|
|
1131
|
+
}
|
|
1132
|
+
// #region sanity/validation
|
|
1034
1133
|
// 1. Check for Conflicts
|
|
1035
1134
|
const conflicts = ackData.conflicts || [];
|
|
1036
1135
|
console.log(`${lc} [CONFLICT DEBUG] Received conflicts from Ack: ${conflicts.length}`);
|
|
@@ -1044,10 +1143,15 @@ export class SyncSagaCoordinator {
|
|
|
1044
1143
|
// For now, throw to trigger abort.
|
|
1045
1144
|
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1046
1145
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1146
|
+
// at this point, if we have conflicts, they are non-terminal
|
|
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
|
|
1153
|
+
if (conflicts.length > 0) {
|
|
1154
|
+
console.log(`${lc} [CONFLICT DEBUG] Processing ${conflicts.length} non-terminal conflicts`);
|
|
1051
1155
|
// We need to resolve these.
|
|
1052
1156
|
// Strategy:
|
|
1053
1157
|
// 1. Analyze Divergence (Sender vs Receiver)
|
|
@@ -1055,8 +1159,8 @@ export class SyncSagaCoordinator {
|
|
|
1055
1159
|
// 3. Request that data (as Delta Reqs)
|
|
1056
1160
|
// 4. (Later in Delta Phase) Perform Merge.
|
|
1057
1161
|
// BUT: The Delta Phase is usually generic "Send me these Addrs".
|
|
1058
|
-
// If we just add to `
|
|
1059
|
-
// 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.
|
|
1060
1164
|
// We (Sender) are processing the Ack.
|
|
1061
1165
|
// We need to request data FROM Receiver.
|
|
1062
1166
|
// But the protocol 'Ack' step typically leads to 'Delta' (Sender sending data).
|
|
@@ -1073,198 +1177,140 @@ export class SyncSagaCoordinator {
|
|
|
1073
1177
|
// `peer.pull(addr)`?
|
|
1074
1178
|
// Yes! The Coordinator has the `peer`.
|
|
1075
1179
|
// Let's analyze and pull immediately.
|
|
1076
|
-
for (const conflict of
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
//
|
|
1080
|
-
//
|
|
1081
|
-
//
|
|
1082
|
-
//
|
|
1083
|
-
//
|
|
1084
|
-
//
|
|
1085
|
-
|
|
1086
|
-
//
|
|
1087
|
-
//
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1093
|
-
// Basic Diff: Find what Receiver has that we don't.
|
|
1094
|
-
// Actually, we need to traverse OUR past to find commonality.
|
|
1095
|
-
const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
|
|
1096
|
-
const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
|
|
1097
|
-
if (receiverOnlyAddrs.length > 0) {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1158
|
-
else {
|
|
1159
|
-
console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1160
|
-
}
|
|
1180
|
+
for (const conflict of conflicts) {
|
|
1181
|
+
// todo: integrate conflict strategies into this point...this whole block needs to be redone, but I want to check the fast-forward/backward simpler case tests first
|
|
1182
|
+
throw new Error(`conflicts not (re)implemented yet (E: 3b7d0819f83842a6de3ae988819bc826)`);
|
|
1183
|
+
// const { timelineAddrs, localAddr: receiverTip, remoteAddr: senderTip } = conflict;
|
|
1184
|
+
// // Sender History
|
|
1185
|
+
// // We need our own history for this timeline.
|
|
1186
|
+
// // We know the 'senderTip' (remoteAddr in Ack).
|
|
1187
|
+
// // Sender should verify it has this tip.
|
|
1188
|
+
// // Compute Diffs
|
|
1189
|
+
// // We need to find `receiverOnly` addrs.
|
|
1190
|
+
// // Receiver sent us `timelineAddrs` (Full History).
|
|
1191
|
+
// const receiverHistorySet = new Set(timelineAddrs);
|
|
1192
|
+
// // We need our execution context's history for this senderTip.
|
|
1193
|
+
// // We can fetch valid 'past' from space.
|
|
1194
|
+
// const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
|
|
1195
|
+
// const senderTipIbGib = resSenderTip.ibGibs?.[0];
|
|
1196
|
+
// if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
|
|
1197
|
+
// // Basic Diff: Find what Receiver has that we don't.
|
|
1198
|
+
// // Actually, we need to traverse OUR past to find commonality.
|
|
1199
|
+
// const senderHistory = [senderTip, ...(senderTipIbGib.rel8ns?.past || [])];
|
|
1200
|
+
// const receiverOnlyAddrs = timelineAddrs.filter(addr => !senderHistory.includes(addr));
|
|
1201
|
+
// if (receiverOnlyAddrs.length > 0) {
|
|
1202
|
+
// console.log(`${lc} [CONFLICT DEBUG] Found ${receiverOnlyAddrs.length} receiver-only frames - need to pull for merge`);
|
|
1203
|
+
// console.log(`${lc} [CONFLICT DEBUG] Receiver-only addrs:`, receiverOnlyAddrs);
|
|
1204
|
+
// // PULL these frames from Peer into Local Space
|
|
1205
|
+
// // (Validation: We trust peer for now / verification happens on put)
|
|
1206
|
+
// for (const addr of receiverOnlyAddrs) {
|
|
1207
|
+
// console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
|
|
1208
|
+
// }
|
|
1209
|
+
// // Compute DELTA dependencies for each receiver-only frame
|
|
1210
|
+
// // Find LCA to determine what dependencies we already have
|
|
1211
|
+
// const lcaAddr = timelineAddrs.find(addr => senderHistory.includes(addr));
|
|
1212
|
+
// console.log(`${lc} [CONFLICT DEBUG] LCA: ${lcaAddr || 'NONE'}`);
|
|
1213
|
+
// const skipAddrsSet = new Set<string>();
|
|
1214
|
+
// if (lcaAddr) {
|
|
1215
|
+
// try {
|
|
1216
|
+
// const lcaRes = await getFromSpace({ addr: lcaAddr, space: destSpace });
|
|
1217
|
+
// const lcaIbGib = lcaRes.ibGibs?.[0];
|
|
1218
|
+
// if (lcaIbGib) {
|
|
1219
|
+
// const lcaDeps = await getDependencyGraph({ ibGib: lcaIbGib, space: destSpace });
|
|
1220
|
+
// if (lcaDeps) Object.keys(lcaDeps).forEach(a => skipAddrsSet.add(a));
|
|
1221
|
+
// console.log(`${lc} [CONFLICT DEBUG] LCA deps to skip: ${skipAddrsSet.size}`);
|
|
1222
|
+
// }
|
|
1223
|
+
// } catch (e) {
|
|
1224
|
+
// console.warn(`${lc} Error getting LCA deps: ${extractErrorMsg(e)}`);
|
|
1225
|
+
// }
|
|
1226
|
+
// }
|
|
1227
|
+
// // For each receiver-only frame, get its DELTA dependency graph (minus LCA deps)
|
|
1228
|
+
// for (const addr of receiverOnlyAddrs) {
|
|
1229
|
+
// // Add the frame itself first
|
|
1230
|
+
// if (!outgoingDeltaAddrRequestInfos.includes(addr)) {
|
|
1231
|
+
// outgoingDeltaAddrRequestInfos.push(addr);
|
|
1232
|
+
// }
|
|
1233
|
+
// // Get the frame's delta dependencies (skip LCA's deps)
|
|
1234
|
+
// try {
|
|
1235
|
+
// const frameRes = await getFromSpace({ addr, space: destSpace });
|
|
1236
|
+
// const frameIbGib = frameRes.ibGibs?.[0];
|
|
1237
|
+
// if (frameIbGib) {
|
|
1238
|
+
// // Get dependency graph, skipping all LCA dependencies
|
|
1239
|
+
// const frameDeltaDeps = await getDependencyGraph({
|
|
1240
|
+
// ibGib: frameIbGib,
|
|
1241
|
+
// space: destSpace,
|
|
1242
|
+
// skipAddrs: Array.from(skipAddrsSet), // Skip entire LCA dep graph
|
|
1243
|
+
// });
|
|
1244
|
+
// if (frameDeltaDeps) {
|
|
1245
|
+
// // Add all delta dependencies (Object.keys gives us the addresses)
|
|
1246
|
+
// Object.keys(frameDeltaDeps).forEach(depAddr => {
|
|
1247
|
+
// if (!outgoingDeltaAddrRequestInfos.includes(depAddr) && !skipAddrsSet.has(depAddr)) {
|
|
1248
|
+
// outgoingDeltaAddrRequestInfos.push(depAddr);
|
|
1249
|
+
// }
|
|
1250
|
+
// });
|
|
1251
|
+
// }
|
|
1252
|
+
// }
|
|
1253
|
+
// } catch (depError) {
|
|
1254
|
+
// console.warn(`${lc} [CONFLICT DEBUG] Error getting delta deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1255
|
+
// }
|
|
1256
|
+
// }
|
|
1257
|
+
// console.log(`${lc} [CONFLICT DEBUG] Total merge requests (frames + delta deps): ${outgoingDeltaAddrRequestInfos.length}`);
|
|
1258
|
+
// } else {
|
|
1259
|
+
// console.log(`${lc} [CONFLICT DEBUG] No receiver-only frames found for this conflict`);
|
|
1260
|
+
// }
|
|
1161
1261
|
}
|
|
1162
|
-
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${
|
|
1262
|
+
console.log(`${lc} [CONFLICT DEBUG] Finished processing ${conflicts.length} conflicts. outgoingDeltaAddrRequestInfos: ${outgoingDeltaAddrRequestInfos.length}`);
|
|
1163
1263
|
}
|
|
1164
1264
|
else {
|
|
1165
1265
|
console.log(`${lc} [CONFLICT DEBUG] No optimistic conflicts to process`);
|
|
1166
1266
|
}
|
|
1167
1267
|
// 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1178
|
-
// 2. Process Delta Requests (Push Payload)
|
|
1179
|
-
// [NEW] Smart Diff: Use knowledgeVector to skip dependencies
|
|
1180
|
-
// const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
|
|
1181
|
-
const skipAddrs = new Set();
|
|
1182
|
-
if (ackData.knowledgeVector) {
|
|
1183
|
-
Object.values(ackData.knowledgeVector).forEach(addrs => {
|
|
1184
|
-
// addrs.forEach(a => skipAddrs.add(a));
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
const payloadIbGibs = [];
|
|
1188
|
-
// Gather all tips to sync first
|
|
1189
|
-
const tipsToSync = [];
|
|
1190
|
-
for (const addr of deltaReqAddrs) {
|
|
1191
|
-
// let ibGib = srcGraph[addr];
|
|
1192
|
-
// if (!ibGib) {
|
|
1193
|
-
// const res = await getFromSpace({ addr, space: destSpace });
|
|
1194
|
-
// if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1195
|
-
// ibGib = res.ibGibs[0];
|
|
1196
|
-
// }
|
|
1197
|
-
// }
|
|
1198
|
-
// if (ibGib) {
|
|
1199
|
-
// tipsToSync.push(ibGib);
|
|
1200
|
-
// } else {
|
|
1201
|
-
// throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
|
|
1202
|
-
// }
|
|
1203
|
-
}
|
|
1204
|
-
// Calculate Dependency Graph for ALL tips, effectively utilizing common history
|
|
1205
|
-
// Pass skipAddrs to `getDependencyGraph` or gather manually.
|
|
1206
|
-
// `getDependencyGraph` takes a single ibGib.
|
|
1207
|
-
// We can optimize by doing it for each tip and unioning the result?
|
|
1208
|
-
// Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
|
|
1209
|
-
// We will loop.
|
|
1210
|
-
const allDepsSet = new Set();
|
|
1211
|
-
for (const tip of tipsToSync) {
|
|
1212
|
-
// Always include the tip itself
|
|
1213
|
-
const tipAddr = getIbGibAddr({ ibGib: tip });
|
|
1214
|
-
// Only process if not skipped (though deltaReq implies they barely just asked for it)
|
|
1215
|
-
// But detailed deps might be skipped.
|
|
1216
|
-
// Get Graph with Skips
|
|
1217
|
-
// Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
|
|
1218
|
-
const deps = await getDependencyGraph({
|
|
1219
|
-
ibGib: tip,
|
|
1220
|
-
space: destSpace,
|
|
1221
|
-
skipAddrs: Array.from(skipAddrs)
|
|
1222
|
-
});
|
|
1223
|
-
// [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
|
|
1224
|
-
let tipIncluded = false;
|
|
1225
|
-
if (deps) {
|
|
1226
|
-
Object.values(deps).forEach(d => {
|
|
1227
|
-
const dAddr = getIbGibAddr({ ibGib: d });
|
|
1228
|
-
if (!allDepsSet.has(dAddr)) {
|
|
1229
|
-
allDepsSet.add(dAddr);
|
|
1230
|
-
payloadIbGibs.push(d);
|
|
1231
|
-
}
|
|
1232
|
-
if (dAddr === tipAddr) {
|
|
1233
|
-
tipIncluded = true;
|
|
1234
|
-
}
|
|
1235
|
-
});
|
|
1236
|
-
}
|
|
1237
|
-
if (!tipIncluded && !skipAddrs.has(tipAddr)) {
|
|
1238
|
-
if (logalot) {
|
|
1239
|
-
console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`);
|
|
1240
|
-
}
|
|
1241
|
-
if (!allDepsSet.has(tipAddr)) {
|
|
1242
|
-
allDepsSet.add(tipAddr);
|
|
1243
|
-
payloadIbGibs.push(tip);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
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
|
+
});
|
|
1247
1278
|
// 3. Create Delta Frame
|
|
1248
|
-
const sagaId = ackData.sagaId;
|
|
1249
1279
|
const deltaData = {
|
|
1250
1280
|
sagaId: sagaIbGib.data.uuid,
|
|
1251
1281
|
stage: SyncStage.delta,
|
|
1252
|
-
|
|
1253
|
-
|
|
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,
|
|
1254
1300
|
};
|
|
1255
1301
|
if (logalot) {
|
|
1256
1302
|
console.log(`${lc} Creating Delta Stone. Data stage: ${deltaData.stage}`);
|
|
1257
1303
|
}
|
|
1258
1304
|
const deltaStone = await this.createSyncMsgStone({
|
|
1259
1305
|
data: deltaData,
|
|
1260
|
-
localSpace:
|
|
1306
|
+
localSpace: mySpace,
|
|
1261
1307
|
metaspace,
|
|
1262
1308
|
});
|
|
1263
1309
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1264
1310
|
prevSagaIbGib: sagaIbGib,
|
|
1265
1311
|
msgStones: [deltaStone],
|
|
1266
1312
|
sessionIdentity: identity,
|
|
1267
|
-
localSpace:
|
|
1313
|
+
localSpace: mySpace,
|
|
1268
1314
|
metaspace,
|
|
1269
1315
|
});
|
|
1270
1316
|
if (logalot) {
|
|
@@ -1275,9 +1321,70 @@ export class SyncSagaCoordinator {
|
|
|
1275
1321
|
if (identity) {
|
|
1276
1322
|
payloadIbGibsControl.push(identity);
|
|
1277
1323
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
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;
|
|
1281
1388
|
}
|
|
1282
1389
|
catch (error) {
|
|
1283
1390
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -1299,275 +1406,165 @@ export class SyncSagaCoordinator {
|
|
|
1299
1406
|
* 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
|
|
1300
1407
|
* 3. **Completion**: If no more requests, transitions to `Commit`.
|
|
1301
1408
|
*/
|
|
1302
|
-
async handleDeltaFrame({ sagaIbGib, srcGraph,
|
|
1409
|
+
async handleDeltaFrame({ sagaContext, sagaIbGib, srcGraph, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1303
1410
|
const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
|
|
1308
|
-
const deltaData = messageData;
|
|
1309
|
-
if (!deltaData) {
|
|
1310
|
-
throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
|
|
1311
|
-
}
|
|
1312
|
-
if (deltaData.stage !== SyncStage.delta) {
|
|
1313
|
-
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1314
|
-
}
|
|
1315
|
-
if (logalot) {
|
|
1316
|
-
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1317
|
-
}
|
|
1318
|
-
console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
|
|
1319
|
-
const payloadAddrs = deltaData.payloadAddrs || [];
|
|
1320
|
-
const peerRequests = deltaData.requests || [];
|
|
1321
|
-
const peerProposesCommit = deltaData.proposeCommit || false;
|
|
1322
|
-
// 1. Process Received Payload (Ingest)
|
|
1323
|
-
const receivedPayloadIbGibs = [];
|
|
1324
|
-
if (payloadAddrs.length > 0) {
|
|
1325
|
-
// We use `payloadAddrs` as the manifest.
|
|
1326
|
-
// The ACTUAL collection of ibGibs should be available via `getFromSpace`
|
|
1327
|
-
// assuming the "Transport" layer put them there implicitly?
|
|
1328
|
-
// OR, if we are local-only, we just get them.
|
|
1329
|
-
// The `handleDeltaFrame` contract assumes data is reachable in `space`.
|
|
1330
|
-
const res = await getFromSpace({
|
|
1331
|
-
addrs: payloadAddrs,
|
|
1332
|
-
space: tempSpace, // Incoming data is in tempSpace
|
|
1333
|
-
});
|
|
1334
|
-
if (res.ibGibs) {
|
|
1335
|
-
receivedPayloadIbGibs.push(...res.ibGibs);
|
|
1336
|
-
// Also put them? `getFromSpace` retrieves. If they are in space, they are persisted.
|
|
1337
|
-
// If this is a Temp Space, they are safe.
|
|
1338
|
-
}
|
|
1339
|
-
else {
|
|
1340
|
-
console.warn(`${lc} Failed to retrieve payloads listed in delta: ${payloadAddrs.join(', ')}`);
|
|
1411
|
+
try {
|
|
1412
|
+
if (logalot) {
|
|
1413
|
+
console.log(`${lc} starting... (I: a1d0a85eb4189466f86dfd61e3df2626)`);
|
|
1341
1414
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
for (const addr of peerRequests) {
|
|
1348
|
-
// Get the requested ibGib
|
|
1349
|
-
let ibGib = srcGraph[addr];
|
|
1350
|
-
if (!ibGib) {
|
|
1351
|
-
const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
|
|
1352
|
-
if (res.ibGibs && res.ibGibs.length > 0) {
|
|
1353
|
-
ibGib = res.ibGibs[0];
|
|
1354
|
-
}
|
|
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)`);
|
|
1355
1420
|
}
|
|
1356
|
-
if (
|
|
1357
|
-
|
|
1358
|
-
const ibGibAddr = getIbGibAddr({ ibGib });
|
|
1359
|
-
if (!outgoingAddrsSet.has(ibGibAddr)) {
|
|
1360
|
-
outgoingPayload.push(ibGib);
|
|
1361
|
-
outgoingAddrsSet.add(ibGibAddr);
|
|
1362
|
-
}
|
|
1363
|
-
// Expand to include full dependency graph for this ibGib
|
|
1364
|
-
// (Receiver needs all deps to properly process/merge)
|
|
1365
|
-
try {
|
|
1366
|
-
const deps = await getDependencyGraph({
|
|
1367
|
-
ibGib,
|
|
1368
|
-
space: destSpace,
|
|
1369
|
-
});
|
|
1370
|
-
if (deps) {
|
|
1371
|
-
Object.values(deps).forEach(depIbGib => {
|
|
1372
|
-
const depAddr = getIbGibAddr({ ibGib: depIbGib });
|
|
1373
|
-
if (!outgoingAddrsSet.has(depAddr)) {
|
|
1374
|
-
outgoingPayload.push(depIbGib);
|
|
1375
|
-
outgoingAddrsSet.add(depAddr);
|
|
1376
|
-
}
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
catch (depError) {
|
|
1381
|
-
console.warn(`${lc} [CONFLICT DEBUG] Error expanding deps for ${addr}: ${extractErrorMsg(depError)}`);
|
|
1382
|
-
}
|
|
1421
|
+
if (deltaData.stage !== SyncStage.delta) {
|
|
1422
|
+
throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
|
|
1383
1423
|
}
|
|
1384
|
-
|
|
1385
|
-
console.
|
|
1424
|
+
if (logalot) {
|
|
1425
|
+
console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`);
|
|
1386
1426
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
+
}
|
|
1418
1471
|
}
|
|
1419
1472
|
}
|
|
1420
1473
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
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
|
|
1452
1507
|
}
|
|
1453
|
-
mergeResultIbGibs.push(mergeResult);
|
|
1454
|
-
outgoingPayload.push(mergeResult); // Send result to peer
|
|
1455
1508
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1509
|
+
catch (e) {
|
|
1510
|
+
console.error(`${lc} Merge failed: ${e}`);
|
|
1511
|
+
// If merge fails, we might Abort or just continue?
|
|
1512
|
+
}
|
|
1460
1513
|
}
|
|
1461
1514
|
}
|
|
1462
1515
|
}
|
|
1463
1516
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
// We have business to attend to -> Send Delta
|
|
1475
|
-
const responseDeltaData = {
|
|
1476
|
-
sagaId: deltaData.sagaId,
|
|
1477
|
-
stage: SyncStage.delta,
|
|
1478
|
-
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1479
|
-
requests: hasMyRequests ? myRequests : undefined,
|
|
1480
|
-
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1481
|
-
// Wait. If we send data, we are NOT committing yet.
|
|
1482
|
-
// We are sending data. The OTHER side must ingest it.
|
|
1483
|
-
// So proposeCommit = true?
|
|
1484
|
-
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1485
|
-
// Yes.
|
|
1486
|
-
};
|
|
1487
|
-
// BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
|
|
1488
|
-
// We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
|
|
1489
|
-
// So yes, proposeCommit = true.
|
|
1490
|
-
responseDeltaData.proposeCommit = true;
|
|
1491
|
-
const deltaStone = await this.createSyncMsgStone({
|
|
1492
|
-
data: responseDeltaData,
|
|
1493
|
-
localSpace: tempSpace,
|
|
1494
|
-
metaspace
|
|
1495
|
-
});
|
|
1496
|
-
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1497
|
-
prevSagaIbGib: sagaIbGib,
|
|
1498
|
-
msgStones: [deltaStone],
|
|
1499
|
-
sessionIdentity: identity,
|
|
1500
|
-
localSpace: tempSpace,
|
|
1501
|
-
metaspace
|
|
1502
|
-
});
|
|
1503
|
-
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1504
|
-
const payloadIbGibsControl = [deltaFrame, deltaStone];
|
|
1505
|
-
if (identity) {
|
|
1506
|
-
payloadIbGibsControl.push(identity);
|
|
1507
|
-
}
|
|
1508
|
-
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1509
|
-
// return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1510
|
-
throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1511
|
-
}
|
|
1512
|
-
else {
|
|
1513
|
-
// We have nothing to send.
|
|
1514
|
-
if (peerProposesCommit) {
|
|
1515
|
-
// Peer is done. We are done. -> Commit.
|
|
1516
|
-
const commitData = {
|
|
1517
|
-
sagaId: deltaData.sagaId,
|
|
1518
|
-
stage: SyncStage.commit,
|
|
1519
|
-
success: true,
|
|
1520
|
-
};
|
|
1521
|
-
const commitStone = await this.createSyncMsgStone({
|
|
1522
|
-
data: commitData,
|
|
1523
|
-
localSpace: tempSpace,
|
|
1524
|
-
metaspace
|
|
1525
|
-
});
|
|
1526
|
-
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1527
|
-
prevSagaIbGib: sagaIbGib,
|
|
1528
|
-
msgStones: [commitStone],
|
|
1529
|
-
sessionIdentity: identity,
|
|
1530
|
-
localSpace: tempSpace,
|
|
1531
|
-
metaspace
|
|
1532
|
-
});
|
|
1533
|
-
// Build control payloads for commit
|
|
1534
|
-
const commitCtrlPayloads = [commitFrame, commitStone];
|
|
1535
|
-
if (identity) {
|
|
1536
|
-
commitCtrlPayloads.push(identity);
|
|
1537
|
-
}
|
|
1538
|
-
// return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
|
|
1539
|
-
// return { frame: commitFrame, };
|
|
1540
|
-
throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
|
|
1541
|
-
}
|
|
1542
|
-
else {
|
|
1543
|
-
// peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
|
|
1544
|
-
// But we are empty.
|
|
1545
|
-
// 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
|
|
1546
1527
|
const responseDeltaData = {
|
|
1547
1528
|
sagaId: deltaData.sagaId,
|
|
1548
1529
|
stage: SyncStage.delta,
|
|
1549
|
-
|
|
1550
|
-
|
|
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.
|
|
1551
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;
|
|
1552
1543
|
const deltaStone = await this.createSyncMsgStone({
|
|
1553
1544
|
data: responseDeltaData,
|
|
1554
|
-
localSpace:
|
|
1555
|
-
metaspace
|
|
1545
|
+
localSpace: mySpace,
|
|
1546
|
+
metaspace,
|
|
1556
1547
|
});
|
|
1557
1548
|
const deltaFrame = await this.evolveSyncSagaIbGib({
|
|
1558
1549
|
prevSagaIbGib: sagaIbGib,
|
|
1559
1550
|
msgStones: [deltaStone],
|
|
1560
1551
|
sessionIdentity: identity,
|
|
1561
|
-
localSpace:
|
|
1562
|
-
metaspace
|
|
1552
|
+
localSpace: mySpace,
|
|
1553
|
+
metaspace,
|
|
1563
1554
|
});
|
|
1564
|
-
//
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
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.
|
|
1571
1568
|
const commitData = {
|
|
1572
1569
|
sagaId: deltaData.sagaId,
|
|
1573
1570
|
stage: SyncStage.commit,
|
|
@@ -1575,54 +1572,122 @@ export class SyncSagaCoordinator {
|
|
|
1575
1572
|
};
|
|
1576
1573
|
const commitStone = await this.createSyncMsgStone({
|
|
1577
1574
|
data: commitData,
|
|
1578
|
-
localSpace:
|
|
1575
|
+
localSpace: mySpace,
|
|
1579
1576
|
metaspace
|
|
1580
1577
|
});
|
|
1581
1578
|
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
1582
|
-
prevSagaIbGib:
|
|
1579
|
+
prevSagaIbGib: sagaIbGib,
|
|
1583
1580
|
msgStones: [commitStone],
|
|
1584
1581
|
sessionIdentity: identity,
|
|
1585
|
-
localSpace:
|
|
1582
|
+
localSpace: mySpace,
|
|
1586
1583
|
metaspace
|
|
1587
1584
|
});
|
|
1588
1585
|
// Build control payloads for commit
|
|
1589
|
-
const
|
|
1586
|
+
const commitCtrlPayloads = [commitFrame, commitStone];
|
|
1590
1587
|
if (identity) {
|
|
1591
|
-
|
|
1588
|
+
commitCtrlPayloads.push(identity);
|
|
1592
1589
|
}
|
|
1593
|
-
|
|
1594
|
-
// return { frame: commitFrame, };
|
|
1595
|
-
throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
|
|
1590
|
+
return { frame: commitFrame, };
|
|
1596
1591
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
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 };
|
|
1601
1650
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
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.`);
|
|
1605
1660
|
}
|
|
1606
1661
|
}
|
|
1607
1662
|
}
|
|
1608
|
-
async handleCommitFrame({ sagaIbGib,
|
|
1663
|
+
async handleCommitFrame({ sagaIbGib, mySpace, myTempSpace, metaspace, identity, }) {
|
|
1609
1664
|
const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
|
|
1610
|
-
|
|
1611
|
-
|
|
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 };
|
|
1612
1681
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
if (logalot) {
|
|
1622
|
-
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
|
+
}
|
|
1623
1690
|
}
|
|
1624
|
-
// return { responseWasNull: true };
|
|
1625
|
-
throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
|
|
1626
1691
|
}
|
|
1627
1692
|
// #endregion Handlers
|
|
1628
1693
|
async createSyncMsgStone({ data, localSpace, metaspace, }) {
|