@twin.org/synchronised-storage-service 0.0.1-next.2 → 0.0.1-next.3
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 +370 -187
- package/dist/esm/index.mjs +370 -187
- package/dist/types/entities/syncSnapshotEntry.d.ts +3 -3
- package/dist/types/helpers/changeSetHelper.d.ts +4 -3
- package/dist/types/helpers/localSyncStateHelper.d.ts +6 -6
- package/dist/types/helpers/remoteSyncStateHelper.d.ts +14 -13
- package/dist/types/index.d.ts +5 -1
- package/dist/types/models/ISyncChangeSet.d.ts +2 -2
- package/dist/types/models/ISyncPointerStore.d.ts +11 -0
- package/dist/types/models/ISynchronisedStorageServiceConfig.d.ts +5 -5
- package/dist/types/models/ISynchronisedStorageServiceConstructorOptions.d.ts +5 -0
- package/docs/changelog.md +14 -0
- package/docs/reference/classes/SyncSnapshotEntry.md +5 -5
- package/docs/reference/index.md +4 -0
- package/docs/reference/interfaces/ISyncChangeSet.md +65 -0
- package/docs/reference/interfaces/ISyncPointerStore.md +15 -0
- package/docs/reference/interfaces/ISyncSnapshot.md +35 -0
- package/docs/reference/interfaces/ISyncState.md +11 -0
- package/docs/reference/interfaces/ISynchronisedStorageServiceConfig.md +7 -7
- package/docs/reference/interfaces/ISynchronisedStorageServiceConstructorOptions.md +14 -0
- package/locales/en.json +27 -7
- package/package.json +3 -2
- package/dist/types/models/ISyncPointer.d.ts +0 -9
package/dist/cjs/index.cjs
CHANGED
|
@@ -22,9 +22,9 @@ exports.SyncSnapshotEntry = class SyncSnapshotEntry {
|
|
|
22
22
|
*/
|
|
23
23
|
id;
|
|
24
24
|
/**
|
|
25
|
-
* The
|
|
25
|
+
* The storage key for the snapshot i.e. which entity is being synchronized.
|
|
26
26
|
*/
|
|
27
|
-
|
|
27
|
+
storageKey;
|
|
28
28
|
/**
|
|
29
29
|
* The date the snapshot was created.
|
|
30
30
|
*/
|
|
@@ -44,7 +44,7 @@ exports.SyncSnapshotEntry = class SyncSnapshotEntry {
|
|
|
44
44
|
/**
|
|
45
45
|
* The changes that were made in this snapshot, if this is a local snapshot.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
changes;
|
|
48
48
|
};
|
|
49
49
|
__decorate([
|
|
50
50
|
entity.property({ type: "string", isPrimary: true }),
|
|
@@ -53,7 +53,7 @@ __decorate([
|
|
|
53
53
|
__decorate([
|
|
54
54
|
entity.property({ type: "string", isSecondary: true }),
|
|
55
55
|
__metadata("design:type", String)
|
|
56
|
-
], exports.SyncSnapshotEntry.prototype, "
|
|
56
|
+
], exports.SyncSnapshotEntry.prototype, "storageKey", void 0);
|
|
57
57
|
__decorate([
|
|
58
58
|
entity.property({ type: "string" }),
|
|
59
59
|
__metadata("design:type", String)
|
|
@@ -73,7 +73,7 @@ __decorate([
|
|
|
73
73
|
__decorate([
|
|
74
74
|
entity.property({ type: "array", itemType: "object", optional: true }),
|
|
75
75
|
__metadata("design:type", Array)
|
|
76
|
-
], exports.SyncSnapshotEntry.prototype, "
|
|
76
|
+
], exports.SyncSnapshotEntry.prototype, "changes", void 0);
|
|
77
77
|
exports.SyncSnapshotEntry = __decorate([
|
|
78
78
|
entity.entity()
|
|
79
79
|
], exports.SyncSnapshotEntry);
|
|
@@ -231,15 +231,14 @@ class ChangeSetHelper {
|
|
|
231
231
|
/**
|
|
232
232
|
* Apply a sync changeset.
|
|
233
233
|
* @param changeSetStorageId The id of the sync changeset to apply.
|
|
234
|
-
* @returns
|
|
234
|
+
* @returns The changeset if it existed.
|
|
235
235
|
*/
|
|
236
236
|
async getAndApplyChangeset(changeSetStorageId) {
|
|
237
237
|
const syncChangeset = await this.getAndVerifyChangeset(changeSetStorageId);
|
|
238
238
|
if (!core.Is.empty(syncChangeset)) {
|
|
239
239
|
await this.applyChangeset(syncChangeset);
|
|
240
|
-
return true;
|
|
241
240
|
}
|
|
242
|
-
return
|
|
241
|
+
return syncChangeset;
|
|
243
242
|
}
|
|
244
243
|
/**
|
|
245
244
|
* Apply a sync changeset.
|
|
@@ -266,7 +265,7 @@ class ChangeSetHelper {
|
|
|
266
265
|
// so we need to restore it here.
|
|
267
266
|
change.entity.nodeIdentity = syncChangeset.nodeIdentity;
|
|
268
267
|
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemSet, {
|
|
269
|
-
|
|
268
|
+
storageKey: syncChangeset.storageKey,
|
|
270
269
|
entity: change.entity
|
|
271
270
|
});
|
|
272
271
|
}
|
|
@@ -274,7 +273,7 @@ class ChangeSetHelper {
|
|
|
274
273
|
case synchronisedStorageModels.SyncChangeOperation.Delete:
|
|
275
274
|
if (!core.Is.empty(change.id)) {
|
|
276
275
|
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemRemove, {
|
|
277
|
-
|
|
276
|
+
storageKey: syncChangeset.storageKey,
|
|
278
277
|
id: change.id
|
|
279
278
|
});
|
|
280
279
|
}
|
|
@@ -286,9 +285,10 @@ class ChangeSetHelper {
|
|
|
286
285
|
/**
|
|
287
286
|
* Store the changeset.
|
|
288
287
|
* @param syncChangeSet The sync change set to store.
|
|
288
|
+
* @param nodeIdentity The node identity to use for the changeset.
|
|
289
289
|
* @returns The id of the change set.
|
|
290
290
|
*/
|
|
291
|
-
async storeChangeSet(syncChangeSet) {
|
|
291
|
+
async storeChangeSet(syncChangeSet, nodeIdentity) {
|
|
292
292
|
await this._logging?.log({
|
|
293
293
|
level: "info",
|
|
294
294
|
source: this.CLASS_NAME,
|
|
@@ -302,7 +302,7 @@ class ChangeSetHelper {
|
|
|
302
302
|
return this._blobStorageComponent.create(core.Converter.bytesToBase64(core.ObjectHelper.toBytes(syncChangeSet)), undefined, undefined, undefined, {
|
|
303
303
|
disableEncryption: true,
|
|
304
304
|
compress: blobStorageModels.BlobStorageCompressionType.Gzip
|
|
305
|
-
});
|
|
305
|
+
}, undefined, nodeIdentity);
|
|
306
306
|
}
|
|
307
307
|
/**
|
|
308
308
|
* Verify the proof of a sync changeset.
|
|
@@ -356,7 +356,7 @@ class ChangeSetHelper {
|
|
|
356
356
|
delete changeSetWithoutProof.proof;
|
|
357
357
|
const proof = await this._identityConnector.createProof(syncChangeset.nodeIdentity, identityModels.DocumentHelper.joinId(syncChangeset.nodeIdentity, this._decentralisedStorageMethodId), standardsW3cDid.ProofTypes.DataIntegrityProof, changeSetWithoutProof);
|
|
358
358
|
await this._logging?.log({
|
|
359
|
-
level: "
|
|
359
|
+
level: "info",
|
|
360
360
|
source: this.CLASS_NAME,
|
|
361
361
|
message: "createdChangeSetProof",
|
|
362
362
|
data: {
|
|
@@ -406,33 +406,51 @@ class LocalSyncStateHelper {
|
|
|
406
406
|
}
|
|
407
407
|
/**
|
|
408
408
|
* Add a new change to the local snapshot.
|
|
409
|
-
* @param
|
|
409
|
+
* @param storageKey The storage key of the snapshot to add the change for.
|
|
410
410
|
* @param operation The operation to perform.
|
|
411
411
|
* @param id The id of the entity to add the change for.
|
|
412
412
|
* @returns Nothing.
|
|
413
413
|
*/
|
|
414
|
-
async addLocalChange(
|
|
415
|
-
|
|
416
|
-
|
|
414
|
+
async addLocalChange(storageKey, operation, id) {
|
|
415
|
+
await this._logging?.log({
|
|
416
|
+
level: "info",
|
|
417
|
+
source: this.CLASS_NAME,
|
|
418
|
+
message: "addLocalChange",
|
|
419
|
+
data: {
|
|
420
|
+
storageKey,
|
|
421
|
+
operation,
|
|
422
|
+
id
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
const localChangeSnapshot = await this.getLocalChangeSnapshot(storageKey);
|
|
426
|
+
localChangeSnapshot.changes ??= [];
|
|
417
427
|
// If we already have a change for this id we are
|
|
418
428
|
// about to supersede it, we remove the previous change
|
|
419
429
|
// to avoid having multiple changes for the same id
|
|
420
|
-
const previousChangeIndex = localChangeSnapshot.
|
|
430
|
+
const previousChangeIndex = localChangeSnapshot.changes.findIndex(change => change.id === id);
|
|
421
431
|
if (previousChangeIndex !== -1) {
|
|
422
|
-
localChangeSnapshot.
|
|
432
|
+
localChangeSnapshot.changes.splice(previousChangeIndex, 1);
|
|
423
433
|
}
|
|
424
|
-
if (localChangeSnapshot.
|
|
434
|
+
if (localChangeSnapshot.changes.length > 0) {
|
|
425
435
|
localChangeSnapshot.dateModified = new Date(Date.now()).toISOString();
|
|
426
436
|
}
|
|
427
|
-
localChangeSnapshot.
|
|
437
|
+
localChangeSnapshot.changes.push({ operation, id });
|
|
428
438
|
await this.setLocalChangeSnapshot(localChangeSnapshot);
|
|
429
439
|
}
|
|
430
440
|
/**
|
|
431
441
|
* Get the current local snapshot.
|
|
432
|
-
* @param
|
|
442
|
+
* @param storageKey The storage key of the snapshot to get.
|
|
433
443
|
* @returns The local snapshot entry.
|
|
434
444
|
*/
|
|
435
|
-
async getLocalChangeSnapshot(
|
|
445
|
+
async getLocalChangeSnapshot(storageKey) {
|
|
446
|
+
await this._logging?.log({
|
|
447
|
+
level: "info",
|
|
448
|
+
source: this.CLASS_NAME,
|
|
449
|
+
message: "getLocalChangeSnapshot",
|
|
450
|
+
data: {
|
|
451
|
+
storageKey
|
|
452
|
+
}
|
|
453
|
+
});
|
|
436
454
|
const queryResult = await this._localSyncSnapshotEntryEntityStorage.query({
|
|
437
455
|
conditions: [
|
|
438
456
|
{
|
|
@@ -441,18 +459,34 @@ class LocalSyncStateHelper {
|
|
|
441
459
|
comparison: entity.ComparisonOperator.Equals
|
|
442
460
|
},
|
|
443
461
|
{
|
|
444
|
-
property: "
|
|
445
|
-
value:
|
|
462
|
+
property: "storageKey",
|
|
463
|
+
value: storageKey,
|
|
446
464
|
comparison: entity.ComparisonOperator.Equals
|
|
447
465
|
}
|
|
448
466
|
]
|
|
449
467
|
});
|
|
450
468
|
if (queryResult.entities.length > 0) {
|
|
469
|
+
await this._logging?.log({
|
|
470
|
+
level: "info",
|
|
471
|
+
source: this.CLASS_NAME,
|
|
472
|
+
message: "localChangeSnapshotExists",
|
|
473
|
+
data: {
|
|
474
|
+
storageKey
|
|
475
|
+
}
|
|
476
|
+
});
|
|
451
477
|
return queryResult.entities[0];
|
|
452
478
|
}
|
|
479
|
+
await this._logging?.log({
|
|
480
|
+
level: "info",
|
|
481
|
+
source: this.CLASS_NAME,
|
|
482
|
+
message: "localChangeSnapshotDoesNotExist",
|
|
483
|
+
data: {
|
|
484
|
+
storageKey
|
|
485
|
+
}
|
|
486
|
+
});
|
|
453
487
|
return {
|
|
454
488
|
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
455
|
-
|
|
489
|
+
storageKey,
|
|
456
490
|
dateCreated: new Date(Date.now()).toISOString(),
|
|
457
491
|
changeSetStorageIds: [],
|
|
458
492
|
isLocalSnapshot: true
|
|
@@ -464,6 +498,14 @@ class LocalSyncStateHelper {
|
|
|
464
498
|
* @returns Nothing.
|
|
465
499
|
*/
|
|
466
500
|
async setLocalChangeSnapshot(localChangeSnapshot) {
|
|
501
|
+
await this._logging?.log({
|
|
502
|
+
level: "info",
|
|
503
|
+
source: this.CLASS_NAME,
|
|
504
|
+
message: "setLocalChangeSnapshot",
|
|
505
|
+
data: {
|
|
506
|
+
storageKey: localChangeSnapshot.storageKey
|
|
507
|
+
}
|
|
508
|
+
});
|
|
467
509
|
await this._localSyncSnapshotEntryEntityStorage.set(localChangeSnapshot);
|
|
468
510
|
}
|
|
469
511
|
/**
|
|
@@ -484,11 +526,11 @@ class LocalSyncStateHelper {
|
|
|
484
526
|
}
|
|
485
527
|
/**
|
|
486
528
|
* Sync local data using a remote sync state.
|
|
487
|
-
* @param
|
|
529
|
+
* @param storageKey The storage key of the snapshot to sync with.
|
|
488
530
|
* @param remoteSyncState The sync state to sync with.
|
|
489
531
|
* @returns Nothing.
|
|
490
532
|
*/
|
|
491
|
-
async syncFromRemote(
|
|
533
|
+
async syncFromRemote(storageKey, remoteSyncState) {
|
|
492
534
|
await this._logging?.log({
|
|
493
535
|
level: "info",
|
|
494
536
|
source: this.CLASS_NAME,
|
|
@@ -514,7 +556,7 @@ class LocalSyncStateHelper {
|
|
|
514
556
|
const localSnapshot = await this._localSyncSnapshotEntryEntityStorage.get(remoteSnapshot.id);
|
|
515
557
|
const remoteSnapshotWithContext = {
|
|
516
558
|
...remoteSnapshot,
|
|
517
|
-
|
|
559
|
+
storageKey
|
|
518
560
|
};
|
|
519
561
|
if (core.Is.empty(localSnapshot)) {
|
|
520
562
|
// We don't have the snapshot locally, so we need to process it
|
|
@@ -633,12 +675,12 @@ class RemoteSyncStateHelper {
|
|
|
633
675
|
*/
|
|
634
676
|
_changeSetHelper;
|
|
635
677
|
/**
|
|
636
|
-
* The storage ids of the batch responses for each
|
|
678
|
+
* The storage ids of the batch responses for each storage key.
|
|
637
679
|
* @internal
|
|
638
680
|
*/
|
|
639
681
|
_batchResponseStorageIds;
|
|
640
682
|
/**
|
|
641
|
-
* The full changes for each
|
|
683
|
+
* The full changes for each storage key.
|
|
642
684
|
* @internal
|
|
643
685
|
*/
|
|
644
686
|
_populateFullChanges;
|
|
@@ -686,52 +728,73 @@ class RemoteSyncStateHelper {
|
|
|
686
728
|
}
|
|
687
729
|
/**
|
|
688
730
|
* Create and store a change set.
|
|
689
|
-
* @param
|
|
731
|
+
* @param storageKey The storage key of the change set.
|
|
690
732
|
* @param changes The changes to apply.
|
|
691
733
|
* @param completeCallback The callback to call when the changeset is created and stored.
|
|
692
734
|
* @returns The storage id of the change set if created.
|
|
693
735
|
*/
|
|
694
|
-
async createAndStoreChangeSet(
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
const setChanges = changes.filter(c => c.operation === synchronisedStorageModels.SyncChangeOperation.Set);
|
|
703
|
-
if (setChanges.length === 0) {
|
|
704
|
-
// If we don't need to request any full details, we can just call the complete callback
|
|
705
|
-
await this.finaliseFullChanges(schemaType, completeCallback);
|
|
706
|
-
}
|
|
707
|
-
else {
|
|
708
|
-
// Otherwise we need to request the full details for each change
|
|
709
|
-
this._populateFullChanges[schemaType].requestIds = setChanges.map(change => change.id);
|
|
710
|
-
// Once all the requests are handled the callback will be called
|
|
711
|
-
for (const change of setChanges) {
|
|
712
|
-
// Create a request for each change to populate the full details
|
|
713
|
-
this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemRequest, {
|
|
714
|
-
schemaType,
|
|
715
|
-
id: change.id
|
|
716
|
-
});
|
|
717
|
-
}
|
|
736
|
+
async createAndStoreChangeSet(storageKey, changes, completeCallback) {
|
|
737
|
+
await this._logging?.log({
|
|
738
|
+
level: "info",
|
|
739
|
+
source: this.CLASS_NAME,
|
|
740
|
+
message: "createAndStoreChangeSet",
|
|
741
|
+
data: {
|
|
742
|
+
storageKey,
|
|
743
|
+
changeCount: changes.length
|
|
718
744
|
}
|
|
745
|
+
});
|
|
746
|
+
this._populateFullChanges[storageKey] = {
|
|
747
|
+
changes,
|
|
748
|
+
entities: {},
|
|
749
|
+
requestIds: [],
|
|
750
|
+
completeCallback: async () => this.finaliseFullChanges(storageKey, completeCallback)
|
|
751
|
+
};
|
|
752
|
+
const setChanges = changes.filter(c => c.operation === synchronisedStorageModels.SyncChangeOperation.Set);
|
|
753
|
+
if (setChanges.length === 0) {
|
|
754
|
+
// If we don't need to request any full details, we can just call the complete callback
|
|
755
|
+
await this.finaliseFullChanges(storageKey, completeCallback);
|
|
719
756
|
}
|
|
720
757
|
else {
|
|
721
|
-
|
|
758
|
+
// Otherwise we need to request the full details for each change
|
|
759
|
+
this._populateFullChanges[storageKey].requestIds = setChanges.map(change => change.id);
|
|
760
|
+
// Once all the requests are handled the callback will be called
|
|
761
|
+
for (const change of setChanges) {
|
|
762
|
+
// Create a request for each change to populate the full details
|
|
763
|
+
await this._logging?.log({
|
|
764
|
+
level: "info",
|
|
765
|
+
source: this.CLASS_NAME,
|
|
766
|
+
message: "createChangeSetRequestingItem",
|
|
767
|
+
data: {
|
|
768
|
+
storageKey,
|
|
769
|
+
id: change.id
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemRequest, {
|
|
773
|
+
storageKey,
|
|
774
|
+
id: change.id
|
|
775
|
+
});
|
|
776
|
+
}
|
|
722
777
|
}
|
|
723
778
|
}
|
|
724
779
|
/**
|
|
725
780
|
* Finalise the full details for the sync change set.
|
|
726
|
-
* @param
|
|
781
|
+
* @param storageKey The storage key of the change set.
|
|
727
782
|
* @param completeCallback The callback to call when the changeset is populated.
|
|
728
783
|
* @returns Nothing.
|
|
729
784
|
*/
|
|
730
|
-
async finaliseFullChanges(
|
|
785
|
+
async finaliseFullChanges(storageKey, completeCallback) {
|
|
786
|
+
await this._logging?.log({
|
|
787
|
+
level: "info",
|
|
788
|
+
source: this.CLASS_NAME,
|
|
789
|
+
message: "finalisingSyncChanges",
|
|
790
|
+
data: {
|
|
791
|
+
storageKey
|
|
792
|
+
}
|
|
793
|
+
});
|
|
731
794
|
if (core.Is.stringValue(this._nodeIdentity)) {
|
|
732
|
-
const changes = this._populateFullChanges[
|
|
795
|
+
const changes = this._populateFullChanges[storageKey].changes;
|
|
733
796
|
for (const change of changes) {
|
|
734
|
-
change.entity = this._populateFullChanges[
|
|
797
|
+
change.entity = this._populateFullChanges[storageKey].entities[change.id] ?? change.entity;
|
|
735
798
|
if (change.operation === synchronisedStorageModels.SyncChangeOperation.Set && core.Is.objectValue(change.entity)) {
|
|
736
799
|
// Remove the node identity as the changeset has this stored at the top level
|
|
737
800
|
// and we do not want to store it in the change itself to reduce redundancy
|
|
@@ -742,15 +805,29 @@ class RemoteSyncStateHelper {
|
|
|
742
805
|
const syncChangeSet = {
|
|
743
806
|
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
744
807
|
dateCreated: new Date(Date.now()).toISOString(),
|
|
745
|
-
|
|
808
|
+
storageKey,
|
|
746
809
|
changes,
|
|
747
810
|
nodeIdentity: this._nodeIdentity
|
|
748
811
|
};
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
812
|
+
try {
|
|
813
|
+
// And sign it with the node identity
|
|
814
|
+
syncChangeSet.proof = await this._changeSetHelper.createChangeSetProof(syncChangeSet);
|
|
815
|
+
// Store the changeset in the blob storage
|
|
816
|
+
const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet, this._nodeIdentity);
|
|
817
|
+
await completeCallback(changeSetStorageId);
|
|
818
|
+
}
|
|
819
|
+
catch (err) {
|
|
820
|
+
await this._logging?.log({
|
|
821
|
+
level: "error",
|
|
822
|
+
source: this.CLASS_NAME,
|
|
823
|
+
message: "finalisingSyncChangesFailed",
|
|
824
|
+
data: {
|
|
825
|
+
storageKey
|
|
826
|
+
},
|
|
827
|
+
error: core.BaseError.fromError(err)
|
|
828
|
+
});
|
|
829
|
+
await completeCallback();
|
|
830
|
+
}
|
|
754
831
|
}
|
|
755
832
|
else {
|
|
756
833
|
await completeCallback();
|
|
@@ -758,15 +835,25 @@ class RemoteSyncStateHelper {
|
|
|
758
835
|
}
|
|
759
836
|
/**
|
|
760
837
|
* Add a new changeset into the sync state.
|
|
838
|
+
* @param storageKey The storage key of the change set to add.
|
|
761
839
|
* @param changeSetStorageId The id of the change set to add the the current state
|
|
762
840
|
* @returns Nothing.
|
|
763
841
|
*/
|
|
764
|
-
async addChangeSetToSyncState(changeSetStorageId) {
|
|
765
|
-
|
|
766
|
-
|
|
842
|
+
async addChangeSetToSyncState(storageKey, changeSetStorageId) {
|
|
843
|
+
await this._logging?.log({
|
|
844
|
+
level: "info",
|
|
845
|
+
source: this.CLASS_NAME,
|
|
846
|
+
message: "addChangeSetToSyncState",
|
|
847
|
+
data: {
|
|
848
|
+
storageKey,
|
|
849
|
+
changeSetStorageId
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
// First load the sync pointer store to get the current sync pointer for the storage key
|
|
853
|
+
const syncPointerStore = await this.getVerifiableSyncPointerStore();
|
|
767
854
|
let syncState;
|
|
768
|
-
if (!core.Is.empty(
|
|
769
|
-
syncState = await this.getRemoteSyncState(
|
|
855
|
+
if (!core.Is.empty(syncPointerStore.syncPointers[storageKey])) {
|
|
856
|
+
syncState = await this.getRemoteSyncState(syncPointerStore.syncPointers[storageKey]);
|
|
770
857
|
}
|
|
771
858
|
// No current sync state, so we create a new one
|
|
772
859
|
if (core.Is.empty(syncState)) {
|
|
@@ -791,34 +878,34 @@ class RemoteSyncStateHelper {
|
|
|
791
878
|
// Add the changeset storage id to the current snapshot
|
|
792
879
|
currentSnapshot.changeSetStorageIds.push(changeSetStorageId);
|
|
793
880
|
// Store the sync state in the blob storage
|
|
794
|
-
|
|
795
|
-
// Store the verifiable sync pointer in the verifiable storage
|
|
796
|
-
await this.
|
|
881
|
+
syncPointerStore.syncPointers[storageKey] = await this.storeRemoteSyncState(syncState);
|
|
882
|
+
// Store the verifiable sync pointer store in the verifiable storage
|
|
883
|
+
await this.storeVerifiableSyncPointerStore(syncPointerStore);
|
|
797
884
|
}
|
|
798
885
|
/**
|
|
799
886
|
* Create a consolidated snapshot for the entire storage.
|
|
800
|
-
* @param
|
|
887
|
+
* @param storageKey The storage key of the snapshot to create.
|
|
801
888
|
* @param batchSize The batch size to use for consolidation.
|
|
802
889
|
* @returns Nothing.
|
|
803
890
|
*/
|
|
804
|
-
async consolidateFromLocal(
|
|
891
|
+
async consolidateFromLocal(storageKey, batchSize) {
|
|
805
892
|
await this._logging?.log({
|
|
806
893
|
level: "info",
|
|
807
894
|
source: this.CLASS_NAME,
|
|
808
895
|
message: "consolidationStarting"
|
|
809
896
|
});
|
|
810
|
-
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, {
|
|
897
|
+
await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, { storageKey, batchSize });
|
|
811
898
|
}
|
|
812
899
|
/**
|
|
813
|
-
* Get the sync pointer.
|
|
814
|
-
* @returns The sync pointer.
|
|
900
|
+
* Get the sync pointer store.
|
|
901
|
+
* @returns The sync pointer store.
|
|
815
902
|
*/
|
|
816
|
-
async
|
|
903
|
+
async getVerifiableSyncPointerStore() {
|
|
817
904
|
try {
|
|
818
905
|
await this._logging?.log({
|
|
819
906
|
level: "info",
|
|
820
907
|
source: this.CLASS_NAME,
|
|
821
|
-
message: "
|
|
908
|
+
message: "verifiableSyncPointerStoreRetrieving",
|
|
822
909
|
data: {
|
|
823
910
|
key: this._synchronisedStorageKey
|
|
824
911
|
}
|
|
@@ -829,10 +916,9 @@ class RemoteSyncStateHelper {
|
|
|
829
916
|
await this._logging?.log({
|
|
830
917
|
level: "info",
|
|
831
918
|
source: this.CLASS_NAME,
|
|
832
|
-
message: "
|
|
919
|
+
message: "verifiableSyncPointerStoreRetrieved",
|
|
833
920
|
data: {
|
|
834
|
-
key: this._synchronisedStorageKey
|
|
835
|
-
syncPointerId: syncPointer.syncPointerId
|
|
921
|
+
key: this._synchronisedStorageKey
|
|
836
922
|
}
|
|
837
923
|
});
|
|
838
924
|
return syncPointer;
|
|
@@ -846,34 +932,34 @@ class RemoteSyncStateHelper {
|
|
|
846
932
|
await this._logging?.log({
|
|
847
933
|
level: "info",
|
|
848
934
|
source: this.CLASS_NAME,
|
|
849
|
-
message: "
|
|
935
|
+
message: "verifiableSyncPointerStoreNotFound",
|
|
850
936
|
data: {
|
|
851
937
|
key: this._synchronisedStorageKey
|
|
852
938
|
}
|
|
853
939
|
});
|
|
940
|
+
// If no sync pointer store exists, we return an empty one
|
|
941
|
+
return {
|
|
942
|
+
syncPointers: {}
|
|
943
|
+
};
|
|
854
944
|
}
|
|
855
945
|
/**
|
|
856
946
|
* Store the verifiable sync pointer in the verifiable storage.
|
|
857
|
-
* @param
|
|
947
|
+
* @param syncPointerStore The sync pointer store to store.
|
|
858
948
|
* @returns Nothing.
|
|
859
949
|
*/
|
|
860
|
-
async
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
});
|
|
874
|
-
// Store the verifiable sync pointer in the verifiable storage
|
|
875
|
-
await this._verifiableSyncPointerStorageConnector.create(this._synchronisedStorageKey, core.ObjectHelper.toBytes(verifiableSyncPointer));
|
|
876
|
-
return verifiableSyncPointer;
|
|
950
|
+
async storeVerifiableSyncPointerStore(syncPointerStore) {
|
|
951
|
+
if (this._nodeIdentity) {
|
|
952
|
+
await this._logging?.log({
|
|
953
|
+
level: "info",
|
|
954
|
+
source: this.CLASS_NAME,
|
|
955
|
+
message: "verifiableSyncPointerStoreStoring",
|
|
956
|
+
data: {
|
|
957
|
+
key: this._synchronisedStorageKey
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
// Store the verifiable sync pointer in the verifiable storage
|
|
961
|
+
await this._verifiableSyncPointerStorageConnector.update(this._nodeIdentity, this._synchronisedStorageKey, core.ObjectHelper.toBytes(syncPointerStore));
|
|
962
|
+
}
|
|
877
963
|
}
|
|
878
964
|
/**
|
|
879
965
|
* Store the remote sync state.
|
|
@@ -891,7 +977,7 @@ class RemoteSyncStateHelper {
|
|
|
891
977
|
});
|
|
892
978
|
// We don't want to encrypt the sync state as no other nodes would be able to read it
|
|
893
979
|
// the blob storage also needs to be publicly accessible so that other nodes can retrieve it
|
|
894
|
-
return this._blobStorageComponent.create(core.Converter.bytesToBase64(core.ObjectHelper.toBytes(syncState)), undefined, undefined, undefined, { disableEncryption: true, compress: blobStorageModels.BlobStorageCompressionType.Gzip });
|
|
980
|
+
return this._blobStorageComponent.create(core.Converter.bytesToBase64(core.ObjectHelper.toBytes(syncState)), undefined, undefined, undefined, { disableEncryption: true, compress: blobStorageModels.BlobStorageCompressionType.Gzip }, undefined, this._nodeIdentity);
|
|
895
981
|
}
|
|
896
982
|
/**
|
|
897
983
|
* Get the remote sync state.
|
|
@@ -910,7 +996,7 @@ class RemoteSyncStateHelper {
|
|
|
910
996
|
});
|
|
911
997
|
const blobEntry = await this._blobStorageComponent.get(syncPointerId, {
|
|
912
998
|
includeContent: true
|
|
913
|
-
});
|
|
999
|
+
}, undefined, this._nodeIdentity);
|
|
914
1000
|
if (core.Is.stringBase64(blobEntry.blob)) {
|
|
915
1001
|
const syncState = core.ObjectHelper.fromBytes(core.Converter.base64ToBytes(blobEntry.blob));
|
|
916
1002
|
await this._logging?.log({
|
|
@@ -953,28 +1039,31 @@ class RemoteSyncStateHelper {
|
|
|
953
1039
|
operation: synchronisedStorageModels.SyncChangeOperation.Set,
|
|
954
1040
|
id: change[response.primaryKey]
|
|
955
1041
|
})),
|
|
956
|
-
|
|
1042
|
+
storageKey: response.storageKey,
|
|
957
1043
|
nodeIdentity: this._nodeIdentity
|
|
958
1044
|
};
|
|
959
1045
|
// And sign it with the node identity
|
|
960
1046
|
syncChangeSet.proof = await this._changeSetHelper.createChangeSetProof(syncChangeSet);
|
|
961
1047
|
// Store the changeset in the blob storage
|
|
962
|
-
const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet);
|
|
1048
|
+
const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet, this._nodeIdentity);
|
|
963
1049
|
// Add the changeset storage id to the snapshot ids
|
|
964
|
-
this._batchResponseStorageIds[response.
|
|
965
|
-
this._batchResponseStorageIds[response.
|
|
1050
|
+
this._batchResponseStorageIds[response.storageKey] ??= [];
|
|
1051
|
+
this._batchResponseStorageIds[response.storageKey].push(changeSetStorageId);
|
|
966
1052
|
if (response.lastEntry) {
|
|
967
1053
|
const syncState = { snapshots: [] };
|
|
968
1054
|
const batchSnapshot = {
|
|
969
1055
|
id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
|
|
970
1056
|
dateCreated: new Date(Date.now()).toISOString(),
|
|
971
|
-
changeSetStorageIds: this._batchResponseStorageIds[response.
|
|
1057
|
+
changeSetStorageIds: this._batchResponseStorageIds[response.storageKey]
|
|
972
1058
|
};
|
|
973
1059
|
syncState.snapshots.push(batchSnapshot);
|
|
974
1060
|
// Store the sync state in the blob storage
|
|
975
1061
|
const syncStateId = await this.storeRemoteSyncState(syncState);
|
|
1062
|
+
// Get the current sync pointer store
|
|
1063
|
+
const syncPointerStore = await this.getVerifiableSyncPointerStore();
|
|
1064
|
+
syncPointerStore.syncPointers[response.storageKey] = syncStateId;
|
|
976
1065
|
// Store the verifiable sync pointer in the verifiable storage
|
|
977
|
-
await this.
|
|
1066
|
+
await this.storeVerifiableSyncPointerStore(syncPointerStore);
|
|
978
1067
|
await this._logging?.log({
|
|
979
1068
|
level: "info",
|
|
980
1069
|
source: this.CLASS_NAME,
|
|
@@ -988,13 +1077,22 @@ class RemoteSyncStateHelper {
|
|
|
988
1077
|
* @param response The item response to handle.
|
|
989
1078
|
*/
|
|
990
1079
|
async handleLocalItemResponse(response) {
|
|
991
|
-
|
|
992
|
-
|
|
1080
|
+
await this._logging?.log({
|
|
1081
|
+
level: "info",
|
|
1082
|
+
source: this.CLASS_NAME,
|
|
1083
|
+
message: "createChangeSetRespondingItem",
|
|
1084
|
+
data: {
|
|
1085
|
+
storageKey: response.storageKey,
|
|
1086
|
+
id: response.id
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
if (!core.Is.empty(this._populateFullChanges[response.storageKey])) {
|
|
1090
|
+
const idx = this._populateFullChanges[response.storageKey].requestIds.indexOf(response.id);
|
|
993
1091
|
if (idx !== -1) {
|
|
994
|
-
this._populateFullChanges[response.
|
|
995
|
-
this._populateFullChanges[response.
|
|
996
|
-
if (this._populateFullChanges[response.
|
|
997
|
-
await this._populateFullChanges[response.
|
|
1092
|
+
this._populateFullChanges[response.storageKey].requestIds.splice(idx, 1);
|
|
1093
|
+
this._populateFullChanges[response.storageKey].entities[response.id] = response.entity;
|
|
1094
|
+
if (this._populateFullChanges[response.storageKey].requestIds.length === 0) {
|
|
1095
|
+
await this._populateFullChanges[response.storageKey].completeCallback();
|
|
998
1096
|
}
|
|
999
1097
|
}
|
|
1000
1098
|
}
|
|
@@ -1006,20 +1104,20 @@ class RemoteSyncStateHelper {
|
|
|
1006
1104
|
*/
|
|
1007
1105
|
class SynchronisedStorageService {
|
|
1008
1106
|
/**
|
|
1009
|
-
* The default interval to check for entity updates
|
|
1107
|
+
* The default interval to check for entity updates.
|
|
1010
1108
|
* @internal
|
|
1011
1109
|
*/
|
|
1012
|
-
static
|
|
1110
|
+
static _DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES = 5;
|
|
1013
1111
|
/**
|
|
1014
|
-
* The default interval to perform consolidation
|
|
1112
|
+
* The default interval to perform consolidation.
|
|
1015
1113
|
* @internal
|
|
1016
1114
|
*/
|
|
1017
|
-
static
|
|
1115
|
+
static _DEFAULT_CONSOLIDATION_INTERVAL_MINUTES = 60;
|
|
1018
1116
|
/**
|
|
1019
1117
|
* The default size of a consolidation batch.
|
|
1020
1118
|
* @internal
|
|
1021
1119
|
*/
|
|
1022
|
-
static _DEFAULT_CONSOLIDATION_BATCH_SIZE =
|
|
1120
|
+
static _DEFAULT_CONSOLIDATION_BATCH_SIZE = 100;
|
|
1023
1121
|
/**
|
|
1024
1122
|
* Runtime name for the class.
|
|
1025
1123
|
*/
|
|
@@ -1054,6 +1152,11 @@ class SynchronisedStorageService {
|
|
|
1054
1152
|
* @internal
|
|
1055
1153
|
*/
|
|
1056
1154
|
_identityConnector;
|
|
1155
|
+
/**
|
|
1156
|
+
* The task scheduler component.
|
|
1157
|
+
* @internal
|
|
1158
|
+
*/
|
|
1159
|
+
_taskSchedulerComponent;
|
|
1057
1160
|
/**
|
|
1058
1161
|
* The synchronised storage service to use when this is not a trusted node.
|
|
1059
1162
|
* @internal
|
|
@@ -1080,15 +1183,15 @@ class SynchronisedStorageService {
|
|
|
1080
1183
|
*/
|
|
1081
1184
|
_config;
|
|
1082
1185
|
/**
|
|
1083
|
-
* The
|
|
1186
|
+
* The flag to determine if the service has been started.
|
|
1084
1187
|
* @internal
|
|
1085
1188
|
*/
|
|
1086
|
-
|
|
1189
|
+
_serviceStarted;
|
|
1087
1190
|
/**
|
|
1088
|
-
* The
|
|
1191
|
+
* The active storage keys for the synchronised storage service.
|
|
1089
1192
|
* @internal
|
|
1090
1193
|
*/
|
|
1091
|
-
|
|
1194
|
+
_activeStorageKeys;
|
|
1092
1195
|
/**
|
|
1093
1196
|
* The identity of the node this connector is running on.
|
|
1094
1197
|
* @internal
|
|
@@ -1107,14 +1210,15 @@ class SynchronisedStorageService {
|
|
|
1107
1210
|
this._verifiableSyncPointerStorageConnector = verifiableStorageModels.VerifiableStorageConnectorFactory.get(options.verifiableStorageConnectorType ?? "verifiable-storage");
|
|
1108
1211
|
this._blobStorageComponent = core.ComponentFactory.get(options.blobStorageComponentType ?? "blob-storage");
|
|
1109
1212
|
this._identityConnector = identityModels.IdentityConnectorFactory.get(options.identityConnectorType ?? "identity");
|
|
1213
|
+
this._taskSchedulerComponent = core.ComponentFactory.get(options.taskSchedulerComponentType ?? "task-scheduler");
|
|
1110
1214
|
this._config = {
|
|
1111
1215
|
synchronisedStorageKey: options.config.synchronisedStorageKey,
|
|
1112
1216
|
synchronisedStorageMethodId: options.config.synchronisedStorageMethodId ?? "synchronised-storage-assertion",
|
|
1113
|
-
|
|
1114
|
-
SynchronisedStorageService.
|
|
1217
|
+
entityUpdateIntervalMinutes: options.config.entityUpdateIntervalMinutes ??
|
|
1218
|
+
SynchronisedStorageService._DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES,
|
|
1115
1219
|
isTrustedNode: options.config.isTrustedNode ?? false,
|
|
1116
|
-
|
|
1117
|
-
SynchronisedStorageService.
|
|
1220
|
+
consolidationIntervalMinutes: options.config.consolidationIntervalMinutes ??
|
|
1221
|
+
SynchronisedStorageService._DEFAULT_CONSOLIDATION_INTERVAL_MINUTES,
|
|
1118
1222
|
consolidationBatchSize: options.config.consolidationBatchSize ??
|
|
1119
1223
|
SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE
|
|
1120
1224
|
};
|
|
@@ -1128,10 +1232,10 @@ class SynchronisedStorageService {
|
|
|
1128
1232
|
this._changeSetHelper = new ChangeSetHelper(this._logging, this._eventBusComponent, this._blobStorageComponent, this._identityConnector, this._config.synchronisedStorageMethodId);
|
|
1129
1233
|
this._localSyncStateHelper = new LocalSyncStateHelper(this._logging, this._localSyncSnapshotEntryEntityStorage, this._changeSetHelper);
|
|
1130
1234
|
this._remoteSyncStateHelper = new RemoteSyncStateHelper(this._logging, this._eventBusComponent, this._blobStorageComponent, this._verifiableSyncPointerStorageConnector, this._changeSetHelper, this._config.synchronisedStorageKey);
|
|
1131
|
-
this.
|
|
1132
|
-
this.
|
|
1133
|
-
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.
|
|
1134
|
-
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, async (event) => this._localSyncStateHelper.addLocalChange(event.data.
|
|
1235
|
+
this._serviceStarted = false;
|
|
1236
|
+
this._activeStorageKeys = {};
|
|
1237
|
+
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RegisterStorageKey, async (event) => this.registerType(event.data));
|
|
1238
|
+
this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, async (event) => this._localSyncStateHelper.addLocalChange(event.data.storageKey, event.data.operation, event.data.id));
|
|
1135
1239
|
}
|
|
1136
1240
|
/**
|
|
1137
1241
|
* The component needs to be started when the node is initialized.
|
|
@@ -1143,6 +1247,11 @@ class SynchronisedStorageService {
|
|
|
1143
1247
|
async start(nodeIdentity, nodeLoggingConnectorType, componentState) {
|
|
1144
1248
|
this._nodeIdentity = nodeIdentity;
|
|
1145
1249
|
this._remoteSyncStateHelper.setNodeIdentity(nodeIdentity);
|
|
1250
|
+
this._serviceStarted = true;
|
|
1251
|
+
// If there are already storage keys registered, we need to activate them
|
|
1252
|
+
for (const storageKey in this._activeStorageKeys) {
|
|
1253
|
+
await this.activateStorageKey(storageKey);
|
|
1254
|
+
}
|
|
1146
1255
|
}
|
|
1147
1256
|
/**
|
|
1148
1257
|
* The component needs to be stopped when the node is closed.
|
|
@@ -1152,13 +1261,10 @@ class SynchronisedStorageService {
|
|
|
1152
1261
|
* @returns Nothing.
|
|
1153
1262
|
*/
|
|
1154
1263
|
async stop(nodeIdentity, nodeLoggingConnectorType, componentState) {
|
|
1155
|
-
for (const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
for (const schemaType in this._consolidationTimers) {
|
|
1160
|
-
clearTimeout(this._consolidationTimers[schemaType]);
|
|
1161
|
-
delete this._consolidationTimers[schemaType];
|
|
1264
|
+
for (const storageKey in this._activeStorageKeys) {
|
|
1265
|
+
this._activeStorageKeys[storageKey] = false;
|
|
1266
|
+
this._taskSchedulerComponent.removeTask(`synchronised-storage-update-${storageKey}`);
|
|
1267
|
+
this._taskSchedulerComponent.removeTask(`synchronised-storage-consolidation-${storageKey}`);
|
|
1162
1268
|
}
|
|
1163
1269
|
}
|
|
1164
1270
|
/**
|
|
@@ -1179,21 +1285,29 @@ class SynchronisedStorageService {
|
|
|
1179
1285
|
// This will be performed using rights-management
|
|
1180
1286
|
const changeSet = await this._changeSetHelper.getAndApplyChangeset(changeSetStorageId);
|
|
1181
1287
|
if (!core.Is.empty(changeSet)) {
|
|
1182
|
-
await this._remoteSyncStateHelper.addChangeSetToSyncState(changeSetStorageId);
|
|
1288
|
+
await this._remoteSyncStateHelper.addChangeSetToSyncState(changeSet.storageKey, changeSetStorageId);
|
|
1183
1289
|
}
|
|
1184
1290
|
}
|
|
1185
1291
|
/**
|
|
1186
1292
|
* Start the sync with further updates after an interval.
|
|
1187
|
-
* @param
|
|
1293
|
+
* @param storageKey The storage key to sync.
|
|
1188
1294
|
* @returns Nothing.
|
|
1189
1295
|
* @internal
|
|
1190
1296
|
*/
|
|
1191
|
-
async startEntitySync(
|
|
1297
|
+
async startEntitySync(storageKey) {
|
|
1192
1298
|
try {
|
|
1299
|
+
await this._logging?.log({
|
|
1300
|
+
level: "info",
|
|
1301
|
+
source: this.CLASS_NAME,
|
|
1302
|
+
message: "startEntitySync",
|
|
1303
|
+
data: {
|
|
1304
|
+
storageKey
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1193
1307
|
// First we check for remote changes
|
|
1194
|
-
await this.updateFromRemoteSyncState(
|
|
1308
|
+
await this.updateFromRemoteSyncState(storageKey);
|
|
1195
1309
|
// Now send any updates we have to the remote storage
|
|
1196
|
-
await this.updateFromLocalSyncState(
|
|
1310
|
+
await this.updateFromLocalSyncState(storageKey);
|
|
1197
1311
|
}
|
|
1198
1312
|
catch (error) {
|
|
1199
1313
|
await this._logging?.log({
|
|
@@ -1203,27 +1317,31 @@ class SynchronisedStorageService {
|
|
|
1203
1317
|
error: core.BaseError.fromError(error)
|
|
1204
1318
|
});
|
|
1205
1319
|
}
|
|
1206
|
-
finally {
|
|
1207
|
-
// Set a timer to check for updates again
|
|
1208
|
-
this._entityUpdateTimers[schemaType] = setTimeout(async () => this.startEntitySync(schemaType), this._config.entityUpdateIntervalMs);
|
|
1209
|
-
}
|
|
1210
1320
|
}
|
|
1211
1321
|
/**
|
|
1212
1322
|
* Check for updates in the remote storage.
|
|
1213
|
-
* @param
|
|
1323
|
+
* @param storageKey The storage key to check for updates.
|
|
1214
1324
|
* @returns Nothing.
|
|
1215
1325
|
* @internal
|
|
1216
1326
|
*/
|
|
1217
|
-
async updateFromRemoteSyncState(
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1327
|
+
async updateFromRemoteSyncState(storageKey) {
|
|
1328
|
+
await this._logging?.log({
|
|
1329
|
+
level: "info",
|
|
1330
|
+
source: this.CLASS_NAME,
|
|
1331
|
+
message: "updateFromRemoteSyncState",
|
|
1332
|
+
data: {
|
|
1333
|
+
storageKey
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
// Get the verifiable sync pointer store from the verifiable storage
|
|
1337
|
+
const verifiableSyncPointerStore = await this._remoteSyncStateHelper.getVerifiableSyncPointerStore();
|
|
1338
|
+
if (!core.Is.empty(verifiableSyncPointerStore.syncPointers[storageKey])) {
|
|
1221
1339
|
// Load the sync state from the remote blob storage using the sync pointer
|
|
1222
1340
|
// to load the sync state
|
|
1223
|
-
const remoteSyncState = await this._remoteSyncStateHelper.getRemoteSyncState(
|
|
1341
|
+
const remoteSyncState = await this._remoteSyncStateHelper.getRemoteSyncState(verifiableSyncPointerStore.syncPointers[storageKey]);
|
|
1224
1342
|
// If we got the sync state we can try and sync from it
|
|
1225
1343
|
if (!core.Is.undefined(remoteSyncState)) {
|
|
1226
|
-
await this._localSyncStateHelper.syncFromRemote(
|
|
1344
|
+
await this._localSyncStateHelper.syncFromRemote(storageKey, remoteSyncState);
|
|
1227
1345
|
}
|
|
1228
1346
|
}
|
|
1229
1347
|
}
|
|
@@ -1232,43 +1350,78 @@ class SynchronisedStorageService {
|
|
|
1232
1350
|
* @returns Nothing.
|
|
1233
1351
|
* @internal
|
|
1234
1352
|
*/
|
|
1235
|
-
async updateFromLocalSyncState(
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1353
|
+
async updateFromLocalSyncState(storageKey) {
|
|
1354
|
+
await this._logging?.log({
|
|
1355
|
+
level: "info",
|
|
1356
|
+
source: this.CLASS_NAME,
|
|
1357
|
+
message: "updateFromLocalSyncState",
|
|
1358
|
+
data: {
|
|
1359
|
+
storageKey
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
const localChangeSnapshot = await this._localSyncStateHelper.getLocalChangeSnapshot(storageKey);
|
|
1363
|
+
if (core.Is.arrayValue(localChangeSnapshot.changes)) {
|
|
1364
|
+
await this._remoteSyncStateHelper.createAndStoreChangeSet(storageKey, localChangeSnapshot.changes, async (changeSetStorageId) => {
|
|
1365
|
+
if (core.Is.stringValue(changeSetStorageId)) {
|
|
1366
|
+
await this._logging?.log({
|
|
1367
|
+
level: "info",
|
|
1368
|
+
source: this.CLASS_NAME,
|
|
1369
|
+
message: "createdStorageChangeSet",
|
|
1370
|
+
data: {
|
|
1371
|
+
storageKey,
|
|
1372
|
+
changeSetStorageId
|
|
1249
1373
|
}
|
|
1374
|
+
});
|
|
1375
|
+
// Send the local changes to the remote storage if we are a trusted node
|
|
1376
|
+
if (this._config.isTrustedNode) {
|
|
1377
|
+
await this._remoteSyncStateHelper.addChangeSetToSyncState(storageKey, changeSetStorageId);
|
|
1250
1378
|
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
1251
1379
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1380
|
+
else if (!core.Is.empty(this._trustedSynchronisedStorageComponent)) {
|
|
1381
|
+
// If we are not a trusted node, we need to send the changes to the trusted node
|
|
1382
|
+
await this._trustedSynchronisedStorageComponent.syncChangeSet(changeSetStorageId);
|
|
1383
|
+
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
await this._logging?.log({
|
|
1388
|
+
level: "info",
|
|
1389
|
+
source: this.CLASS_NAME,
|
|
1390
|
+
message: "createdStorageChangeSetNone",
|
|
1391
|
+
data: {
|
|
1392
|
+
storageKey
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
else {
|
|
1399
|
+
await this._logging?.log({
|
|
1400
|
+
level: "info",
|
|
1401
|
+
source: this.CLASS_NAME,
|
|
1402
|
+
message: "updateFromLocalSyncStateNoChanges",
|
|
1403
|
+
data: {
|
|
1404
|
+
storageKey
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1254
1407
|
}
|
|
1255
1408
|
}
|
|
1256
1409
|
/**
|
|
1257
1410
|
* Start the consolidation sync.
|
|
1258
|
-
* @param
|
|
1411
|
+
* @param storageKey The storage key to consolidate.
|
|
1259
1412
|
* @returns Nothing.
|
|
1260
1413
|
* @internal
|
|
1261
1414
|
*/
|
|
1262
|
-
async startConsolidationSync(
|
|
1415
|
+
async startConsolidationSync(storageKey) {
|
|
1263
1416
|
let localChangeSnapshot;
|
|
1264
1417
|
try {
|
|
1265
1418
|
// If we are performing a consolidation, we can remove the local changes
|
|
1266
|
-
await this._localSyncStateHelper.getLocalChangeSnapshot(
|
|
1419
|
+
await this._localSyncStateHelper.getLocalChangeSnapshot(storageKey);
|
|
1267
1420
|
if (!core.Is.empty(localChangeSnapshot)) {
|
|
1268
1421
|
await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
|
|
1269
1422
|
}
|
|
1270
1423
|
if (core.Is.stringValue(this._nodeIdentity)) {
|
|
1271
|
-
await this._remoteSyncStateHelper.consolidateFromLocal(
|
|
1424
|
+
await this._remoteSyncStateHelper.consolidateFromLocal(storageKey, this._config.consolidationBatchSize ??
|
|
1272
1425
|
SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE);
|
|
1273
1426
|
// The consolidation was successful, so we can remove the local change snapshot permanently
|
|
1274
1427
|
localChangeSnapshot = undefined;
|
|
@@ -1286,10 +1439,6 @@ class SynchronisedStorageService {
|
|
|
1286
1439
|
error: core.BaseError.fromError(error)
|
|
1287
1440
|
});
|
|
1288
1441
|
}
|
|
1289
|
-
finally {
|
|
1290
|
-
// Set a timer to perform the consolidation again
|
|
1291
|
-
this._consolidationTimers[schemaType] = setTimeout(async () => this.startConsolidationSync(schemaType), this._config.consolidationIntervalMs);
|
|
1292
|
-
}
|
|
1293
1442
|
}
|
|
1294
1443
|
/**
|
|
1295
1444
|
* Register a new sync type.
|
|
@@ -1302,14 +1451,48 @@ class SynchronisedStorageService {
|
|
|
1302
1451
|
source: this.CLASS_NAME,
|
|
1303
1452
|
message: "registerType",
|
|
1304
1453
|
data: {
|
|
1305
|
-
|
|
1454
|
+
storageKey: syncRegisterType.storageKey
|
|
1306
1455
|
}
|
|
1307
1456
|
});
|
|
1308
|
-
if (this.
|
|
1309
|
-
|
|
1457
|
+
if (core.Is.empty(this._activeStorageKeys[syncRegisterType.storageKey])) {
|
|
1458
|
+
this._activeStorageKeys[syncRegisterType.storageKey] = false;
|
|
1459
|
+
if (this._serviceStarted) {
|
|
1460
|
+
await this.activateStorageKey(syncRegisterType.storageKey);
|
|
1461
|
+
}
|
|
1310
1462
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Activate a storage key.
|
|
1466
|
+
* @param storageKey The storage key to activate.
|
|
1467
|
+
* @internal
|
|
1468
|
+
*/
|
|
1469
|
+
async activateStorageKey(storageKey) {
|
|
1470
|
+
if (!core.Is.empty(this._activeStorageKeys[storageKey]) && !this._activeStorageKeys[storageKey]) {
|
|
1471
|
+
await this._logging?.log({
|
|
1472
|
+
level: "info",
|
|
1473
|
+
source: this.CLASS_NAME,
|
|
1474
|
+
message: "activateType",
|
|
1475
|
+
data: {
|
|
1476
|
+
storageKey
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1479
|
+
this._activeStorageKeys[storageKey] = true;
|
|
1480
|
+
if (this._config.entityUpdateIntervalMinutes > 0) {
|
|
1481
|
+
await this._taskSchedulerComponent.addTask(`synchronised-storage-update-${storageKey}`, [
|
|
1482
|
+
{
|
|
1483
|
+
nextTriggerTime: Date.now(),
|
|
1484
|
+
intervalMinutes: this._config.entityUpdateIntervalMinutes
|
|
1485
|
+
}
|
|
1486
|
+
], async () => this.startEntitySync(storageKey));
|
|
1487
|
+
}
|
|
1488
|
+
if (this._config.isTrustedNode && this._config.consolidationIntervalMinutes > 0) {
|
|
1489
|
+
await this._taskSchedulerComponent.addTask(`synchronised-storage-consolidation-${storageKey}`, [
|
|
1490
|
+
{
|
|
1491
|
+
nextTriggerTime: Date.now(),
|
|
1492
|
+
intervalMinutes: this._config.consolidationIntervalMinutes
|
|
1493
|
+
}
|
|
1494
|
+
], async () => this.startConsolidationSync(storageKey));
|
|
1495
|
+
}
|
|
1313
1496
|
}
|
|
1314
1497
|
}
|
|
1315
1498
|
}
|