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