@ibgib/core-gib 0.1.27 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sync/sync-helpers.d.mts +7 -1
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +21 -1
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +23 -159
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +323 -130
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +12 -17
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +6 -5
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/sync/sync-helpers.mts +23 -4
- package/src/sync/sync-saga-coordinator.mts +367 -150
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +13 -18
- package/src/sync/sync-types.mts +7 -7
|
@@ -31,8 +31,9 @@ import {
|
|
|
31
31
|
SYNC_CONFLICT_STRATEGY_VALID_VALUES, HandleSagaResponseContextResult,
|
|
32
32
|
SyncExecutionContext,
|
|
33
33
|
SYNC_EXECUTION_CONTEXT_VALID_VALUES,
|
|
34
|
+
SyncSagaFrameDependencyGraph,
|
|
34
35
|
} from "./sync-types.mjs";
|
|
35
|
-
import { getExecutionContext, getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
|
|
36
|
+
import { getExecutionContext, getFullSyncSagaHistory, getSyncIb, getTempSpaceName, isPastFrame, putInSpace_dnasThenNonDnas, validateFullSyncSagaHistory } from "./sync-helpers.mjs";
|
|
36
37
|
import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
37
38
|
import {
|
|
38
39
|
SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
|
|
@@ -76,10 +77,10 @@ const lcControlDomain = '[ControlDomain]';
|
|
|
76
77
|
* to a specific Saga session, not fixed node identities.
|
|
77
78
|
*/
|
|
78
79
|
export class SyncSagaCoordinator {
|
|
79
|
-
|
|
80
|
+
private lc: string = `[${SyncSagaCoordinator.name}]`;
|
|
80
81
|
|
|
81
82
|
constructor(
|
|
82
|
-
|
|
83
|
+
private keystone: KeystoneService_V1
|
|
83
84
|
) {
|
|
84
85
|
|
|
85
86
|
}
|
|
@@ -240,7 +241,11 @@ export class SyncSagaCoordinator {
|
|
|
240
241
|
|
|
241
242
|
if (!contextResult) {
|
|
242
243
|
if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: 43da8bb6c846b1fe7766332643be0e26)`); }
|
|
243
|
-
|
|
244
|
+
// does this ever hit now?
|
|
245
|
+
return null; /* <<<< returns early */
|
|
246
|
+
} else if (contextResult.nextFrameInfo?.sagaComplete) {
|
|
247
|
+
// this is the current proper workflow I believe as of 01/22/2026
|
|
248
|
+
return null; /* <<<< returns early */
|
|
244
249
|
}
|
|
245
250
|
|
|
246
251
|
// #region error conditions throw
|
|
@@ -248,8 +253,6 @@ export class SyncSagaCoordinator {
|
|
|
248
253
|
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: 7b41a183cf3cb58a5859c803800cf826)`);
|
|
249
254
|
} else if (!contextResult.nextFrameInfo) {
|
|
250
255
|
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: 5740542f5eb8ccb41dfec188d87c1e26)`);
|
|
251
|
-
} else if (contextResult.nextFrameInfo?.responseWasNull) {
|
|
252
|
-
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: ae06748d8c0c5e70c92322c8fb0cb426)`);
|
|
253
256
|
}
|
|
254
257
|
// #endregion error conditions throw
|
|
255
258
|
|
|
@@ -278,7 +281,7 @@ export class SyncSagaCoordinator {
|
|
|
278
281
|
}
|
|
279
282
|
}
|
|
280
283
|
|
|
281
|
-
|
|
284
|
+
private async getSessionIdentity({
|
|
282
285
|
sagaId,
|
|
283
286
|
metaspace,
|
|
284
287
|
tempSpace,
|
|
@@ -329,7 +332,7 @@ export class SyncSagaCoordinator {
|
|
|
329
332
|
* the NEXT request context.
|
|
330
333
|
* When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
|
|
331
334
|
*/
|
|
332
|
-
|
|
335
|
+
private async executeSagaLoop({
|
|
333
336
|
initFrame,
|
|
334
337
|
initDomainGraph,
|
|
335
338
|
peer,
|
|
@@ -511,8 +514,8 @@ export class SyncSagaCoordinator {
|
|
|
511
514
|
throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
|
|
512
515
|
} else if (!contextResult.nextFrameInfo) {
|
|
513
516
|
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
|
|
514
|
-
} else if (contextResult.nextFrameInfo?.
|
|
515
|
-
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.
|
|
517
|
+
} else if (contextResult.nextFrameInfo?.sagaComplete) {
|
|
518
|
+
throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.sagaComplete? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
|
|
516
519
|
}
|
|
517
520
|
// #endregion error conditions throw
|
|
518
521
|
|
|
@@ -610,7 +613,7 @@ export class SyncSagaCoordinator {
|
|
|
610
613
|
}
|
|
611
614
|
}
|
|
612
615
|
|
|
613
|
-
|
|
616
|
+
private async analyzeDomainIbGibs({
|
|
614
617
|
domainIbGibs,
|
|
615
618
|
space,
|
|
616
619
|
}: {
|
|
@@ -656,7 +659,7 @@ export class SyncSagaCoordinator {
|
|
|
656
659
|
* Generates the first frame containing the Knowledge Vector of the Local Space.
|
|
657
660
|
* This is sent to the Receiver to begin Gap Analysis.
|
|
658
661
|
*/
|
|
659
|
-
|
|
662
|
+
private async createInitFrame({
|
|
660
663
|
sagaId,
|
|
661
664
|
sessionIdentity,
|
|
662
665
|
domainIbGibs,
|
|
@@ -739,7 +742,7 @@ export class SyncSagaCoordinator {
|
|
|
739
742
|
*
|
|
740
743
|
* @returns when all {@link expectedAddrs} are done being transmitted.
|
|
741
744
|
*/
|
|
742
|
-
|
|
745
|
+
private async pollForDomainPayloads({
|
|
743
746
|
expectedAddrs,
|
|
744
747
|
pollIntervalMs,
|
|
745
748
|
domainPayloadsMap,
|
|
@@ -824,7 +827,7 @@ export class SyncSagaCoordinator {
|
|
|
824
827
|
*
|
|
825
828
|
* This is a one-off on the receiver.
|
|
826
829
|
*/
|
|
827
|
-
|
|
830
|
+
private async handleResponseSagaContext({
|
|
828
831
|
sagaContext,
|
|
829
832
|
initDomainGraph,
|
|
830
833
|
mySpace,
|
|
@@ -940,7 +943,7 @@ export class SyncSagaCoordinator {
|
|
|
940
943
|
* 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
|
|
941
944
|
* 4. Returns an `Ack` frame containing these lists.
|
|
942
945
|
*/
|
|
943
|
-
|
|
946
|
+
private async handleInitFrame({
|
|
944
947
|
sagaIbGib,
|
|
945
948
|
messageData,
|
|
946
949
|
mySpace,
|
|
@@ -1289,7 +1292,7 @@ export class SyncSagaCoordinator {
|
|
|
1289
1292
|
*
|
|
1290
1293
|
* Returns a `Delta` frame.
|
|
1291
1294
|
*/
|
|
1292
|
-
|
|
1295
|
+
private async handleAckFrame({
|
|
1293
1296
|
sagaContext,
|
|
1294
1297
|
sagaIbGib,
|
|
1295
1298
|
initDomainGraph,
|
|
@@ -1565,70 +1568,6 @@ export class SyncSagaCoordinator {
|
|
|
1565
1568
|
}
|
|
1566
1569
|
}
|
|
1567
1570
|
|
|
1568
|
-
private async getPayloadsForRequestedInfos({
|
|
1569
|
-
deltaRequestAddrInfos,
|
|
1570
|
-
mySpace,
|
|
1571
|
-
}: {
|
|
1572
|
-
deltaRequestAddrInfos: SyncSagaRequestAddrInfo[];
|
|
1573
|
-
mySpace: IbGibSpaceAny;
|
|
1574
|
-
}): Promise<IbGib_V1[]> {
|
|
1575
|
-
const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
|
|
1576
|
-
try {
|
|
1577
|
-
if (logalot) { console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`); }
|
|
1578
|
-
/**
|
|
1579
|
-
* graph of ibgibs we will send to the receiver. addr-based, so will
|
|
1580
|
-
* already be unique (no need to call `unique` on the domains
|
|
1581
|
-
* ibgibs)
|
|
1582
|
-
*/
|
|
1583
|
-
const outgoingPayloadIbGibsDomainGraph: FlatIbGibGraph = {};
|
|
1584
|
-
for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
|
|
1585
|
-
let deltaDepGraph: FlatIbGibGraph;
|
|
1586
|
-
if (latestAddrAlreadyHave) {
|
|
1587
|
-
// already has some, so only get the delta
|
|
1588
|
-
// remember: if we didn't have the other's latest addr, then
|
|
1589
|
-
// this would be in the conflicts not requested addrs
|
|
1590
|
-
deltaDepGraph = await getDeltaDependencyGraph({
|
|
1591
|
-
ibGibAddr: addr,
|
|
1592
|
-
latestCommonFrameAddr: latestAddrAlreadyHave,
|
|
1593
|
-
space: mySpace,
|
|
1594
|
-
live: true,
|
|
1595
|
-
});
|
|
1596
|
-
} else {
|
|
1597
|
-
// doesn't have anything, so get the entire dependency graph
|
|
1598
|
-
// INEFFICIENT: we've already gotten all of the domain
|
|
1599
|
-
// dependencies in initDomainGraph, but getDependencyGraph
|
|
1600
|
-
// only works against a space so we are going to rerun this.
|
|
1601
|
-
// an optimization would be to adapt/create a new
|
|
1602
|
-
// getDependencyGraph to work against an existing flat ibgib
|
|
1603
|
-
// map.
|
|
1604
|
-
deltaDepGraph = await getDependencyGraph({
|
|
1605
|
-
ibGibAddr: addr,
|
|
1606
|
-
space: mySpace,
|
|
1607
|
-
live: true,
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
const depGraphSize = Object.keys(deltaDepGraph).length;
|
|
1612
|
-
if (depGraphSize === 0) {
|
|
1613
|
-
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)`);
|
|
1614
|
-
} else {
|
|
1615
|
-
// we have dependencies!
|
|
1616
|
-
Object.values(deltaDepGraph).forEach(x => {
|
|
1617
|
-
outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
const result = Object.values(outgoingPayloadIbGibsDomainGraph);
|
|
1623
|
-
return result;
|
|
1624
|
-
} catch (error) {
|
|
1625
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1626
|
-
throw error;
|
|
1627
|
-
} finally {
|
|
1628
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
1571
|
/**
|
|
1633
1572
|
* Handles the `Delta` frame.
|
|
1634
1573
|
*
|
|
@@ -1639,7 +1578,7 @@ export class SyncSagaCoordinator {
|
|
|
1639
1578
|
* 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
|
|
1640
1579
|
* 3. **Completion**: If no more requests, transitions to `Commit`.
|
|
1641
1580
|
*/
|
|
1642
|
-
|
|
1581
|
+
private async handleDeltaFrame({
|
|
1643
1582
|
sagaContext,
|
|
1644
1583
|
sagaIbGib,
|
|
1645
1584
|
srcGraph,
|
|
@@ -1701,27 +1640,32 @@ export class SyncSagaCoordinator {
|
|
|
1701
1640
|
// V1 timelines carry full history in `past`.
|
|
1702
1641
|
const pastAddrs = sagaIbGib.rel8ns?.past || [];
|
|
1703
1642
|
console.log(`${lc} [TEST DEBUG] pastAddrs count: ${pastAddrs.length}`);
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1643
|
+
|
|
1644
|
+
const sagaHistory = await getFullSyncSagaHistory({
|
|
1645
|
+
sagaIbGib,
|
|
1646
|
+
space: mySpace,
|
|
1647
|
+
});
|
|
1648
|
+
// #region validate/sanity sagaHistory
|
|
1649
|
+
if (sagaHistory.length === 0) { throw new Error(`(UNEXPECTED) sagaHistory.length is 0? We're in handleDeltaFrame. So we should have at least init and ack. (E: 815735c9cf756b275719bf434f180826)`); }
|
|
1650
|
+
if (sagaHistory.length === 1) { throw new Error(`(UNEXPECTED) sagaHistory.length is 1? We're in handleDeltaFrame. So we should have at least init and ack. (E: 0e4ef8e3ed088e83b2cdcd18ecea9826)`); }
|
|
1651
|
+
// #endregion validate/sanity sagaHistory
|
|
1652
|
+
|
|
1653
|
+
const ackGraphs = sagaHistory.filter(x =>
|
|
1654
|
+
x.msgStones.at(0)!.data!.stage === SyncStage.ack
|
|
1655
|
+
);
|
|
1656
|
+
// #region validate/sanity ackGraphs
|
|
1657
|
+
if (ackGraphs.length > 1) {
|
|
1658
|
+
throw new Error(`(UNEXPECTED) more than one ack stage found in sagaHistory? we're expecting exactly one. (E: 7150983bb3a841caf8acd60826ba4f26)`);
|
|
1659
|
+
} else if (ackGraphs.length === 0) {
|
|
1660
|
+
throw new Error(`(UNEXPECTED) couldn't find ack stage in sagaHistory? (E: 7d2da859196b86de28e7c8183af1e826)`);
|
|
1661
|
+
}
|
|
1662
|
+
if (ackGraphs[0].msgStones.length !== 1) {
|
|
1663
|
+
throw new Error(`(UNEXPECTED) ackGraph has more than one msg stone? only one expected right now. (E: f07d388abff8d26f98cbf6e8d3932826)`);
|
|
1724
1664
|
}
|
|
1665
|
+
// #endregion validate/sanity ackGraphs
|
|
1666
|
+
|
|
1667
|
+
const ackData = ackGraphs[0].msgStones[0].data as SyncSagaMessageAckData_V1;
|
|
1668
|
+
if (!ackData) { throw new Error(`(UNEXPECTED) ackData falsy? (E: f9af88427f66a8db1ba868b810e8b826)`); }
|
|
1725
1669
|
|
|
1726
1670
|
if (ackData && ackData.conflicts) {
|
|
1727
1671
|
const optimisticConflicts = ackData.conflicts.filter(c => !c.terminal);
|
|
@@ -1785,20 +1729,8 @@ export class SyncSagaCoordinator {
|
|
|
1785
1729
|
stage: SyncStage.delta,
|
|
1786
1730
|
payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
|
|
1787
1731
|
requests: hasMyRequests ? myRequests : undefined,
|
|
1788
|
-
proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
|
|
1789
|
-
// Wait. If we send data, we are NOT committing yet.
|
|
1790
|
-
// We are sending data. The OTHER side must ingest it.
|
|
1791
|
-
// So proposeCommit = true?
|
|
1792
|
-
// "Here is the data. I'm done. If you are good, let's commit."
|
|
1793
|
-
// Yes.
|
|
1794
1732
|
};
|
|
1795
1733
|
|
|
1796
|
-
// BUT if `peerProposesCommit` was true, and we are sending data, we are effectively rejecting/delaying it.
|
|
1797
|
-
// We just send the Delta. Peer receives it, ingests, sees ProposeCommit=True (from us), and then Commits.
|
|
1798
|
-
|
|
1799
|
-
// So yes, proposeCommit = true.
|
|
1800
|
-
responseDeltaData.proposeCommit = true;
|
|
1801
|
-
|
|
1802
1734
|
const deltaStone = await this.createSyncMsgStone({
|
|
1803
1735
|
data: responseDeltaData,
|
|
1804
1736
|
localSpace: mySpace,
|
|
@@ -1813,42 +1745,45 @@ export class SyncSagaCoordinator {
|
|
|
1813
1745
|
metaspace,
|
|
1814
1746
|
});
|
|
1815
1747
|
|
|
1816
|
-
// Build control payloads: frame + its dependencies (msg stone, identity)
|
|
1817
|
-
const payloadIbGibsControl: IbGib_V1[] = [deltaFrame, deltaStone];
|
|
1818
|
-
if (identity) { payloadIbGibsControl.push(identity); }
|
|
1819
|
-
|
|
1820
|
-
// return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
|
|
1821
1748
|
return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
|
|
1822
|
-
// throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
|
|
1823
|
-
|
|
1824
1749
|
} else {
|
|
1825
1750
|
// We have nothing to send.
|
|
1826
1751
|
|
|
1827
1752
|
if (peerProposesCommit) {
|
|
1828
1753
|
// Peer is done. We are done. -> Commit.
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
};
|
|
1834
|
-
|
|
1835
|
-
const commitStone = await this.createSyncMsgStone({
|
|
1836
|
-
data: commitData,
|
|
1837
|
-
localSpace: mySpace,
|
|
1838
|
-
metaspace
|
|
1754
|
+
// one last validate entire history?
|
|
1755
|
+
const history = await getFullSyncSagaHistory({
|
|
1756
|
+
sagaIbGib,
|
|
1757
|
+
space: mySpace,
|
|
1839
1758
|
});
|
|
1759
|
+
const validationErrors = await validateFullSyncSagaHistory({
|
|
1760
|
+
history,
|
|
1761
|
+
});
|
|
1762
|
+
if (validationErrors.length > 0) {
|
|
1763
|
+
const errorCommitFrame = await this.createCommitFrame({
|
|
1764
|
+
sagaIbGib,
|
|
1765
|
+
errors: validationErrors,
|
|
1766
|
+
metaspace, mySpace,
|
|
1767
|
+
identity,
|
|
1768
|
+
});
|
|
1769
|
+
return { frame: errorCommitFrame, }; /* <<<< returns early */
|
|
1770
|
+
}
|
|
1840
1771
|
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1772
|
+
await this.executeLocalCommit({
|
|
1773
|
+
sagaHistory: history,
|
|
1774
|
+
deltaFrame: sagaIbGib,
|
|
1775
|
+
localTempSpace: myTempSpace,
|
|
1845
1776
|
localSpace: mySpace,
|
|
1846
|
-
metaspace
|
|
1847
|
-
})
|
|
1777
|
+
metaspace,
|
|
1778
|
+
})
|
|
1848
1779
|
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1780
|
+
const commitFrame = await this.createCommitFrame({
|
|
1781
|
+
sagaIbGib,
|
|
1782
|
+
errors: undefined,
|
|
1783
|
+
metaspace,
|
|
1784
|
+
mySpace,
|
|
1785
|
+
identity,
|
|
1786
|
+
});
|
|
1852
1787
|
|
|
1853
1788
|
return { frame: commitFrame, };
|
|
1854
1789
|
|
|
@@ -1924,9 +1859,190 @@ export class SyncSagaCoordinator {
|
|
|
1924
1859
|
}
|
|
1925
1860
|
|
|
1926
1861
|
}
|
|
1862
|
+
/**
|
|
1863
|
+
* should throw if fails
|
|
1864
|
+
*/
|
|
1865
|
+
private async executeLocalCommit({
|
|
1866
|
+
deltaFrame,
|
|
1867
|
+
sagaHistory,
|
|
1868
|
+
metaspace, localSpace, localTempSpace,
|
|
1869
|
+
identity,
|
|
1870
|
+
}: {
|
|
1871
|
+
deltaFrame: SyncIbGib_V1;
|
|
1872
|
+
sagaHistory: SyncSagaFrameDependencyGraph[];
|
|
1873
|
+
metaspace: MetaspaceService;
|
|
1874
|
+
localSpace: IbGibSpaceAny;
|
|
1875
|
+
localTempSpace: IbGibSpaceAny;
|
|
1876
|
+
identity?: KeystoneIbGib_V1;
|
|
1877
|
+
}): Promise<void> {
|
|
1878
|
+
const lc = `${this.lc}[${this.executeLocalCommit.name}]`;
|
|
1879
|
+
try {
|
|
1880
|
+
if (logalot) { console.log(`${lc} starting... (I: 6734980446b86a63c1af6e2e206de826)`); }
|
|
1881
|
+
|
|
1882
|
+
// #region validate/sanity
|
|
1883
|
+
if (!deltaFrame.data) { throw new Error(`(UNEXPECTED) deltaFrame.data falsy? (E: a8be68d48668d93d992d793834823826)`); }
|
|
1884
|
+
// #endregion validate/sanity
|
|
1927
1885
|
|
|
1886
|
+
// * move all payload addrs from temp space to local space
|
|
1887
|
+
// * register each and every iteration of each timeline (including
|
|
1888
|
+
// stones, which are like their own sealed timeline of length 1)
|
|
1889
|
+
|
|
1890
|
+
const allPayloadAddrsDomainTransferred: IbGibAddr[] = [];
|
|
1891
|
+
const fnAddPayloadAddr = (addr: IbGibAddr) => {
|
|
1892
|
+
if (!allPayloadAddrsDomainTransferred.includes(addr)) {
|
|
1893
|
+
allPayloadAddrsDomainTransferred.push(addr);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1928
1896
|
|
|
1929
|
-
|
|
1897
|
+
sagaHistory.forEach(x => {
|
|
1898
|
+
if (!x.sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 34d7f8cdee14717ce828878d98f89826)`); }
|
|
1899
|
+
x.msgStones.forEach(msgStone => {
|
|
1900
|
+
if (!msgStone.data) { throw new Error(`(UNEXPECTED) msgStone.data falsy? (E: 21406101f91847514cc759c8a7382f26)`); }
|
|
1901
|
+
if (msgStone.data.stage === SyncStage.ack) {
|
|
1902
|
+
const ackData = msgStone.data as SyncSagaMessageAckData_V1;
|
|
1903
|
+
if (ackData.pushOfferInfos && ackData.pushOfferInfos.length > 0) {
|
|
1904
|
+
ackData.pushOfferInfos.forEach(info => {
|
|
1905
|
+
info.addrs.forEach(addr => fnAddPayloadAddr(addr));
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
} else if (msgStone.data.stage === SyncStage.delta) {
|
|
1909
|
+
const deltaData = msgStone.data as SyncSagaMessageDeltaData_V1;
|
|
1910
|
+
if (deltaData.payloadAddrsDomain && deltaData.payloadAddrsDomain.length > 0) {
|
|
1911
|
+
deltaData.payloadAddrsDomain.forEach(addr => fnAddPayloadAddr(addr));
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
})
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
// at this point, we have a list of ALL payload addrs retrieved.
|
|
1918
|
+
let allPayloadIbGibsDomainTransferred: IbGib_V1[] = [];
|
|
1919
|
+
const resGetAllTransferred =
|
|
1920
|
+
await getFromSpace({ addrs: allPayloadAddrsDomainTransferred, space: localTempSpace });
|
|
1921
|
+
if (resGetAllTransferred.success && resGetAllTransferred.ibGibs && resGetAllTransferred.ibGibs.length === allPayloadAddrsDomainTransferred.length) {
|
|
1922
|
+
allPayloadIbGibsDomainTransferred = resGetAllTransferred.ibGibs.concat();
|
|
1923
|
+
} else {
|
|
1924
|
+
// errored out, gather info
|
|
1925
|
+
if (!resGetAllTransferred.rawResultIbGib) { throw new Error(`(UNEXPECTED) !resGetAllTransferred.rawResultIbGib falsy? (E: dc6cf8729668f4fbe8c024f887d97a26)`); }
|
|
1926
|
+
const { addrsNotFound, addrsErrored, errors } = resGetAllTransferred.rawResultIbGib.data as IbGibSpaceResultData;
|
|
1927
|
+
throw new Error(`(UNEXPECTED) we couldn't get all addrs transferred throughout the saga from the tempspace (${localTempSpace.ib})? addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. errors: ${errors ?? []}(E: 222e341e634862b4d88ae7282584d826)`);
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// now we have all ibgibs transferred, first put them all the local space
|
|
1931
|
+
|
|
1932
|
+
const { payload_Dnas, payload_NonDnas } =
|
|
1933
|
+
await putInSpace_dnasThenNonDnas({
|
|
1934
|
+
ibGibs: allPayloadIbGibsDomainTransferred,
|
|
1935
|
+
space: localSpace
|
|
1936
|
+
});
|
|
1937
|
+
|
|
1938
|
+
const { mapWithTjp_NoDna, mapWithTjp_YesDna, mapWithoutTjps } =
|
|
1939
|
+
splitPerTjpAndOrDna({ ibGibs: payload_NonDnas, filterPrimitives: true });
|
|
1940
|
+
|
|
1941
|
+
// first register all non-tjp stones
|
|
1942
|
+
const nontjps = Object.values(mapWithoutTjps);
|
|
1943
|
+
for (const nontjp of nontjps) {
|
|
1944
|
+
await metaspace.registerNewIbGib({
|
|
1945
|
+
ibGib: nontjp,
|
|
1946
|
+
space: localSpace,
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
// next register each timeline in order...
|
|
1951
|
+
|
|
1952
|
+
// ...first the ones without dna...
|
|
1953
|
+
const timelinesByTjpAddr_NoDna =
|
|
1954
|
+
getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_NoDna) });
|
|
1955
|
+
const tjpAddrs_NoDna = Object.keys(timelinesByTjpAddr_NoDna);
|
|
1956
|
+
for (const tjpAddr_NoDna of tjpAddrs_NoDna) {
|
|
1957
|
+
const timelineIbGibs = timelinesByTjpAddr_NoDna[tjpAddr_NoDna];
|
|
1958
|
+
for (const ibGib of timelineIbGibs) {
|
|
1959
|
+
await metaspace.registerNewIbGib({
|
|
1960
|
+
ibGib,
|
|
1961
|
+
space: localSpace,
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// ...then the ones WITH dna.
|
|
1967
|
+
const timelinesByTjpAddr_YesDna =
|
|
1968
|
+
getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_YesDna) });
|
|
1969
|
+
const tjpAddrs_YesDna = Object.keys(timelinesByTjpAddr_YesDna);
|
|
1970
|
+
for (const tjpAddr_YesDna of tjpAddrs_YesDna) {
|
|
1971
|
+
const timelineIbGibs = timelinesByTjpAddr_YesDna[tjpAddr_YesDna];
|
|
1972
|
+
for (const ibGib of timelineIbGibs) {
|
|
1973
|
+
await metaspace.registerNewIbGib({
|
|
1974
|
+
ibGib,
|
|
1975
|
+
space: localSpace,
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
|
|
1981
|
+
} catch (error) {
|
|
1982
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1983
|
+
throw error;
|
|
1984
|
+
} finally {
|
|
1985
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
private async createCommitFrame({
|
|
1990
|
+
sagaIbGib,
|
|
1991
|
+
errors,
|
|
1992
|
+
metaspace,
|
|
1993
|
+
mySpace,
|
|
1994
|
+
identity,
|
|
1995
|
+
}: {
|
|
1996
|
+
sagaIbGib: SyncIbGib_V1,
|
|
1997
|
+
/**
|
|
1998
|
+
* if truthy and non-empty, will create an errored commit frame (data
|
|
1999
|
+
* has `success: false`)
|
|
2000
|
+
*/
|
|
2001
|
+
errors?: string[],
|
|
2002
|
+
metaspace: MetaspaceService,
|
|
2003
|
+
mySpace: IbGibSpaceAny,
|
|
2004
|
+
identity: KeystoneIbGib_V1 | undefined,
|
|
2005
|
+
}): Promise<SyncIbGib_V1> {
|
|
2006
|
+
const lc = `[${this.createCommitFrame.name}]`;
|
|
2007
|
+
try {
|
|
2008
|
+
if (logalot) { console.log(`${lc} starting... (I: 48fc57c023f80122135a284855757526)`); }
|
|
2009
|
+
const commitData: SyncSagaMessageCommitData_V1 = errors && errors.length > 0 ?
|
|
2010
|
+
{
|
|
2011
|
+
// errored
|
|
2012
|
+
sagaId: sagaIbGib.data!.uuid,
|
|
2013
|
+
stage: SyncStage.commit,
|
|
2014
|
+
success: false,
|
|
2015
|
+
errors,
|
|
2016
|
+
} :
|
|
2017
|
+
{
|
|
2018
|
+
// not errored
|
|
2019
|
+
sagaId: sagaIbGib.data!.uuid,
|
|
2020
|
+
stage: SyncStage.commit,
|
|
2021
|
+
success: true,
|
|
2022
|
+
};
|
|
2023
|
+
|
|
2024
|
+
const commitStone = await this.createSyncMsgStone({
|
|
2025
|
+
data: commitData,
|
|
2026
|
+
localSpace: mySpace,
|
|
2027
|
+
metaspace
|
|
2028
|
+
});
|
|
2029
|
+
const commitFrame = await this.evolveSyncSagaIbGib({
|
|
2030
|
+
prevSagaIbGib: sagaIbGib,
|
|
2031
|
+
msgStones: [commitStone],
|
|
2032
|
+
sessionIdentity: identity,
|
|
2033
|
+
localSpace: mySpace,
|
|
2034
|
+
metaspace,
|
|
2035
|
+
});
|
|
2036
|
+
return commitFrame;
|
|
2037
|
+
} catch (error) {
|
|
2038
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
2039
|
+
throw error;
|
|
2040
|
+
} finally {
|
|
2041
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
private async handleCommitFrame({
|
|
1930
2046
|
sagaIbGib,
|
|
1931
2047
|
mySpace,
|
|
1932
2048
|
myTempSpace,
|
|
@@ -1943,7 +2059,7 @@ export class SyncSagaCoordinator {
|
|
|
1943
2059
|
try {
|
|
1944
2060
|
if (logalot) { console.log(`${lc} starting... (I: e179573bdd881202f8ba3168da1c3826)`); }
|
|
1945
2061
|
|
|
1946
|
-
|
|
2062
|
+
let resNextSagaFrameInfo: NextSagaFrameInfo;
|
|
1947
2063
|
|
|
1948
2064
|
// Sender Logic (Finalizing):
|
|
1949
2065
|
// If we are here, we received a Commit frame from the Peer.
|
|
@@ -1951,15 +2067,52 @@ export class SyncSagaCoordinator {
|
|
|
1951
2067
|
// We should now:
|
|
1952
2068
|
// 1. Validate (implicitly done by receiving valid frame)
|
|
1953
2069
|
// 2. Perform our own cleanup (Temp -> Dest, if applicable)
|
|
1954
|
-
// 3. Return
|
|
2070
|
+
// 3. Return saga completion.
|
|
1955
2071
|
|
|
1956
|
-
//
|
|
2072
|
+
// one last validate entire history?
|
|
2073
|
+
const history = await getFullSyncSagaHistory({
|
|
2074
|
+
sagaIbGib,
|
|
2075
|
+
space: mySpace,
|
|
2076
|
+
});
|
|
2077
|
+
const validationErrors = await validateFullSyncSagaHistory({
|
|
2078
|
+
history,
|
|
2079
|
+
});
|
|
2080
|
+
if (validationErrors.length > 0) {
|
|
2081
|
+
const errorCommitFrame = await this.createCommitFrame({
|
|
2082
|
+
sagaIbGib,
|
|
2083
|
+
metaspace,
|
|
2084
|
+
mySpace,
|
|
2085
|
+
identity,
|
|
2086
|
+
errors: validationErrors,
|
|
2087
|
+
});
|
|
2088
|
+
return { frame: errorCommitFrame, }; /* <<<< returns early */
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
await this.executeLocalCommit({
|
|
2092
|
+
deltaFrame: sagaIbGib,
|
|
2093
|
+
sagaHistory: history,
|
|
2094
|
+
localSpace: mySpace,
|
|
2095
|
+
localTempSpace: myTempSpace,
|
|
2096
|
+
metaspace,
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
// todo: implement explicit cleanup logic here and in peer
|
|
2100
|
+
console.error(`${lc} NAG ERROR (NOT THROWN): implement cleanup logic, including add a cleanup method to the peer (E: 3a9a24befb98a981a88fbdbf52920e26)`);
|
|
1957
2101
|
if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
|
|
1958
2102
|
|
|
1959
|
-
|
|
2103
|
+
// the holy grail!
|
|
2104
|
+
return { sagaComplete: true };
|
|
1960
2105
|
} catch (error) {
|
|
1961
|
-
|
|
1962
|
-
|
|
2106
|
+
const emsg = `${lc} ${extractErrorMsg(error)}`;
|
|
2107
|
+
console.error(emsg);
|
|
2108
|
+
const errorCommitFrame = await this.createCommitFrame({
|
|
2109
|
+
sagaIbGib,
|
|
2110
|
+
errors: [emsg],
|
|
2111
|
+
metaspace,
|
|
2112
|
+
mySpace,
|
|
2113
|
+
identity,
|
|
2114
|
+
});
|
|
2115
|
+
return { frame: errorCommitFrame }
|
|
1963
2116
|
} finally {
|
|
1964
2117
|
if (logalot) { console.log(`${lc} complete.`); }
|
|
1965
2118
|
}
|
|
@@ -1968,7 +2121,7 @@ export class SyncSagaCoordinator {
|
|
|
1968
2121
|
|
|
1969
2122
|
// #endregion Handlers
|
|
1970
2123
|
|
|
1971
|
-
|
|
2124
|
+
private async createSyncMsgStone<TStoneData extends SyncSagaMessageData_V1>({
|
|
1972
2125
|
data,
|
|
1973
2126
|
localSpace,
|
|
1974
2127
|
metaspace,
|
|
@@ -2000,11 +2153,75 @@ export class SyncSagaCoordinator {
|
|
|
2000
2153
|
}
|
|
2001
2154
|
}
|
|
2002
2155
|
|
|
2156
|
+
private async getPayloadsForRequestedInfos({
|
|
2157
|
+
deltaRequestAddrInfos,
|
|
2158
|
+
mySpace,
|
|
2159
|
+
}: {
|
|
2160
|
+
deltaRequestAddrInfos: SyncSagaRequestAddrInfo[];
|
|
2161
|
+
mySpace: IbGibSpaceAny;
|
|
2162
|
+
}): Promise<IbGib_V1[]> {
|
|
2163
|
+
const lc = `${this.lc}[${this.getPayloadsForRequestedInfos.name}]`;
|
|
2164
|
+
try {
|
|
2165
|
+
if (logalot) { console.log(`${lc} starting... (I: 4fe13d0d80050f20a8b74ba80cee5826)`); }
|
|
2166
|
+
/**
|
|
2167
|
+
* graph of ibgibs we will send to the receiver. addr-based, so will
|
|
2168
|
+
* already be unique (no need to call `unique` on the domains
|
|
2169
|
+
* ibgibs)
|
|
2170
|
+
*/
|
|
2171
|
+
const outgoingPayloadIbGibsDomainGraph: FlatIbGibGraph = {};
|
|
2172
|
+
for (const { addr, latestAddrAlreadyHave } of deltaRequestAddrInfos) {
|
|
2173
|
+
let deltaDepGraph: FlatIbGibGraph;
|
|
2174
|
+
if (latestAddrAlreadyHave) {
|
|
2175
|
+
// already has some, so only get the delta
|
|
2176
|
+
// remember: if we didn't have the other's latest addr, then
|
|
2177
|
+
// this would be in the conflicts not requested addrs
|
|
2178
|
+
deltaDepGraph = await getDeltaDependencyGraph({
|
|
2179
|
+
ibGibAddr: addr,
|
|
2180
|
+
latestCommonFrameAddr: latestAddrAlreadyHave,
|
|
2181
|
+
space: mySpace,
|
|
2182
|
+
live: true,
|
|
2183
|
+
});
|
|
2184
|
+
} else {
|
|
2185
|
+
// doesn't have anything, so get the entire dependency graph
|
|
2186
|
+
// INEFFICIENT: we've already gotten all of the domain
|
|
2187
|
+
// dependencies in initDomainGraph, but getDependencyGraph
|
|
2188
|
+
// only works against a space so we are going to rerun this.
|
|
2189
|
+
// an optimization would be to adapt/create a new
|
|
2190
|
+
// getDependencyGraph to work against an existing flat ibgib
|
|
2191
|
+
// map.
|
|
2192
|
+
deltaDepGraph = await getDependencyGraph({
|
|
2193
|
+
ibGibAddr: addr,
|
|
2194
|
+
space: mySpace,
|
|
2195
|
+
live: true,
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
const depGraphSize = Object.keys(deltaDepGraph).length;
|
|
2200
|
+
if (depGraphSize === 0) {
|
|
2201
|
+
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)`);
|
|
2202
|
+
} else {
|
|
2203
|
+
// we have dependencies!
|
|
2204
|
+
Object.values(deltaDepGraph).forEach(x => {
|
|
2205
|
+
outgoingPayloadIbGibsDomainGraph[getIbGibAddr({ ibGib: x })] = x;
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
const result = Object.values(outgoingPayloadIbGibsDomainGraph);
|
|
2211
|
+
return result;
|
|
2212
|
+
} catch (error) {
|
|
2213
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
2214
|
+
throw error;
|
|
2215
|
+
} finally {
|
|
2216
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2003
2220
|
|
|
2004
2221
|
/**
|
|
2005
2222
|
* Evolves the saga timeline with a new frame.
|
|
2006
2223
|
*/
|
|
2007
|
-
|
|
2224
|
+
private async evolveSyncSagaIbGib({
|
|
2008
2225
|
prevSagaIbGib,
|
|
2009
2226
|
conflictStrategy,
|
|
2010
2227
|
msgStones,
|
|
@@ -2128,7 +2345,7 @@ export class SyncSagaCoordinator {
|
|
|
2128
2345
|
}
|
|
2129
2346
|
}
|
|
2130
2347
|
|
|
2131
|
-
|
|
2348
|
+
private async getStageAndPayloadFromFrame({
|
|
2132
2349
|
sagaFrame,
|
|
2133
2350
|
space
|
|
2134
2351
|
}: {
|
|
@@ -2164,7 +2381,7 @@ export class SyncSagaCoordinator {
|
|
|
2164
2381
|
}
|
|
2165
2382
|
}
|
|
2166
2383
|
|
|
2167
|
-
|
|
2384
|
+
private sortTimelinesTopologically(timelines: { [tjp: string]: IbGib_V1[] }): string[] {
|
|
2168
2385
|
const lc = `${this.lc}[${this.sortTimelinesTopologically.name}]`;
|
|
2169
2386
|
const tjps = Object.keys(timelines);
|
|
2170
2387
|
if (tjps.length === 0) { return []; }
|