@ibgib/core-gib 0.1.29 → 0.1.30
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/.vscode/launch.json +17 -1
- package/dist/common/meta-stone/meta-stone-helper.d.mts.map +1 -1
- package/dist/common/meta-stone/meta-stone-helper.mjs +11 -5
- package/dist/common/meta-stone/meta-stone-helper.mjs.map +1 -1
- package/dist/common/meta-stone/meta-stone-types.d.mts +5 -2
- package/dist/common/meta-stone/meta-stone-types.d.mts.map +1 -1
- package/dist/sync/strategies/conflict-optimistic.d.mts +16 -0
- package/dist/sync/strategies/conflict-optimistic.d.mts.map +1 -1
- package/dist/sync/strategies/conflict-optimistic.mjs +28 -1
- package/dist/sync/strategies/conflict-optimistic.mjs.map +1 -1
- package/dist/sync/sync-conflict.respec.mjs +5 -5
- package/dist/sync/sync-conflict.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +62 -9
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +3 -3
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +4 -4
- package/dist/sync/sync-innerspace.respec.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 +12 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +15 -10
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +44 -46
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +3 -3
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/timeline/timeline-api.d.mts +12 -0
- package/dist/timeline/timeline-api.d.mts.map +1 -1
- package/dist/timeline/timeline-api.mjs +26 -0
- package/dist/timeline/timeline-api.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.d.mts +19 -0
- package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs +189 -30
- package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +9 -0
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/space-helper.d.mts.map +1 -1
- package/dist/witness/space/space-helper.mjs +2 -1
- package/dist/witness/space/space-helper.mjs.map +1 -1
- package/package.json +2 -1
- package/src/common/meta-stone/meta-stone-helper.mts +8 -4
- package/src/common/meta-stone/meta-stone-types.mts +5 -2
- package/src/sync/README.md +4 -4
- package/src/sync/docs/architecture.md +6 -6
- package/src/sync/strategies/conflict-optimistic.mts +41 -4
- package/src/sync/sync-conflict.respec.mts +5 -5
- package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -1
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +73 -9
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
- package/src/sync/sync-innerspace-partial-update.respec.mts +2 -2
- package/src/sync/sync-innerspace.respec.mts +3 -3
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +12 -2
- package/src/sync/sync-saga-coordinator.mts +42 -48
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +3 -3
- package/src/timeline/timeline-api.mts +51 -11
- package/src/witness/space/inner-space/inner-space-v1.mts +191 -29
- package/src/witness/space/inner-space/inner-space-v1.respec.mts +13 -0
- package/src/witness/space/space-helper.mts +3 -2
- package/test_output.log +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module conflict-optimistic
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Implements the Optimistic Conflict Resolution Strategy.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Logic:
|
|
7
7
|
* 1. findLCA: Traverses `past` to find common ancestor.
|
|
8
8
|
* 2. mergeDivergentTimelines: Replays DNA to create a merged tip.
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
import { IbGib_V1, IbGibRel8ns_V1, IbGibData_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
12
12
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
13
|
+
|
|
13
14
|
import { IbGibSpaceAny } from '../../witness/space/space-base-v1.mjs';
|
|
14
15
|
import { getFromSpace, putInSpace } from '../../witness/space/space-helper.mjs';
|
|
15
16
|
import { mut8Timeline } from '../../timeline/timeline-api.mjs';
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
const logalot = GLOBAL_LOG_A_LOT || true;
|
|
18
19
|
|
|
19
20
|
export interface LCAResult {
|
|
20
21
|
lcaAddr: string;
|
|
@@ -22,9 +23,42 @@ export interface LCAResult {
|
|
|
22
23
|
branchB: IbGib_V1[]; // Path from LCA to Tip B (exclusive of LCA)
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
export interface TimelinesAnalysis {
|
|
27
|
+
latestCommonAddr: IbGibAddr;
|
|
28
|
+
uniqueBranchA
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* gets a quick analysis comparing/contrasting two timeline addrs.
|
|
33
|
+
*
|
|
34
|
+
* This does not do in-depth analysis that would involve retrieving ibgibs
|
|
35
|
+
from
|
|
36
|
+
* any spaces.
|
|
37
|
+
*/
|
|
38
|
+
export function analyzeTimelinesShallow({
|
|
39
|
+
a,
|
|
40
|
+
b,
|
|
41
|
+
}: {
|
|
42
|
+
a: IbGibAddr[],
|
|
43
|
+
b: IbGibAddr[],
|
|
44
|
+
}
|
|
45
|
+
): IbGibAddr {
|
|
46
|
+
const lc = `[${analyzeTimelinesShallow.name}]`;
|
|
47
|
+
try {
|
|
48
|
+
if (logalot) { console.log(`${lc} starting... (I: 1f65b883a2089948842c927c6a08c826)`); }
|
|
49
|
+
|
|
50
|
+
throw new Error(`not implemented (E: eab7ac95806ff9c60dafa22eb59cb926)`);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
53
|
+
throw error;
|
|
54
|
+
} finally {
|
|
55
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
25
59
|
/**
|
|
26
60
|
* Finds the Last Common Ancestor (LCA) between two divergent timelines.
|
|
27
|
-
*
|
|
61
|
+
*
|
|
28
62
|
* Note: simplistic implementation assuming simple linear divergence.
|
|
29
63
|
* Complex DAGs might require more robust graph traversal.
|
|
30
64
|
*/
|
|
@@ -114,6 +148,9 @@ export async function findLCA({
|
|
|
114
148
|
}
|
|
115
149
|
|
|
116
150
|
import { graftTimelines, mergeTextLCS } from '../graft-info/graft-info-helpers.mjs';
|
|
151
|
+
import { IbGibAddr } from '@ibgib/ts-gib';
|
|
152
|
+
import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
|
|
153
|
+
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
117
154
|
|
|
118
155
|
/**
|
|
119
156
|
* Optimistically grafts two divergent timelines by inspecting DNA and replaying transforms.
|
|
@@ -166,7 +166,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
166
166
|
// This ensures the conflict precondition exists.
|
|
167
167
|
await ifWe(sir, 'verify receiver KV pre-sync', async () => {
|
|
168
168
|
try {
|
|
169
|
-
const destKV = await receiverCoordinator.
|
|
169
|
+
const destKV = await receiverCoordinator.getKnowledgeMap({
|
|
170
170
|
space: destSpace,
|
|
171
171
|
metaspace,
|
|
172
172
|
domainIbGibs: [v1_Dest]
|
|
@@ -186,7 +186,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
186
186
|
|
|
187
187
|
} catch (error) {
|
|
188
188
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
189
|
-
iReckon(sir, true).asTo('
|
|
189
|
+
iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
|
|
@@ -251,7 +251,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
251
251
|
|
|
252
252
|
try {
|
|
253
253
|
// Get the KV for the Source Space
|
|
254
|
-
const sourceKV = await senderCoordinator.
|
|
254
|
+
const sourceKV = await senderCoordinator.getKnowledgeMap({
|
|
255
255
|
space: sourceSpace,
|
|
256
256
|
metaspace,
|
|
257
257
|
domainIbGibs: [testRoot] // We want to know the tip of this timeline
|
|
@@ -260,7 +260,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
260
260
|
getTjpAddr({ ibGib: testRoot, defaultIfNone: 'incomingAddr' }) ??
|
|
261
261
|
getIbGibAddr({ ibGib: testRoot });
|
|
262
262
|
|
|
263
|
-
if (logalot) { console.log(`${lc}
|
|
263
|
+
if (logalot) { console.log(`${lc} getKnowledgeMap returned. sourceKV: ${pretty(sourceKV)} (I: e8780cda37c8b2a46eeb85786874e926)`); }
|
|
264
264
|
|
|
265
265
|
const sourceTipAddr = sourceKV[tjpAddr];
|
|
266
266
|
if (!sourceTipAddr) {
|
|
@@ -319,7 +319,7 @@ await respecfully(sir, `Sync Conflict Resolution`, async () => {
|
|
|
319
319
|
}
|
|
320
320
|
} catch (error) {
|
|
321
321
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
322
|
-
iReckon(sir, true).asTo('
|
|
322
|
+
iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
});
|
|
@@ -128,7 +128,7 @@ await respecfully(sir, `Sync InnerSpaces (Deep Updates)`, async () => {
|
|
|
128
128
|
// 5. Verify Dest
|
|
129
129
|
console.log(`${lc} Verifying Destination...`);
|
|
130
130
|
|
|
131
|
-
await
|
|
131
|
+
await ifWe(sir, `verify deep timeline present`, async () => {
|
|
132
132
|
// Verify Tip (V2)
|
|
133
133
|
const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
|
|
134
134
|
iReckon(sir, getV2.success).asTo('Tip V2 present').isGonnaBeTrue();
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
12
12
|
import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
13
13
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
14
|
-
import {
|
|
14
|
+
import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
|
|
15
15
|
|
|
16
16
|
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
|
|
17
|
-
import { putInSpace, getFromSpace } from '../witness/space/space-helper.mjs';
|
|
17
|
+
import { putInSpace, getFromSpace, registerNewIbGib } from '../witness/space/space-helper.mjs';
|
|
18
18
|
import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
|
|
19
19
|
import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
|
|
20
20
|
import { createTimelineRootHelper, getTestKeystoneServiceHelper } from '../agent-helpers.mjs';
|
|
@@ -23,6 +23,8 @@ import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-
|
|
|
23
23
|
import { toDto } from '../common/other/ibgib-helper.mjs';
|
|
24
24
|
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
|
|
25
25
|
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
|
|
26
|
+
import { GetIbGibResult } from '../common/other/other-types.mjs';
|
|
27
|
+
import { IbGibSpaceAny } from '../witness/space/space-base-v1.mjs';
|
|
26
28
|
|
|
27
29
|
const logalot = true;
|
|
28
30
|
const lc = `[sync-innerspace-dest-ahead.respec]`;
|
|
@@ -73,22 +75,43 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
73
75
|
// Root -> V1 (Shared) -> V2 (Dest has New)
|
|
74
76
|
// Source only has V1.
|
|
75
77
|
|
|
76
|
-
const
|
|
78
|
+
const v0 = await createTimelineRootHelper<TestData>({
|
|
77
79
|
ib: 'timeline_root_ff',
|
|
78
80
|
data: { type: 'root', label: 'Root' },
|
|
79
81
|
space: sourceSpace,
|
|
80
82
|
});
|
|
83
|
+
const addrV0 = getIbGibAddr({ ibGib: v0 });
|
|
84
|
+
console.log(pretty(v0));
|
|
85
|
+
|
|
86
|
+
console.log('0000000000000')
|
|
87
|
+
console.log('0000000000000')
|
|
88
|
+
console.log('0000000000000')
|
|
89
|
+
console.log('0000000000000')
|
|
90
|
+
console.log('0000000000000')
|
|
81
91
|
|
|
82
92
|
// V1 (Both have it, but we create in source and copy to dest)
|
|
83
93
|
const v1 = await mut8Timeline<TestData>({
|
|
84
|
-
timeline:
|
|
94
|
+
timeline: v0,
|
|
85
95
|
mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1' } },
|
|
86
96
|
metaspace,
|
|
87
97
|
space: sourceSpace,
|
|
88
98
|
});
|
|
99
|
+
const addrV1 = getIbGibAddr({ ibGib: v1 });
|
|
100
|
+
console.log(pretty(v1));
|
|
101
|
+
|
|
89
102
|
|
|
90
103
|
// Transfer Root & V1 to Dest
|
|
91
|
-
await putInSpace({ space: destSpace, ibGibs: [
|
|
104
|
+
await putInSpace({ space: destSpace, ibGibs: [v0, v1] }); // Naive seeding
|
|
105
|
+
await registerNewIbGib({ space: destSpace, ibGib: v0 });
|
|
106
|
+
await registerNewIbGib({ space: destSpace, ibGib: v1 });
|
|
107
|
+
|
|
108
|
+
console.log('1111111111111111111111111111111')
|
|
109
|
+
console.log('1111111111111111111111111111111')
|
|
110
|
+
console.log('1111111111111111111111111111111')
|
|
111
|
+
console.log('1111111111111111111111111111111')
|
|
112
|
+
console.log('1111111111111111111111111111111')
|
|
113
|
+
console.log('1111111111111111111111111111111')
|
|
114
|
+
console.log('1111111111111111111111111111111')
|
|
92
115
|
|
|
93
116
|
// V2 (Created in Dest ONLY)
|
|
94
117
|
const v2 = await mut8Timeline<TestData>({
|
|
@@ -98,12 +121,31 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
98
121
|
space: destSpace, // Mutate in Dest
|
|
99
122
|
});
|
|
100
123
|
const addrV2 = getIbGibAddr({ ibGib: v2 });
|
|
124
|
+
console.log(pretty(v2));
|
|
125
|
+
|
|
126
|
+
const fnAddrExistsInSpace = async (addr: IbGibAddr, space: IbGibSpaceAny) => {
|
|
127
|
+
const resGet = await getFromSpace({ addr, space });
|
|
128
|
+
return resGet.success && resGet.ibGibs && resGet.ibGibs.length === 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log('22222222222')
|
|
132
|
+
console.log('22222222222')
|
|
133
|
+
console.log('22222222222')
|
|
134
|
+
console.log('22222222222')
|
|
135
|
+
console.log('22222222222')
|
|
136
|
+
console.log('22222222222')
|
|
137
|
+
console.log('22222222222')
|
|
138
|
+
|
|
101
139
|
|
|
102
140
|
await ifWeMight(sir, 'verify setup', async () => {
|
|
103
141
|
// Ensure V2 is ONLY in Dest (it is, per `space: destSpace`)
|
|
104
142
|
// Ensure Source does NOT have V2
|
|
105
|
-
|
|
106
|
-
iReckon(sir,
|
|
143
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, sourceSpace)).asTo('source has V0').isGonnaBeTrue();
|
|
144
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, sourceSpace)).asTo('source has V1').isGonnaBeTrue();
|
|
145
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, sourceSpace)).asTo('source has V2').isGonnaBeFalse();
|
|
146
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, destSpace)).asTo('dest has V0').isGonnaBeTrue();
|
|
147
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, destSpace)).asTo('dest has V1').isGonnaBeTrue();
|
|
148
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, destSpace)).asTo('dest has V2').isGonnaBeTrue();
|
|
107
149
|
});
|
|
108
150
|
|
|
109
151
|
// 3. Setup Sync
|
|
@@ -119,6 +161,14 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
119
161
|
receiverCoordinator,
|
|
120
162
|
receiverMetaspace: metaspace,
|
|
121
163
|
});
|
|
164
|
+
console.log('333333333')
|
|
165
|
+
console.log('333333333')
|
|
166
|
+
console.log('333333333')
|
|
167
|
+
console.log('333333333')
|
|
168
|
+
console.log('333333333')
|
|
169
|
+
console.log('333333333')
|
|
170
|
+
console.log('333333333')
|
|
171
|
+
|
|
122
172
|
|
|
123
173
|
// 4. Run Sync (Source Pushes V1)
|
|
124
174
|
console.log(`${lc} Running Sync...`);
|
|
@@ -132,13 +182,27 @@ await respecfully(sir, `Sync InnerSpaces (Dest Ahead)`, async () => {
|
|
|
132
182
|
|
|
133
183
|
await resSync.done;
|
|
134
184
|
|
|
135
|
-
|
|
185
|
+
console.log('444444444')
|
|
186
|
+
console.log('444444444')
|
|
187
|
+
console.log('444444444')
|
|
188
|
+
console.log('444444444')
|
|
189
|
+
console.log('444444444')
|
|
190
|
+
console.log('444444444')
|
|
191
|
+
console.log('444444444')
|
|
192
|
+
|
|
193
|
+
// 5. Verify Sync (v2 should be in both source and dest now)
|
|
136
194
|
console.log(`${lc} Verifying Destination...`);
|
|
137
195
|
|
|
138
196
|
await ifWeMight(sir, `verify dest stays ahead`, async () => {
|
|
139
197
|
// Verify Tip (V2)
|
|
140
198
|
const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
|
|
141
|
-
|
|
199
|
+
|
|
200
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, sourceSpace)).asTo('source has V0').isGonnaBeTrue();
|
|
201
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, sourceSpace)).asTo('source has V1').isGonnaBeTrue();
|
|
202
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, sourceSpace)).asTo('source has V2').isGonnaBeTrue();
|
|
203
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV0, destSpace)).asTo('dest has V0').isGonnaBeTrue();
|
|
204
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV1, destSpace)).asTo('dest has V1').isGonnaBeTrue();
|
|
205
|
+
iReckon(sir, await fnAddrExistsInSpace(addrV2, destSpace)).asTo('dest has V2').isGonnaBeTrue();
|
|
142
206
|
|
|
143
207
|
// Verify V2 points to V1
|
|
144
208
|
const v2IbGib = getV2.ibGibs![0];
|
|
@@ -130,7 +130,7 @@ await respecfully(sir, `Sync InnerSpaces (Multiple Timelines)`, async () => {
|
|
|
130
130
|
// 5. Verify Dest
|
|
131
131
|
console.log(`${lc} Verifying Destination...`);
|
|
132
132
|
|
|
133
|
-
await
|
|
133
|
+
await ifWe(sir, `verify timelines present`, async () => {
|
|
134
134
|
// Verify A
|
|
135
135
|
const getA = await getFromSpace({ space: destSpace, addr: addrA });
|
|
136
136
|
iReckon(sir, getA.success).asTo('Timeline A present').isGonnaBeTrue();
|
|
@@ -98,7 +98,7 @@ await respecfully(sir, `Sync InnerSpaces (Partial Update)`, async () => {
|
|
|
98
98
|
// Transfer Root & V1 to Dest (Simulate previous sync)
|
|
99
99
|
await putInSpace({ space: destSpace, ibGibs: [root, v1] });
|
|
100
100
|
|
|
101
|
-
await
|
|
101
|
+
await ifWe(sir, 'verify setup', async () => {
|
|
102
102
|
// Verify Dest has V1
|
|
103
103
|
const checkV1 = await getFromSpace({ space: destSpace, addr: getIbGibAddr({ ibGib: v1 }) });
|
|
104
104
|
iReckon(sir, checkV1.success).asTo('Dest has V1').isGonnaBeTrue();
|
|
@@ -137,7 +137,7 @@ await respecfully(sir, `Sync InnerSpaces (Partial Update)`, async () => {
|
|
|
137
137
|
// 5. Verify Dest (Should now have V2)
|
|
138
138
|
console.log(`${lc} Verifying Destination...`);
|
|
139
139
|
|
|
140
|
-
await
|
|
140
|
+
await ifWe(sir, `verify dest updated`, async () => {
|
|
141
141
|
// Verify Tip (V2)
|
|
142
142
|
const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
|
|
143
143
|
iReckon(sir, getV2.success).asTo('V2 present in Dest').isGonnaBeTrue();
|
|
@@ -154,18 +154,18 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
|
154
154
|
try {
|
|
155
155
|
const getChildInDest = await getFromSpace({ space: destSpace, addr: childAddr });
|
|
156
156
|
|
|
157
|
-
await
|
|
157
|
+
await ifWe(sir, `verify success getChildInDest`, async () => {
|
|
158
158
|
iReckon(sir, getChildInDest.success).asTo('Child present in Dest').isGonnaBeTrue();
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
await
|
|
161
|
+
await ifWe(sir, `verify getChildInDest.ibGibs`, async () => {
|
|
162
162
|
const firstChild = getChildInDest.ibGibs?.[0];
|
|
163
163
|
if (logalot) { console.log(`${lc} firstChild: ${pretty(firstChild)}`); }
|
|
164
164
|
iReckon(sir, firstChild?.data?.n).asTo('Child content matches').isGonnaBe(2);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
} catch (error) {
|
|
168
|
-
await
|
|
168
|
+
await ifWe(sir, `doh`, async () => {
|
|
169
169
|
// hack here I'm getting tired...
|
|
170
170
|
iReckon(sir, true).asTo(`error: ${extractErrorMsg(error)}`).isGonnaBeFalse();
|
|
171
171
|
});
|
|
@@ -245,6 +245,17 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
// spin off the payloads, as that may take a long time
|
|
248
|
+
if (responseCtx.payloadIbGibsDomain?.length ?? 0 > 0) {
|
|
249
|
+
// i guess this only will hit once we have conflicts, because
|
|
250
|
+
// it's the return trip, i.e., from the receiver. The initial
|
|
251
|
+
// ack frame does it through push offers?
|
|
252
|
+
await delay(2000);
|
|
253
|
+
console.warn(`${lc} this delay does hit (W: 3940d47d2a7433833837af4cef171126)`)
|
|
254
|
+
for (let i = 0; i < 100; i++) {
|
|
255
|
+
const element = 100;
|
|
256
|
+
console.log(`THIS HITS`)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
248
259
|
const payloadIbGibsDomain_response = responseCtx.payloadIbGibsDomain;
|
|
249
260
|
if (payloadIbGibsDomain_response) {
|
|
250
261
|
new Promise<void>(async (resolve, reject) => {
|
|
@@ -253,13 +264,12 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
|
|
|
253
264
|
if (logalot) { console.log(`${lcPut} starting... (I: 4133e736b4c80d2cf94448b87b206826)`); }
|
|
254
265
|
|
|
255
266
|
// 2 seconds...i want to see huge blocks of logs entries for this to see this happening
|
|
256
|
-
const delayMs =
|
|
267
|
+
const delayMs = 2000;
|
|
257
268
|
for (let i = 0; i < 10; i++) {
|
|
258
269
|
console.warn(`${lc} ARTIFICIALLY DELAYING PAYLOADS TO MIMIC TRANSFER LATENCY. REMOVE THIS DELAY!! (W: 5e7b28f1daa82d955897e9d8cdb16f26)`)
|
|
259
270
|
await delay(delayMs);
|
|
260
271
|
}
|
|
261
272
|
|
|
262
|
-
|
|
263
273
|
await putInSpace_dnasThenNonDnas({
|
|
264
274
|
ibGibs: payloadIbGibsDomain_response,
|
|
265
275
|
space: senderTempSpace,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN,
|
|
24
24
|
} from "./sync-constants.mjs";
|
|
25
25
|
import {
|
|
26
|
-
appendToTimeline, createTimeline, Rel8nInfo, Rel8nRemovalInfo
|
|
26
|
+
appendToTimeline, createTimeline, getHistory, getHistoryAddrs, Rel8nInfo, Rel8nRemovalInfo
|
|
27
27
|
} from "../timeline/timeline-api.mjs";
|
|
28
28
|
import {
|
|
29
29
|
SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
|
|
@@ -536,10 +536,10 @@ export class SyncSagaCoordinator {
|
|
|
536
536
|
}
|
|
537
537
|
|
|
538
538
|
/**
|
|
539
|
-
* Helper to get Knowledge
|
|
539
|
+
* Helper to get Knowledge Map for specific domain ibGibs or TJPs.
|
|
540
540
|
* Useful for testing and external validation.
|
|
541
541
|
*/
|
|
542
|
-
public async
|
|
542
|
+
public async getKnowledgeMap({
|
|
543
543
|
space,
|
|
544
544
|
metaspace,
|
|
545
545
|
domainIbGibs,
|
|
@@ -550,7 +550,7 @@ export class SyncSagaCoordinator {
|
|
|
550
550
|
domainIbGibs?: IbGib_V1[],
|
|
551
551
|
tjpAddrs?: string[],
|
|
552
552
|
}): Promise<{ [tjp: string]: string | null }> {
|
|
553
|
-
const lc = `${this.lc}[${this.
|
|
553
|
+
const lc = `${this.lc}[${this.getKnowledgeMap.name}]`;
|
|
554
554
|
try {
|
|
555
555
|
if (logalot) { console.log(`${lc} starting... (I: e184f8a7818666febfbbd2d841ed3826)`); }
|
|
556
556
|
// console.dir(space);
|
|
@@ -588,7 +588,7 @@ export class SyncSagaCoordinator {
|
|
|
588
588
|
} else {
|
|
589
589
|
// No info provided. Return empty? Or throw?
|
|
590
590
|
// User test context implied "everything", but implementation requires scope.
|
|
591
|
-
console.warn(`${lc} No domainIbGibs or tjpAddrs provided. Returning empty
|
|
591
|
+
console.warn(`${lc} No domainIbGibs or tjpAddrs provided. Returning empty Knowledge.`);
|
|
592
592
|
return {};
|
|
593
593
|
}
|
|
594
594
|
|
|
@@ -655,7 +655,7 @@ export class SyncSagaCoordinator {
|
|
|
655
655
|
* @remarks
|
|
656
656
|
* **Execution Context**: **Sender (Local)**.
|
|
657
657
|
*
|
|
658
|
-
* Generates the first frame containing the Knowledge
|
|
658
|
+
* Generates the first frame containing the Knowledge Map of the Local Space.
|
|
659
659
|
* This is sent to the Receiver to begin Gap Analysis.
|
|
660
660
|
*/
|
|
661
661
|
private async createInitFrame({
|
|
@@ -687,17 +687,17 @@ export class SyncSagaCoordinator {
|
|
|
687
687
|
// we need to store the fullGraph in our tempSpace for later...
|
|
688
688
|
await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
|
|
689
689
|
|
|
690
|
-
// populate our knowledge
|
|
691
|
-
const
|
|
690
|
+
// populate our knowledge map with tjp -> latest addr/tip in timeline
|
|
691
|
+
const knowledgeMap: { [tjp: string]: IbGibAddr } = {};
|
|
692
692
|
Object.keys(srcTimelinesMap).forEach(tjp => {
|
|
693
693
|
const timeline = srcTimelinesMap[tjp];
|
|
694
694
|
const tip = timeline.at(-1)!;
|
|
695
|
-
|
|
695
|
+
knowledgeMap[tjp] = getIbGibAddr({ ibGib: tip });
|
|
696
696
|
});
|
|
697
697
|
const initData: SyncSagaMessageInitData_V1 = {
|
|
698
698
|
sagaId,
|
|
699
699
|
stage: SyncStage.init,
|
|
700
|
-
|
|
700
|
+
knowledgeMap: knowledgeMap,
|
|
701
701
|
identity: sessionIdentity, // KeystoneIbGib is already public data
|
|
702
702
|
mode: SyncMode.sync,
|
|
703
703
|
stones: srcStones.map(s => getIbGibAddr({ ibGib: s })),
|
|
@@ -815,16 +815,21 @@ export class SyncSagaCoordinator {
|
|
|
815
815
|
*
|
|
816
816
|
* ## 1. Sender
|
|
817
817
|
*
|
|
818
|
-
* On the sender, this is called within the
|
|
819
|
-
* initiates and drives the
|
|
818
|
+
* On the sender, this is called repeatedly within the the sync saga
|
|
819
|
+
* coordinator's {@link executeSagaLoop} which initiates and drives the
|
|
820
|
+
* overall sync process. This loop continues until there are no more frames
|
|
821
|
+
* to be exchanged between endpoints.
|
|
820
822
|
*
|
|
821
823
|
* ## 2. Receiver
|
|
822
824
|
*
|
|
823
|
-
* On the receiver, this is called directly
|
|
824
|
-
*
|
|
825
|
-
*
|
|
825
|
+
* On the receiver, this is called directly from {@link continueSync}, which
|
|
826
|
+
* itself is called either directly by the concrete sync peer or from within
|
|
827
|
+
* the receiving node endpoint. That endpoint's job is basically to get
|
|
828
|
+
* these things collocated and prepared to make this call.
|
|
826
829
|
*
|
|
827
|
-
*
|
|
830
|
+
* In contrast to the sender, this can be thought of as a one-off on the
|
|
831
|
+
* receiver, since the sender's {@link executeSagaLoop} is what actually
|
|
832
|
+
* drives the ping-pong process.
|
|
828
833
|
*/
|
|
829
834
|
private async handleResponseSagaContext({
|
|
830
835
|
sagaContext,
|
|
@@ -868,11 +873,6 @@ export class SyncSagaCoordinator {
|
|
|
868
873
|
|
|
869
874
|
if (logalot) { console.log(`${lc} handling frame stage: ${stage}`); }
|
|
870
875
|
|
|
871
|
-
/**
|
|
872
|
-
* don't like this name, need to refactor
|
|
873
|
-
*/
|
|
874
|
-
const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
|
|
875
|
-
|
|
876
876
|
let nextFrameInfo: NextSagaFrameInfo;
|
|
877
877
|
switch (stage) {
|
|
878
878
|
case SyncStage.init:
|
|
@@ -899,7 +899,6 @@ export class SyncSagaCoordinator {
|
|
|
899
899
|
nextFrameInfo = await this.handleDeltaFrame({
|
|
900
900
|
sagaContext,
|
|
901
901
|
sagaIbGib,
|
|
902
|
-
srcGraph,
|
|
903
902
|
metaspace, mySpace, myTempSpace,
|
|
904
903
|
identity,
|
|
905
904
|
});
|
|
@@ -940,7 +939,7 @@ export class SyncSagaCoordinator {
|
|
|
940
939
|
* **Execution Context**: **Receiver (Remote)**.
|
|
941
940
|
*
|
|
942
941
|
* The Receiver performs Gap Analysis here:
|
|
943
|
-
* 1. Compares Sender's Knowledge
|
|
942
|
+
* 1. Compares Sender's Knowledge Map (in `sagaIbGib`) vs Receiver's Local Knowledge Map.
|
|
944
943
|
* 2. Identifies what Sender needs (`pushOfferAddrs`).
|
|
945
944
|
* 3. Identifies what Receiver needs (`deltaRequestAddrInfos`).
|
|
946
945
|
* 4. Returns an `Ack` frame containing these lists.
|
|
@@ -983,8 +982,8 @@ export class SyncSagaCoordinator {
|
|
|
983
982
|
throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
|
|
984
983
|
}
|
|
985
984
|
// if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
|
|
986
|
-
if (!initData || !initData.
|
|
987
|
-
throw new Error(`${lc} Invalid init frame: missing
|
|
985
|
+
if (!initData || !initData.knowledgeMap) {
|
|
986
|
+
throw new Error(`${lc} Invalid init frame: missing knowledgeMap (E: ed02c869e028d2d06841b9c7f80f2826)`);
|
|
988
987
|
}
|
|
989
988
|
|
|
990
989
|
// Determine Strategy from Saga Data (since V1 stores it in root)
|
|
@@ -1017,9 +1016,9 @@ export class SyncSagaCoordinator {
|
|
|
1017
1016
|
/**
|
|
1018
1017
|
* "remote" is sender in this case
|
|
1019
1018
|
*/
|
|
1020
|
-
const
|
|
1021
|
-
if (logalot) { console.log(`${lc}
|
|
1022
|
-
const remoteTjps = Object.keys(
|
|
1019
|
+
const remoteKnowledge = initData.knowledgeMap;
|
|
1020
|
+
if (logalot) { console.log(`${lc} remoteKnowledge: ${pretty(remoteKnowledge)} (I: 9f957862356dfeae183c200854e86e26)`); }
|
|
1021
|
+
const remoteTjps = Object.keys(remoteKnowledge);
|
|
1023
1022
|
if (logalot) { console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`); }
|
|
1024
1023
|
if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
|
|
1025
1024
|
|
|
@@ -1034,13 +1033,13 @@ export class SyncSagaCoordinator {
|
|
|
1034
1033
|
if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
|
|
1035
1034
|
if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
|
|
1036
1035
|
localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
|
|
1037
|
-
if (logalot) { console.log(`${lc} [TEST DEBUG]
|
|
1038
|
-
if (logalot) { console.log(`${lc}
|
|
1036
|
+
if (logalot) { console.log(`${lc} [TEST DEBUG] localKnowledge: ${JSON.stringify(localLatestAddrsMap)}`); }
|
|
1037
|
+
if (logalot) { console.log(`${lc} localKnowledge: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`); }
|
|
1039
1038
|
}
|
|
1040
1039
|
|
|
1041
1040
|
// 2. Gap Analysis
|
|
1042
1041
|
for (const tjp of remoteTjps) {
|
|
1043
|
-
const remoteAddr =
|
|
1042
|
+
const remoteAddr = remoteKnowledge[tjp];
|
|
1044
1043
|
const localAddr = localLatestAddrsMap[tjp];
|
|
1045
1044
|
|
|
1046
1045
|
if (!localAddr) {
|
|
@@ -1099,7 +1098,6 @@ export class SyncSagaCoordinator {
|
|
|
1099
1098
|
*/
|
|
1100
1099
|
let localIsInPast = false;
|
|
1101
1100
|
try {
|
|
1102
|
-
// we could
|
|
1103
1101
|
localIsInPast = await isPastFrame({
|
|
1104
1102
|
olderAddr: localAddr,
|
|
1105
1103
|
newerAddr: remoteAddr,
|
|
@@ -1135,27 +1133,23 @@ export class SyncSagaCoordinator {
|
|
|
1135
1133
|
reason: 'divergence',
|
|
1136
1134
|
terminal: true
|
|
1137
1135
|
});
|
|
1136
|
+
// todo: create error saga frame for when aborting or other terminal error
|
|
1138
1137
|
} else if (conflictStrategy === 'optimistic') {
|
|
1139
1138
|
// Optimistic: We want resolve this.
|
|
1140
1139
|
// We need to send our history to the Sender so they can Merge.
|
|
1141
1140
|
|
|
1142
1141
|
// Fetch Full History for Local Timeline
|
|
1143
|
-
// Note: We might optimize this to only send "recent" history if we had a KV?
|
|
1144
|
-
// But for now, get full past.
|
|
1145
|
-
// Optimization: localKV might not have full history.
|
|
1146
|
-
// We need to inspect the 'past' of the local tip.
|
|
1147
|
-
|
|
1148
|
-
// We need the ACTUAL object to get the past.
|
|
1149
|
-
// We have localAddr.
|
|
1150
1142
|
const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
|
|
1151
1143
|
if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
|
|
1152
|
-
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
1144
|
+
throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}). errorMsg: ${resLocalTip.errorMsg ?? '[unknown error (E: b3c3d823276300aa384ebdba4a8eb826)]'} (E: 83cb88a767e22bbda99c6788bec50526)`);
|
|
1153
1145
|
}
|
|
1154
1146
|
const localTip = resLocalTip.ibGibs[0];
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1147
|
+
const pastAddrs = await getHistoryAddrs({
|
|
1148
|
+
timeline: localTip,
|
|
1149
|
+
space: mySpace,
|
|
1150
|
+
metaspace,
|
|
1151
|
+
});
|
|
1152
|
+
const timelineAddrs = [...pastAddrs, localAddr];
|
|
1159
1153
|
conflicts.push({
|
|
1160
1154
|
tjpAddr: tjp,
|
|
1161
1155
|
localAddr,
|
|
@@ -1349,8 +1343,10 @@ export class SyncSagaCoordinator {
|
|
|
1349
1343
|
const terminalConflicts = conflicts.filter(c => c.terminal);
|
|
1350
1344
|
if (terminalConflicts.length > 0) {
|
|
1351
1345
|
if (logalot) { console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`); }
|
|
1352
|
-
// Terminal failure. Sender should probably Commit(Fail) or just
|
|
1353
|
-
// For now, throw to trigger abort.
|
|
1346
|
+
// Terminal failure. Sender should probably Commit(Fail) or just
|
|
1347
|
+
// Abort. For now, throw to trigger abort. the incoming ack
|
|
1348
|
+
// frame has already been persisted in local durable space by
|
|
1349
|
+
// now.
|
|
1354
1350
|
throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
|
|
1355
1351
|
}
|
|
1356
1352
|
|
|
@@ -1583,7 +1579,6 @@ export class SyncSagaCoordinator {
|
|
|
1583
1579
|
private async handleDeltaFrame({
|
|
1584
1580
|
sagaContext,
|
|
1585
1581
|
sagaIbGib,
|
|
1586
|
-
srcGraph,
|
|
1587
1582
|
mySpace,
|
|
1588
1583
|
myTempSpace,
|
|
1589
1584
|
metaspace,
|
|
@@ -1591,7 +1586,6 @@ export class SyncSagaCoordinator {
|
|
|
1591
1586
|
}: {
|
|
1592
1587
|
sagaContext: SyncSagaContextIbGib_V1,
|
|
1593
1588
|
sagaIbGib: SyncIbGib_V1,
|
|
1594
|
-
srcGraph: { [addr: string]: IbGib_V1 },
|
|
1595
1589
|
mySpace: IbGibSpaceAny,
|
|
1596
1590
|
myTempSpace: IbGibSpaceAny,
|
|
1597
1591
|
metaspace: MetaspaceService,
|
|
@@ -40,7 +40,7 @@ export interface SyncSagaMessageIbGib_V1 extends IbGib_V1<SyncSagaMessageData_V1
|
|
|
40
40
|
/**
|
|
41
41
|
* The "Hello" of the sync protocol.
|
|
42
42
|
*
|
|
43
|
-
* Exchanges knowledge
|
|
43
|
+
* Exchanges knowledge maps (what I have) and identity (who I am).
|
|
44
44
|
*/
|
|
45
45
|
export interface SyncSagaMessageInitData_V1 extends SyncSagaMessageData_V1 {
|
|
46
46
|
stage: typeof SyncStage.init;
|
|
@@ -51,7 +51,7 @@ export interface SyncSagaMessageInitData_V1 extends SyncSagaMessageData_V1 {
|
|
|
51
51
|
*
|
|
52
52
|
* IOW, this is a map of the starting and end points of an ibGib's timeline.
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
knowledgeMap: { [tjp: string]: IbGibAddr };
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* The Keystone Identity of the sender.
|
|
@@ -149,7 +149,7 @@ export interface SyncSagaMessageAckData_V1 extends SyncSagaMessageData_V1 {
|
|
|
149
149
|
/**
|
|
150
150
|
* Infos regarding ibgibs that the receiver (generator of this ack)
|
|
151
151
|
* knows/believes that the sender does NOT have (after looking at knowledge
|
|
152
|
-
*
|
|
152
|
+
* map of sender).
|
|
153
153
|
*
|
|
154
154
|
* This includes addrs that will correspond to the context.payloadAddrsDomain
|
|
155
155
|
*
|