@twin.org/synchronised-storage-service 0.0.1-next.4 → 0.0.1-next.5
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/cjs/index.cjs +369 -147
- package/dist/esm/index.mjs +370 -148
- package/dist/types/entities/syncSnapshotEntry.d.ts +11 -3
- package/dist/types/helpers/blobStorageHelper.d.ts +7 -1
- package/dist/types/helpers/changeSetHelper.d.ts +8 -1
- package/dist/types/helpers/localSyncStateHelper.d.ts +4 -3
- package/dist/types/helpers/remoteSyncStateHelper.d.ts +3 -2
- package/dist/types/models/ISyncSnapshot.d.ts +4 -0
- package/dist/types/models/ISyncState.d.ts +4 -0
- package/dist/types/models/ISynchronisedStorageServiceConfig.d.ts +5 -0
- package/docs/architecture.md +4 -1
- package/docs/changelog.md +14 -0
- package/docs/open-api/spec.json +2 -0
- package/docs/reference/classes/SyncSnapshotEntry.md +21 -5
- package/docs/reference/interfaces/ISyncSnapshot.md +8 -0
- package/docs/reference/interfaces/ISyncState.md +8 -0
- package/docs/reference/interfaces/ISynchronisedStorageServiceConfig.md +14 -0
- package/locales/en.json +16 -9
- package/package.json +2 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -23,6 +23,10 @@ exports.SyncSnapshotEntry = class SyncSnapshotEntry {
|
|
|
23
23
|
* The id for the snapshot.
|
|
24
24
|
*/
|
|
25
25
|
id;
|
|
26
|
+
/**
|
|
27
|
+
* The version for the snapshot.
|
|
28
|
+
*/
|
|
29
|
+
version;
|
|
26
30
|
/**
|
|
27
31
|
* The storage key for the snapshot i.e. which entity is being synchronized.
|
|
28
32
|
*/
|
|
@@ -36,9 +40,13 @@ exports.SyncSnapshotEntry = class SyncSnapshotEntry {
|
|
|
36
40
|
*/
|
|
37
41
|
dateModified;
|
|
38
42
|
/**
|
|
39
|
-
* The flag to determine if this is the
|
|
43
|
+
* The flag to determine if this is the snapshot is the local one containing changes for this node.
|
|
44
|
+
*/
|
|
45
|
+
isLocal;
|
|
46
|
+
/**
|
|
47
|
+
* The flag to determine if this is a consolidated snapshot.
|
|
40
48
|
*/
|
|
41
|
-
|
|
49
|
+
isConsolidated;
|
|
42
50
|
/**
|
|
43
51
|
* The ids of the storage for the change sets in the snapshot, if this is not a local snapshot.
|
|
44
52
|
*/
|
|
@@ -52,6 +60,10 @@ __decorate([
|
|
|
52
60
|
entity.property({ type: "string", isPrimary: true }),
|
|
53
61
|
__metadata("design:type", String)
|
|
54
62
|
], exports.SyncSnapshotEntry.prototype, "id", void 0);
|
|
63
|
+
__decorate([
|
|
64
|
+
entity.property({ type: "string" }),
|
|
65
|
+
__metadata("design:type", String)
|
|
66
|
+
], exports.SyncSnapshotEntry.prototype, "version", void 0);
|
|
55
67
|
__decorate([
|
|
56
68
|
entity.property({ type: "string", isSecondary: true }),
|
|
57
69
|
__metadata("design:type", String)
|
|
@@ -61,13 +73,17 @@ __decorate([
|
|
|
61
73
|
__metadata("design:type", String)
|
|
62
74
|
], exports.SyncSnapshotEntry.prototype, "dateCreated", void 0);
|
|
63
75
|
__decorate([
|
|
64
|
-
entity.property({ type: "string"
|
|
76
|
+
entity.property({ type: "string" }),
|
|
65
77
|
__metadata("design:type", String)
|
|
66
78
|
], exports.SyncSnapshotEntry.prototype, "dateModified", void 0);
|
|
67
79
|
__decorate([
|
|
68
|
-
entity.property({ type: "boolean"
|
|
80
|
+
entity.property({ type: "boolean" }),
|
|
81
|
+
__metadata("design:type", Boolean)
|
|
82
|
+
], exports.SyncSnapshotEntry.prototype, "isLocal", void 0);
|
|
83
|
+
__decorate([
|
|
84
|
+
entity.property({ type: "boolean" }),
|
|
69
85
|
__metadata("design:type", Boolean)
|
|
70
|
-
], exports.SyncSnapshotEntry.prototype, "
|
|
86
|
+
], exports.SyncSnapshotEntry.prototype, "isConsolidated", void 0);
|
|
71
87
|
__decorate([
|
|
72
88
|
entity.property({ type: "array", itemType: "string", optional: true }),
|
|
73
89
|
__metadata("design:type", Array)
|
|
@@ -116,6 +132,7 @@ function generateRestRoutesSynchronisedStorage(baseRouteName, componentName) {
|
|
|
116
132
|
body: {
|
|
117
133
|
id: "0909090909090909090909090909090909090909090909090909090909090909",
|
|
118
134
|
dateCreated: "2025-05-29T01:00:00.000Z",
|
|
135
|
+
dateModified: "2025-05-29T01:00:00.000Z",
|
|
119
136
|
nodeIdentity: "did:entity-storage:0xd2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2",
|
|
120
137
|
changes: [
|
|
121
138
|
{
|
|
@@ -316,7 +333,7 @@ class BlobStorageHelper {
|
|
|
316
333
|
* @param blobId The id of the blob to apply.
|
|
317
334
|
* @returns The blob.
|
|
318
335
|
*/
|
|
319
|
-
async
|
|
336
|
+
async loadBlob(blobId) {
|
|
320
337
|
await this._logging?.log({
|
|
321
338
|
level: "info",
|
|
322
339
|
source: this.CLASS_NAME,
|
|
@@ -409,6 +426,51 @@ class BlobStorageHelper {
|
|
|
409
426
|
throw error;
|
|
410
427
|
}
|
|
411
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Remove a blob from storage.
|
|
431
|
+
* @param blobId The id of the blob to remove.
|
|
432
|
+
* @returns Nothing.
|
|
433
|
+
*/
|
|
434
|
+
async removeBlob(blobId) {
|
|
435
|
+
await this._logging?.log({
|
|
436
|
+
level: "info",
|
|
437
|
+
source: this.CLASS_NAME,
|
|
438
|
+
message: "removeBlob",
|
|
439
|
+
data: {
|
|
440
|
+
blobId
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
try {
|
|
444
|
+
await this._blobStorageConnector.remove(blobId);
|
|
445
|
+
await this._logging?.log({
|
|
446
|
+
level: "info",
|
|
447
|
+
source: this.CLASS_NAME,
|
|
448
|
+
message: "removedBlob",
|
|
449
|
+
data: {
|
|
450
|
+
blobId
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
await this._logging?.log({
|
|
456
|
+
level: "error",
|
|
457
|
+
source: this.CLASS_NAME,
|
|
458
|
+
message: "removeBlobFailed",
|
|
459
|
+
data: {
|
|
460
|
+
blobId
|
|
461
|
+
},
|
|
462
|
+
error: core.BaseError.fromError(error)
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
await this._logging?.log({
|
|
466
|
+
level: "info",
|
|
467
|
+
source: this.CLASS_NAME,
|
|
468
|
+
message: "removeBlobEmpty",
|
|
469
|
+
data: {
|
|
470
|
+
blobId
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
412
474
|
}
|
|
413
475
|
|
|
414
476
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -488,7 +550,7 @@ class ChangeSetHelper {
|
|
|
488
550
|
}
|
|
489
551
|
});
|
|
490
552
|
try {
|
|
491
|
-
const syncChangeSet = await this._blobStorageHelper.
|
|
553
|
+
const syncChangeSet = await this._blobStorageHelper.loadBlob(changeSetStorageId);
|
|
492
554
|
if (core.Is.object(syncChangeSet)) {
|
|
493
555
|
const verified = await this.verifyChangesetProof(syncChangeSet);
|
|
494
556
|
return verified ? syncChangeSet : undefined;
|
|
@@ -521,7 +583,9 @@ class ChangeSetHelper {
|
|
|
521
583
|
*/
|
|
522
584
|
async getAndApplyChangeset(changeSetStorageId) {
|
|
523
585
|
const syncChangeset = await this.getAndVerifyChangeset(changeSetStorageId);
|
|
524
|
-
|
|
586
|
+
// Only apply changesets from other nodes, we don't want to overwrite
|
|
587
|
+
// any changes we have made to local entity storage
|
|
588
|
+
if (!core.Is.empty(syncChangeset) && syncChangeset.nodeIdentity !== this._nodeIdentity) {
|
|
525
589
|
await this.applyChangeset(syncChangeset);
|
|
526
590
|
}
|
|
527
591
|
return syncChangeset;
|
|
@@ -565,7 +629,8 @@ class ChangeSetHelper {
|
|
|
565
629
|
if (!core.Is.empty(change.id)) {
|
|
566
630
|
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemRemove, {
|
|
567
631
|
storageKey: syncChangeset.storageKey,
|
|
568
|
-
id: change.id
|
|
632
|
+
id: change.id,
|
|
633
|
+
nodeIdentity: syncChangeset.nodeIdentity
|
|
569
634
|
});
|
|
570
635
|
}
|
|
571
636
|
break;
|
|
@@ -707,8 +772,36 @@ class ChangeSetHelper {
|
|
|
707
772
|
}
|
|
708
773
|
}
|
|
709
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Reset the storage for a given storage key.
|
|
777
|
+
* @param storageKey The key of the storage to reset.
|
|
778
|
+
* @param resetMode The reset mode, this will use the nodeIdentity in the entities to determine which are local/remote.
|
|
779
|
+
* @returns Nothing.
|
|
780
|
+
*/
|
|
781
|
+
async reset(storageKey, resetMode) {
|
|
782
|
+
// If we are applying a consolidation we need to reset the local db
|
|
783
|
+
// but keep any entries from the local node, as they might have been updated
|
|
784
|
+
await this._logging?.log({
|
|
785
|
+
level: "info",
|
|
786
|
+
source: this.CLASS_NAME,
|
|
787
|
+
message: "storageReset",
|
|
788
|
+
data: {
|
|
789
|
+
storageKey
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.Reset, {
|
|
793
|
+
storageKey,
|
|
794
|
+
resetMode
|
|
795
|
+
});
|
|
796
|
+
}
|
|
710
797
|
}
|
|
711
798
|
|
|
799
|
+
// Copyright 2024 IOTA Stiftung.
|
|
800
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
801
|
+
const SYNC_STATE_VERSION = "1";
|
|
802
|
+
const SYNC_POINTER_STORE_VERSION = "1";
|
|
803
|
+
const SYNC_SNAPSHOT_VERSION = "1";
|
|
804
|
+
|
|
712
805
|
// Copyright 2024 IOTA Stiftung.
|
|
713
806
|
// SPDX-License-Identifier: Apache-2.0.
|
|
714
807
|
/**
|
|
@@ -763,31 +856,35 @@ class LocalSyncStateHelper {
|
|
|
763
856
|
id
|
|
764
857
|
}
|
|
765
858
|
});
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
localChangeSnapshot.changes.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
859
|
+
const localChangeSnapshots = await this.getSnapshots(storageKey, true);
|
|
860
|
+
if (localChangeSnapshots.length > 0) {
|
|
861
|
+
const localChangeSnapshot = localChangeSnapshots[0];
|
|
862
|
+
localChangeSnapshot.changes ??= [];
|
|
863
|
+
// If we already have a change for this id we are
|
|
864
|
+
// about to supersede it, we remove the previous change
|
|
865
|
+
// to avoid having multiple changes for the same id
|
|
866
|
+
const previousChangeIndex = localChangeSnapshot.changes.findIndex(change => change.id === id);
|
|
867
|
+
if (previousChangeIndex !== -1) {
|
|
868
|
+
localChangeSnapshot.changes.splice(previousChangeIndex, 1);
|
|
869
|
+
}
|
|
870
|
+
if (localChangeSnapshot.changes.length > 0) {
|
|
871
|
+
localChangeSnapshot.dateModified = new Date(Date.now()).toISOString();
|
|
872
|
+
}
|
|
873
|
+
localChangeSnapshot.changes.push({ operation, id });
|
|
874
|
+
await this.setLocalChangeSnapshot(localChangeSnapshot);
|
|
777
875
|
}
|
|
778
|
-
localChangeSnapshot.changes.push({ operation, id });
|
|
779
|
-
await this.setLocalChangeSnapshot(localChangeSnapshot);
|
|
780
876
|
}
|
|
781
877
|
/**
|
|
782
|
-
* Get the
|
|
878
|
+
* Get the snapshot which contains just the changes for this node.
|
|
783
879
|
* @param storageKey The storage key of the snapshot to get.
|
|
880
|
+
* @param isLocal Whether to get the local snapshot or not.
|
|
784
881
|
* @returns The local snapshot entry.
|
|
785
882
|
*/
|
|
786
|
-
async
|
|
883
|
+
async getSnapshots(storageKey, isLocal) {
|
|
787
884
|
await this._logging?.log({
|
|
788
885
|
level: "info",
|
|
789
886
|
source: this.CLASS_NAME,
|
|
790
|
-
message: "
|
|
887
|
+
message: "getSnapshots",
|
|
791
888
|
data: {
|
|
792
889
|
storageKey
|
|
793
890
|
}
|
|
@@ -795,8 +892,8 @@ class LocalSyncStateHelper {
|
|
|
795
892
|
const queryResult = await this._snapshotEntryEntityStorage.query({
|
|
796
893
|
conditions: [
|
|
797
894
|
{
|
|
798
|
-
property: "
|
|
799
|
-
value:
|
|
895
|
+
property: "isLocal",
|
|
896
|
+
value: isLocal,
|
|
800
897
|
comparison: entity.ComparisonOperator.Equals
|
|
801
898
|
},
|
|
802
899
|
{
|
|
@@ -810,28 +907,34 @@ class LocalSyncStateHelper {
|
|
|
810
907
|
await this._logging?.log({
|
|
811
908
|
level: "info",
|
|
812
909
|
source: this.CLASS_NAME,
|
|
813
|
-
message: "
|
|
910
|
+
message: "getSnapshotsExists",
|
|
814
911
|
data: {
|
|
815
912
|
storageKey
|
|
816
913
|
}
|
|
817
914
|
});
|
|
818
|
-
return queryResult.entities
|
|
915
|
+
return queryResult.entities;
|
|
819
916
|
}
|
|
820
917
|
await this._logging?.log({
|
|
821
918
|
level: "info",
|
|
822
919
|
source: this.CLASS_NAME,
|
|
823
|
-
message: "
|
|
920
|
+
message: "getSnapshotsDoesNotExist",
|
|
824
921
|
data: {
|
|
825
922
|
storageKey
|
|
826
923
|
}
|
|
827
924
|
});
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
925
|
+
const now = new Date(Date.now()).toISOString();
|
|
926
|
+
return [
|
|
927
|
+
{
|
|
928
|
+
version: SYNC_SNAPSHOT_VERSION,
|
|
929
|
+
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
930
|
+
storageKey,
|
|
931
|
+
dateCreated: now,
|
|
932
|
+
dateModified: now,
|
|
933
|
+
changeSetStorageIds: [],
|
|
934
|
+
isLocal,
|
|
935
|
+
isConsolidated: false
|
|
936
|
+
}
|
|
937
|
+
];
|
|
835
938
|
}
|
|
836
939
|
/**
|
|
837
940
|
* Set the current local snapshot with changes for this node.
|
|
@@ -880,46 +983,115 @@ class LocalSyncStateHelper {
|
|
|
880
983
|
snapshotCount: syncState.snapshots.length
|
|
881
984
|
}
|
|
882
985
|
});
|
|
986
|
+
// Get all the existing snapshots that we have processed previously
|
|
987
|
+
const existingRemoteSnapshots = await this.getSnapshots(storageKey, false);
|
|
883
988
|
// Sort from newest to oldest
|
|
884
989
|
const sortedSnapshots = syncState.snapshots.sort((a, b) => new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime());
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
990
|
+
// If we have no existing snapshots we can't have yet synced
|
|
991
|
+
// in this case we need to find the most recent consolidation
|
|
992
|
+
// and use that to build a complete DB table
|
|
993
|
+
if (existingRemoteSnapshots.length === 0) {
|
|
888
994
|
await this._logging?.log({
|
|
889
995
|
level: "info",
|
|
890
996
|
source: this.CLASS_NAME,
|
|
891
|
-
message: "
|
|
997
|
+
message: "applySnapshotNoExisting",
|
|
892
998
|
data: {
|
|
893
|
-
|
|
894
|
-
dateCreated: new Date(snapshot.dateCreated).toISOString()
|
|
999
|
+
storageKey
|
|
895
1000
|
}
|
|
896
1001
|
});
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
modifiedSnapshots.push({
|
|
909
|
-
localSnapshot,
|
|
910
|
-
remoteSnapshot: remoteSnapshotWithContext
|
|
1002
|
+
const firstConsolidated = sortedSnapshots.find(snapshot => snapshot.isConsolidated);
|
|
1003
|
+
if (firstConsolidated) {
|
|
1004
|
+
// We found a consolidated snapshot, we can use it
|
|
1005
|
+
await this._logging?.log({
|
|
1006
|
+
level: "info",
|
|
1007
|
+
source: this.CLASS_NAME,
|
|
1008
|
+
message: "applySnapshotFoundConsolidated",
|
|
1009
|
+
data: {
|
|
1010
|
+
storageKey,
|
|
1011
|
+
snapshotId: firstConsolidated.id
|
|
1012
|
+
}
|
|
911
1013
|
});
|
|
1014
|
+
// We need to reset the entity storage and remove all the remote items
|
|
1015
|
+
// so that we use just the ones from the consolidation
|
|
1016
|
+
await this._changeSetHelper.reset(storageKey, synchronisedStorageModels.SyncNodeIdentityMode.Remote);
|
|
1017
|
+
await this.processNewSnapshots([
|
|
1018
|
+
{
|
|
1019
|
+
...firstConsolidated,
|
|
1020
|
+
storageKey,
|
|
1021
|
+
isLocal: false
|
|
1022
|
+
}
|
|
1023
|
+
]);
|
|
912
1024
|
}
|
|
913
1025
|
else {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1026
|
+
await this._logging?.log({
|
|
1027
|
+
level: "info",
|
|
1028
|
+
source: this.CLASS_NAME,
|
|
1029
|
+
message: "applySnapshotNoConsolidated",
|
|
1030
|
+
data: {
|
|
1031
|
+
storageKey
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
// Create a lookup map for the existing snapshots
|
|
1038
|
+
const existingSnapshots = {};
|
|
1039
|
+
for (const snapshot of existingRemoteSnapshots) {
|
|
1040
|
+
existingSnapshots[snapshot.id] = snapshot;
|
|
1041
|
+
}
|
|
1042
|
+
const newSnapshots = [];
|
|
1043
|
+
const modifiedSnapshots = [];
|
|
1044
|
+
const referencedExistingSnapshots = Object.keys(existingSnapshots);
|
|
1045
|
+
for (const snapshot of sortedSnapshots) {
|
|
1046
|
+
await this._logging?.log({
|
|
1047
|
+
level: "info",
|
|
1048
|
+
source: this.CLASS_NAME,
|
|
1049
|
+
message: "applySnapshot",
|
|
1050
|
+
data: {
|
|
1051
|
+
snapshotId: snapshot.id,
|
|
1052
|
+
dateCreated: new Date(snapshot.dateCreated).toISOString()
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
// See if we have the local snapshot
|
|
1056
|
+
const currentSnapshot = existingSnapshots[snapshot.id];
|
|
1057
|
+
// As we are referencing an existing snapshot, we need to remove it from the list
|
|
1058
|
+
// to allow us to cleanup any unreferenced snapshots later
|
|
1059
|
+
const idx = referencedExistingSnapshots.indexOf(snapshot.id);
|
|
1060
|
+
if (idx !== -1) {
|
|
1061
|
+
referencedExistingSnapshots.splice(idx, 1);
|
|
1062
|
+
}
|
|
1063
|
+
const updatedSnapshot = {
|
|
1064
|
+
...snapshot,
|
|
1065
|
+
storageKey,
|
|
1066
|
+
isLocal: false
|
|
1067
|
+
};
|
|
1068
|
+
if (core.Is.empty(currentSnapshot)) {
|
|
1069
|
+
// We don't have the snapshot locally, so we need to process it
|
|
1070
|
+
newSnapshots.push(updatedSnapshot);
|
|
1071
|
+
}
|
|
1072
|
+
else if (currentSnapshot.dateModified !== snapshot.dateModified) {
|
|
1073
|
+
// If the local snapshot has a different dateModified, we need to update it
|
|
1074
|
+
modifiedSnapshots.push({
|
|
1075
|
+
currentSnapshot,
|
|
1076
|
+
updatedSnapshot
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
// we sorted the snapshots from newest to oldest, so if we found a local snapshot
|
|
1081
|
+
// with the same dateModified as the remote snapshot, we can stop processing further
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
// We reverse the order of the snapshots to process them from oldest to newest
|
|
1086
|
+
// because we want to apply the changes in the order they were created
|
|
1087
|
+
await this.processModifiedSnapshots(modifiedSnapshots.reverse());
|
|
1088
|
+
await this.processNewSnapshots(newSnapshots.reverse());
|
|
1089
|
+
// Any ids remaining in this list are no longer referenced in the global state
|
|
1090
|
+
// so we should remove them from the local storage as they will never be updated again
|
|
1091
|
+
for (const referencedSnapshotId of referencedExistingSnapshots) {
|
|
1092
|
+
await this._snapshotEntryEntityStorage.remove(referencedSnapshotId);
|
|
917
1093
|
}
|
|
918
1094
|
}
|
|
919
|
-
// We reverse the order of the snapshots to process them from oldest to newest
|
|
920
|
-
// because we want to apply the changes in the order they were created
|
|
921
|
-
await this.processModifiedSnapshots(modifiedSnapshots.reverse());
|
|
922
|
-
await this.processNewSnapshots(newSnapshots.reverse());
|
|
923
1095
|
}
|
|
924
1096
|
/**
|
|
925
1097
|
* Process the modified snapshots and store them in the local storage.
|
|
@@ -934,15 +1106,15 @@ class LocalSyncStateHelper {
|
|
|
934
1106
|
source: this.CLASS_NAME,
|
|
935
1107
|
message: "processModifiedSnapshot",
|
|
936
1108
|
data: {
|
|
937
|
-
snapshotId: modifiedSnapshot.
|
|
938
|
-
localModified: new Date(modifiedSnapshot.
|
|
939
|
-
modifiedSnapshot.
|
|
940
|
-
remoteModified: new Date(modifiedSnapshot.
|
|
941
|
-
modifiedSnapshot.
|
|
1109
|
+
snapshotId: modifiedSnapshot.updatedSnapshot.id,
|
|
1110
|
+
localModified: new Date(modifiedSnapshot.currentSnapshot.dateModified ??
|
|
1111
|
+
modifiedSnapshot.currentSnapshot.dateCreated).toISOString(),
|
|
1112
|
+
remoteModified: new Date(modifiedSnapshot.updatedSnapshot.dateModified ??
|
|
1113
|
+
modifiedSnapshot.updatedSnapshot.dateCreated).toISOString()
|
|
942
1114
|
}
|
|
943
1115
|
});
|
|
944
|
-
const remoteChangeSetStorageIds = modifiedSnapshot.
|
|
945
|
-
const localChangeSetStorageIds = modifiedSnapshot.
|
|
1116
|
+
const remoteChangeSetStorageIds = modifiedSnapshot.updatedSnapshot.changeSetStorageIds;
|
|
1117
|
+
const localChangeSetStorageIds = modifiedSnapshot.currentSnapshot.changeSetStorageIds ?? [];
|
|
946
1118
|
if (core.Is.arrayValue(remoteChangeSetStorageIds)) {
|
|
947
1119
|
for (const storageId of remoteChangeSetStorageIds) {
|
|
948
1120
|
// Check if the local snapshot does not have the storageId
|
|
@@ -951,7 +1123,7 @@ class LocalSyncStateHelper {
|
|
|
951
1123
|
}
|
|
952
1124
|
}
|
|
953
1125
|
}
|
|
954
|
-
await this._snapshotEntryEntityStorage.set(modifiedSnapshot.
|
|
1126
|
+
await this._snapshotEntryEntityStorage.set(modifiedSnapshot.updatedSnapshot);
|
|
955
1127
|
}
|
|
956
1128
|
}
|
|
957
1129
|
/**
|
|
@@ -968,7 +1140,7 @@ class LocalSyncStateHelper {
|
|
|
968
1140
|
message: "processNewSnapshot",
|
|
969
1141
|
data: {
|
|
970
1142
|
snapshotId: newSnapshot.id,
|
|
971
|
-
|
|
1143
|
+
dateCreated: newSnapshot.dateCreated
|
|
972
1144
|
}
|
|
973
1145
|
});
|
|
974
1146
|
const newSnapshotChangeSetStorageIds = newSnapshot.changeSetStorageIds ?? [];
|
|
@@ -982,12 +1154,6 @@ class LocalSyncStateHelper {
|
|
|
982
1154
|
}
|
|
983
1155
|
}
|
|
984
1156
|
|
|
985
|
-
// Copyright 2024 IOTA Stiftung.
|
|
986
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
987
|
-
const SYNC_STATE_VERSION = "1";
|
|
988
|
-
const SYNC_POINTER_STORE_VERSION = "1";
|
|
989
|
-
const SYNC_SNAPSHOT_VERSION = "1";
|
|
990
|
-
|
|
991
1157
|
// Copyright 2024 IOTA Stiftung.
|
|
992
1158
|
// SPDX-License-Identifier: Apache-2.0.
|
|
993
1159
|
/**
|
|
@@ -1048,6 +1214,11 @@ class RemoteSyncStateHelper {
|
|
|
1048
1214
|
* @internal
|
|
1049
1215
|
*/
|
|
1050
1216
|
_isTrustedNode;
|
|
1217
|
+
/**
|
|
1218
|
+
* Maximum number of consolidations to keep in storage.
|
|
1219
|
+
* @internal
|
|
1220
|
+
*/
|
|
1221
|
+
_maxConsolidations;
|
|
1051
1222
|
/**
|
|
1052
1223
|
* Create a new instance of DecentralisedEntityStorageConnector.
|
|
1053
1224
|
* @param logging The logging connector to use for logging.
|
|
@@ -1056,14 +1227,16 @@ class RemoteSyncStateHelper {
|
|
|
1056
1227
|
* @param blobStorageHelper The blob storage helper to use for remote sync states.
|
|
1057
1228
|
* @param changeSetHelper The change set helper to use for managing changesets.
|
|
1058
1229
|
* @param isTrustedNode Whether the node is trusted or not.
|
|
1230
|
+
* @param maxConsolidations The maximum number of consolidations to keep in storage.
|
|
1059
1231
|
*/
|
|
1060
|
-
constructor(logging, eventBusComponent, verifiableSyncPointerStorageConnector, blobStorageHelper, changeSetHelper, isTrustedNode) {
|
|
1232
|
+
constructor(logging, eventBusComponent, verifiableSyncPointerStorageConnector, blobStorageHelper, changeSetHelper, isTrustedNode, maxConsolidations) {
|
|
1061
1233
|
this._logging = logging;
|
|
1062
1234
|
this._eventBusComponent = eventBusComponent;
|
|
1063
1235
|
this._verifiableSyncPointerStorageConnector = verifiableSyncPointerStorageConnector;
|
|
1064
1236
|
this._changeSetHelper = changeSetHelper;
|
|
1065
1237
|
this._blobStorageHelper = blobStorageHelper;
|
|
1066
1238
|
this._isTrustedNode = isTrustedNode;
|
|
1239
|
+
this._maxConsolidations = maxConsolidations;
|
|
1067
1240
|
this._batchResponseStorageIds = {};
|
|
1068
1241
|
this._populateFullChanges = {};
|
|
1069
1242
|
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.BatchResponse, async (response) => {
|
|
@@ -1165,9 +1338,11 @@ class RemoteSyncStateHelper {
|
|
|
1165
1338
|
core.ObjectHelper.propertyDelete(change.entity, "nodeIdentity");
|
|
1166
1339
|
}
|
|
1167
1340
|
}
|
|
1341
|
+
const now = new Date(Date.now()).toISOString();
|
|
1168
1342
|
const syncChangeSet = {
|
|
1169
1343
|
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
1170
|
-
dateCreated:
|
|
1344
|
+
dateCreated: now,
|
|
1345
|
+
dateModified: now,
|
|
1171
1346
|
storageKey,
|
|
1172
1347
|
changes,
|
|
1173
1348
|
nodeIdentity: this._nodeIdentity
|
|
@@ -1219,23 +1394,26 @@ class RemoteSyncStateHelper {
|
|
|
1219
1394
|
const syncPointerStore = await this.getVerifiableSyncPointerStore();
|
|
1220
1395
|
let syncState;
|
|
1221
1396
|
if (!core.Is.empty(syncPointerStore.syncPointers[storageKey])) {
|
|
1222
|
-
syncState = await this.
|
|
1397
|
+
syncState = await this.getSyncState(syncPointerStore.syncPointers[storageKey]);
|
|
1223
1398
|
}
|
|
1224
1399
|
// No current sync state, so we create a new one
|
|
1225
1400
|
if (core.Is.empty(syncState)) {
|
|
1226
|
-
syncState = { version: SYNC_STATE_VERSION, snapshots: [] };
|
|
1401
|
+
syncState = { version: SYNC_STATE_VERSION, storageKey, snapshots: [] };
|
|
1227
1402
|
}
|
|
1228
1403
|
// Sort the snapshots so the newest snapshot is last in the array
|
|
1229
1404
|
const sortedSnapshots = syncState.snapshots.sort((a, b) => a.dateCreated.localeCompare(b.dateCreated));
|
|
1230
1405
|
// Get the current snapshot, if it does not exist we create a new one
|
|
1231
1406
|
let currentSnapshot = sortedSnapshots[sortedSnapshots.length - 1];
|
|
1232
1407
|
const now = new Date(Date.now()).toISOString();
|
|
1233
|
-
|
|
1408
|
+
// If there is no snapshot or the current one is a consolidation
|
|
1409
|
+
// we start a new snapshot
|
|
1410
|
+
if (core.Is.empty(currentSnapshot) || currentSnapshot.isConsolidated) {
|
|
1234
1411
|
currentSnapshot = {
|
|
1235
1412
|
version: SYNC_SNAPSHOT_VERSION,
|
|
1236
1413
|
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
1237
1414
|
dateCreated: now,
|
|
1238
1415
|
dateModified: now,
|
|
1416
|
+
isConsolidated: false,
|
|
1239
1417
|
changeSetStorageIds: []
|
|
1240
1418
|
};
|
|
1241
1419
|
syncState.snapshots.push(currentSnapshot);
|
|
@@ -1264,7 +1442,7 @@ class RemoteSyncStateHelper {
|
|
|
1264
1442
|
message: "consolidationStarting"
|
|
1265
1443
|
});
|
|
1266
1444
|
// Perform a batch request to start the consolidation
|
|
1267
|
-
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, { storageKey, batchSize });
|
|
1445
|
+
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, { storageKey, batchSize, requestMode: synchronisedStorageModels.SyncNodeIdentityMode.All });
|
|
1268
1446
|
}
|
|
1269
1447
|
/**
|
|
1270
1448
|
* Get the sync pointer store.
|
|
@@ -1343,11 +1521,32 @@ class RemoteSyncStateHelper {
|
|
|
1343
1521
|
await this._logging?.log({
|
|
1344
1522
|
level: "info",
|
|
1345
1523
|
source: this.CLASS_NAME,
|
|
1346
|
-
message: "
|
|
1524
|
+
message: "syncStateStoring",
|
|
1347
1525
|
data: {
|
|
1348
1526
|
snapshotCount: syncState.snapshots.length
|
|
1349
1527
|
}
|
|
1350
1528
|
});
|
|
1529
|
+
// Limits the number of consolidations in the list so that we can shrink decentralised
|
|
1530
|
+
// storage requirements, sort from newest to oldest so that we can easily find the
|
|
1531
|
+
// oldest snapshots to remove.
|
|
1532
|
+
const snapshots = syncState.snapshots.sort((a, b) => new Date(a.dateCreated).getTime() - new Date(b.dateCreated).getTime());
|
|
1533
|
+
// Find all the consolidation indexes
|
|
1534
|
+
const consolidationIndexes = [];
|
|
1535
|
+
for (let i = 0; i < snapshots.length; i++) {
|
|
1536
|
+
const snapshot = snapshots[i];
|
|
1537
|
+
if (snapshot.isConsolidated) {
|
|
1538
|
+
consolidationIndexes.push(i);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
if (consolidationIndexes.length > this._maxConsolidations) {
|
|
1542
|
+
// Once we have reached the max for consolidations we need to remove
|
|
1543
|
+
// all the snapshots, including non consolidated ones, beyond this point
|
|
1544
|
+
const toRemove = snapshots.slice(consolidationIndexes[this._maxConsolidations - 1] + 1);
|
|
1545
|
+
syncState.snapshots = snapshots.slice(0, consolidationIndexes[this._maxConsolidations - 1] + 1);
|
|
1546
|
+
for (const snapshot of toRemove) {
|
|
1547
|
+
await this._blobStorageHelper.removeBlob(snapshot.id);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1351
1550
|
return this._blobStorageHelper.saveBlob(syncState);
|
|
1352
1551
|
}
|
|
1353
1552
|
/**
|
|
@@ -1355,22 +1554,22 @@ class RemoteSyncStateHelper {
|
|
|
1355
1554
|
* @param syncPointerId The id of the sync pointer to retrieve the state for.
|
|
1356
1555
|
* @returns The remote sync state.
|
|
1357
1556
|
*/
|
|
1358
|
-
async
|
|
1557
|
+
async getSyncState(syncPointerId) {
|
|
1359
1558
|
try {
|
|
1360
1559
|
await this._logging?.log({
|
|
1361
1560
|
level: "info",
|
|
1362
1561
|
source: this.CLASS_NAME,
|
|
1363
|
-
message: "
|
|
1562
|
+
message: "syncStateRetrieving",
|
|
1364
1563
|
data: {
|
|
1365
1564
|
syncPointerId
|
|
1366
1565
|
}
|
|
1367
1566
|
});
|
|
1368
|
-
const syncState = await this._blobStorageHelper.
|
|
1567
|
+
const syncState = await this._blobStorageHelper.loadBlob(syncPointerId);
|
|
1369
1568
|
if (core.Is.object(syncState)) {
|
|
1370
1569
|
await this._logging?.log({
|
|
1371
1570
|
level: "info",
|
|
1372
1571
|
source: this.CLASS_NAME,
|
|
1373
|
-
message: "
|
|
1572
|
+
message: "syncStateRetrieved",
|
|
1374
1573
|
data: {
|
|
1375
1574
|
syncPointerId,
|
|
1376
1575
|
snapshotCount: syncState.snapshots.length
|
|
@@ -1393,7 +1592,7 @@ class RemoteSyncStateHelper {
|
|
|
1393
1592
|
await this._logging?.log({
|
|
1394
1593
|
level: "info",
|
|
1395
1594
|
source: this.CLASS_NAME,
|
|
1396
|
-
message: "
|
|
1595
|
+
message: "syncStateNotFound",
|
|
1397
1596
|
data: {
|
|
1398
1597
|
syncPointerId
|
|
1399
1598
|
}
|
|
@@ -1432,19 +1631,24 @@ class RemoteSyncStateHelper {
|
|
|
1432
1631
|
let syncState;
|
|
1433
1632
|
if (core.Is.stringValue(syncPointerStore.syncPointers[response.storageKey])) {
|
|
1434
1633
|
// If the sync pointer exists, we load the current sync state
|
|
1435
|
-
syncState = await this.
|
|
1634
|
+
syncState = await this.getSyncState(syncPointerStore.syncPointers[response.storageKey]);
|
|
1436
1635
|
}
|
|
1437
1636
|
// If the sync state does not exist, we create a new one
|
|
1438
|
-
syncState ??= {
|
|
1637
|
+
syncState ??= {
|
|
1638
|
+
version: SYNC_STATE_VERSION,
|
|
1639
|
+
storageKey: response.storageKey,
|
|
1640
|
+
snapshots: []
|
|
1641
|
+
};
|
|
1439
1642
|
const batchSnapshot = {
|
|
1440
1643
|
version: SYNC_SNAPSHOT_VERSION,
|
|
1441
1644
|
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
1442
1645
|
dateCreated: now,
|
|
1443
1646
|
dateModified: now,
|
|
1647
|
+
isConsolidated: true,
|
|
1444
1648
|
changeSetStorageIds: this._batchResponseStorageIds[response.storageKey]
|
|
1445
1649
|
};
|
|
1446
1650
|
syncState.snapshots.push(batchSnapshot);
|
|
1447
|
-
// Store the sync state
|
|
1651
|
+
// Store the updated sync state
|
|
1448
1652
|
const syncStateId = await this.storeRemoteSyncState(syncState);
|
|
1449
1653
|
syncPointerStore.syncPointers[response.storageKey] = syncStateId;
|
|
1450
1654
|
// Store the verifiable sync pointer in the verifiable storage
|
|
@@ -1474,11 +1678,14 @@ class RemoteSyncStateHelper {
|
|
|
1474
1678
|
id: response.id
|
|
1475
1679
|
}
|
|
1476
1680
|
});
|
|
1681
|
+
// We have received a response to an item request, find the right storage
|
|
1682
|
+
// for the request id
|
|
1477
1683
|
if (!core.Is.empty(this._populateFullChanges[response.storageKey])) {
|
|
1478
1684
|
const idx = this._populateFullChanges[response.storageKey].requestIds.indexOf(response.id);
|
|
1479
1685
|
if (idx !== -1) {
|
|
1480
1686
|
this._populateFullChanges[response.storageKey].requestIds.splice(idx, 1);
|
|
1481
1687
|
this._populateFullChanges[response.storageKey].entities[response.id] = response.entity;
|
|
1688
|
+
// If there are no request ids remaining we can complete the population
|
|
1482
1689
|
if (this._populateFullChanges[response.storageKey].requestIds.length === 0) {
|
|
1483
1690
|
await this._populateFullChanges[response.storageKey].completeCallback();
|
|
1484
1691
|
}
|
|
@@ -1506,6 +1713,11 @@ class SynchronisedStorageService {
|
|
|
1506
1713
|
* @internal
|
|
1507
1714
|
*/
|
|
1508
1715
|
static _DEFAULT_CONSOLIDATION_BATCH_SIZE = 100;
|
|
1716
|
+
/**
|
|
1717
|
+
* The default max number of consolidations to keep in storage.
|
|
1718
|
+
* @internal
|
|
1719
|
+
*/
|
|
1720
|
+
static _DEFAULT_MAX_CONSOLIDATIONS = 5;
|
|
1509
1721
|
/**
|
|
1510
1722
|
* Runtime name for the class.
|
|
1511
1723
|
*/
|
|
@@ -1624,6 +1836,7 @@ class SynchronisedStorageService {
|
|
|
1624
1836
|
SynchronisedStorageService._DEFAULT_CONSOLIDATION_INTERVAL_MINUTES,
|
|
1625
1837
|
consolidationBatchSize: options.config.consolidationBatchSize ??
|
|
1626
1838
|
SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE,
|
|
1839
|
+
maxConsolidations: options.config.maxConsolidations ?? SynchronisedStorageService._DEFAULT_MAX_CONSOLIDATIONS,
|
|
1627
1840
|
blobStorageEncryptionKeyId: options.config.blobStorageEncryptionKeyId ?? "synchronised-storage-blob-encryption-key",
|
|
1628
1841
|
verifiableStorageKeyId: options.config.verifiableStorageKeyId
|
|
1629
1842
|
};
|
|
@@ -1639,11 +1852,16 @@ class SynchronisedStorageService {
|
|
|
1639
1852
|
this._blobStorageHelper = new BlobStorageHelper(this._logging, this._vaultConnector, this._blobStorageConnector, this._config.blobStorageEncryptionKeyId, this._config.isTrustedNode);
|
|
1640
1853
|
this._changeSetHelper = new ChangeSetHelper(this._logging, this._eventBusComponent, this._identityConnector, this._blobStorageHelper, this._config.synchronisedStorageMethodId);
|
|
1641
1854
|
this._localSyncStateHelper = new LocalSyncStateHelper(this._logging, this._localSyncSnapshotEntryEntityStorage, this._changeSetHelper);
|
|
1642
|
-
this._remoteSyncStateHelper = new RemoteSyncStateHelper(this._logging, this._eventBusComponent, this._verifiableSyncPointerStorageConnector, this._blobStorageHelper, this._changeSetHelper, this._config.isTrustedNode);
|
|
1855
|
+
this._remoteSyncStateHelper = new RemoteSyncStateHelper(this._logging, this._eventBusComponent, this._verifiableSyncPointerStorageConnector, this._blobStorageHelper, this._changeSetHelper, this._config.isTrustedNode, this._config.maxConsolidations);
|
|
1643
1856
|
this._serviceStarted = false;
|
|
1644
1857
|
this._activeStorageKeys = {};
|
|
1645
1858
|
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RegisterStorageKey, async (event) => this.registerStorageKey(event.data));
|
|
1646
|
-
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, async (event) =>
|
|
1859
|
+
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, async (event) => {
|
|
1860
|
+
// Make sure the change event is from this node
|
|
1861
|
+
if (core.Is.stringValue(this._nodeIdentity) && this._nodeIdentity === event.data.nodeIdentity) {
|
|
1862
|
+
await this._localSyncStateHelper.addLocalChange(event.data.storageKey, event.data.operation, event.data.id);
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1647
1865
|
}
|
|
1648
1866
|
/**
|
|
1649
1867
|
* The component needs to be started when the node is initialized.
|
|
@@ -1733,7 +1951,7 @@ class SynchronisedStorageService {
|
|
|
1733
1951
|
// to store the change set in the synchronised storage.
|
|
1734
1952
|
// This will be performed using rights-management
|
|
1735
1953
|
const copy = await this._changeSetHelper.copyChangeset(syncChangeSet);
|
|
1736
|
-
if (!core.Is.empty(copy)
|
|
1954
|
+
if (!core.Is.empty(copy)) {
|
|
1737
1955
|
// Apply the changes to this node
|
|
1738
1956
|
await this._changeSetHelper.applyChangeset(copy.syncChangeSet);
|
|
1739
1957
|
// And update the sync state with the latest changes
|
|
@@ -1790,7 +2008,7 @@ class SynchronisedStorageService {
|
|
|
1790
2008
|
if (!core.Is.empty(verifiableSyncPointerStore.syncPointers[storageKey])) {
|
|
1791
2009
|
// Load the sync state from the remote blob storage using the sync pointer
|
|
1792
2010
|
// to load the sync state
|
|
1793
|
-
const remoteSyncState = await this._remoteSyncStateHelper.
|
|
2011
|
+
const remoteSyncState = await this._remoteSyncStateHelper.getSyncState(verifiableSyncPointerStore.syncPointers[storageKey]);
|
|
1794
2012
|
// If we got the sync state we can try and sync from it
|
|
1795
2013
|
if (!core.Is.undefined(remoteSyncState)) {
|
|
1796
2014
|
await this._localSyncStateHelper.applySyncState(storageKey, remoteSyncState);
|
|
@@ -1811,64 +2029,67 @@ class SynchronisedStorageService {
|
|
|
1811
2029
|
storageKey
|
|
1812
2030
|
}
|
|
1813
2031
|
});
|
|
1814
|
-
const
|
|
1815
|
-
if (
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
await this._logging?.log({
|
|
1829
|
-
level: "info",
|
|
1830
|
-
source: this.CLASS_NAME,
|
|
1831
|
-
message: "builtStorageChangeSet",
|
|
1832
|
-
data: {
|
|
1833
|
-
storageKey,
|
|
1834
|
-
changeSetStorageId
|
|
1835
|
-
}
|
|
1836
|
-
});
|
|
1837
|
-
// Send the local changes to the remote storage if we are a trusted node
|
|
1838
|
-
if (this._config.isTrustedNode && core.Is.stringValue(changeSetStorageId)) {
|
|
1839
|
-
// If we are a trusted node, we can add the change set to the sync state
|
|
1840
|
-
// and remove the local change snapshot
|
|
1841
|
-
await this._remoteSyncStateHelper.addChangeSetToSyncState(storageKey, changeSetStorageId);
|
|
1842
|
-
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
2032
|
+
const localChangeSnapshots = await this._localSyncStateHelper.getSnapshots(storageKey, true);
|
|
2033
|
+
if (localChangeSnapshots.length > 0) {
|
|
2034
|
+
const localChangeSnapshot = localChangeSnapshots[0];
|
|
2035
|
+
if (core.Is.arrayValue(localChangeSnapshot.changes)) {
|
|
2036
|
+
await this._remoteSyncStateHelper.buildChangeSet(storageKey, localChangeSnapshot.changes, async (syncChangeSet, changeSetStorageId) => {
|
|
2037
|
+
if (core.Is.empty(syncChangeSet) && core.Is.empty(changeSetStorageId)) {
|
|
2038
|
+
await this._logging?.log({
|
|
2039
|
+
level: "info",
|
|
2040
|
+
source: this.CLASS_NAME,
|
|
2041
|
+
message: "builtStorageChangeSetNone",
|
|
2042
|
+
data: {
|
|
2043
|
+
storageKey
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
1843
2046
|
}
|
|
1844
|
-
else
|
|
1845
|
-
core.Is.object(syncChangeSet)) {
|
|
1846
|
-
// If we are not a trusted node, we need to send the changes to the trusted node
|
|
1847
|
-
// and then remove the local change snapshot
|
|
2047
|
+
else {
|
|
1848
2048
|
await this._logging?.log({
|
|
1849
2049
|
level: "info",
|
|
1850
2050
|
source: this.CLASS_NAME,
|
|
1851
|
-
message: "
|
|
2051
|
+
message: "builtStorageChangeSet",
|
|
1852
2052
|
data: {
|
|
1853
2053
|
storageKey,
|
|
1854
2054
|
changeSetStorageId
|
|
1855
2055
|
}
|
|
1856
2056
|
});
|
|
1857
|
-
|
|
1858
|
-
|
|
2057
|
+
// Send the local changes to the remote storage if we are a trusted node
|
|
2058
|
+
if (this._config.isTrustedNode && core.Is.stringValue(changeSetStorageId)) {
|
|
2059
|
+
// If we are a trusted node, we can add the change set to the sync state
|
|
2060
|
+
// and remove the local change snapshot
|
|
2061
|
+
await this._remoteSyncStateHelper.addChangeSetToSyncState(storageKey, changeSetStorageId);
|
|
2062
|
+
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
2063
|
+
}
|
|
2064
|
+
else if (!core.Is.empty(this._trustedSynchronisedStorageComponent) &&
|
|
2065
|
+
core.Is.object(syncChangeSet)) {
|
|
2066
|
+
// If we are not a trusted node, we need to send the changes to the trusted node
|
|
2067
|
+
// and then remove the local change snapshot
|
|
2068
|
+
await this._logging?.log({
|
|
2069
|
+
level: "info",
|
|
2070
|
+
source: this.CLASS_NAME,
|
|
2071
|
+
message: "sendingChangeSetToTrustedNode",
|
|
2072
|
+
data: {
|
|
2073
|
+
storageKey,
|
|
2074
|
+
changeSetStorageId
|
|
2075
|
+
}
|
|
2076
|
+
});
|
|
2077
|
+
await this._trustedSynchronisedStorageComponent.syncChangeSet(syncChangeSet);
|
|
2078
|
+
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
2079
|
+
}
|
|
1859
2080
|
}
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
else {
|
|
2084
|
+
await this._logging?.log({
|
|
2085
|
+
level: "info",
|
|
2086
|
+
source: this.CLASS_NAME,
|
|
2087
|
+
message: "updateFromLocalSyncStateNoChanges",
|
|
2088
|
+
data: {
|
|
2089
|
+
storageKey
|
|
2090
|
+
}
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
1872
2093
|
}
|
|
1873
2094
|
}
|
|
1874
2095
|
/**
|
|
@@ -1882,7 +2103,8 @@ class SynchronisedStorageService {
|
|
|
1882
2103
|
try {
|
|
1883
2104
|
// If we are performing a consolidation, we can remove the local change snapshot
|
|
1884
2105
|
// as we are going to create a complete changeset from the DB
|
|
1885
|
-
|
|
2106
|
+
const localChangeSnapshots = await this._localSyncStateHelper.getSnapshots(storageKey, true);
|
|
2107
|
+
localChangeSnapshot = localChangeSnapshots[0];
|
|
1886
2108
|
if (!core.Is.empty(localChangeSnapshot)) {
|
|
1887
2109
|
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
1888
2110
|
}
|