@aws-amplify/datastore 3.12.6-next.13 → 3.12.6-next.32
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/CHANGELOG.md +58 -0
- package/lib/authModeStrategies/multiAuthStrategy.js +17 -64
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.js +682 -469
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/predicates/index.js +12 -2
- package/lib/predicates/index.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.js +393 -298
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.js +97 -122
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.js +16 -67
- package/lib/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.native.js +2 -4
- package/lib/storage/adapter/InMemoryStore.native.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +497 -404
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/getDefaultAdapter/index.js +3 -5
- package/lib/storage/adapter/getDefaultAdapter/index.js.map +1 -1
- package/lib/storage/adapter/getDefaultAdapter/index.native.js +2 -4
- package/lib/storage/adapter/getDefaultAdapter/index.native.js.map +1 -1
- package/lib/storage/storage.js +129 -151
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/datastoreConnectivity.js +13 -17
- package/lib/sync/datastoreConnectivity.js.map +1 -1
- package/lib/sync/datastoreReachability/index.native.js +2 -4
- package/lib/sync/datastoreReachability/index.native.js.map +1 -1
- package/lib/sync/index.js +544 -488
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.js +21 -80
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.js +95 -162
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/errorMaps.js +4 -34
- package/lib/sync/processors/errorMaps.js.map +1 -1
- package/lib/sync/processors/mutation.js +285 -312
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js +218 -259
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js +141 -212
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.js +50 -61
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.js +13 -39
- package/lib/types.js.map +1 -1
- package/lib/util.js +429 -242
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +13 -57
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +107 -17
- package/lib-esm/datastore/datastore.js +649 -433
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/index.d.ts +3 -19
- package/lib-esm/predicates/index.d.ts +3 -2
- package/lib-esm/predicates/index.js +13 -3
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +356 -258
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +67 -92
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/InMemoryStore.js +1 -52
- package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +26 -4
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +446 -346
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/index.d.ts +1 -1
- package/lib-esm/storage/storage.d.ts +1 -1
- package/lib-esm/storage/storage.js +94 -113
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
- package/lib-esm/sync/datastoreConnectivity.js +10 -11
- package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
- package/lib-esm/sync/index.d.ts +31 -5
- package/lib-esm/sync/index.js +525 -466
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +9 -3
- package/lib-esm/sync/merger.js +14 -73
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.d.ts +2 -2
- package/lib-esm/sync/outbox.js +79 -146
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/errorMaps.js +1 -31
- package/lib-esm/sync/processors/errorMaps.js.map +1 -1
- package/lib-esm/sync/processors/mutation.d.ts +2 -0
- package/lib-esm/sync/processors/mutation.js +271 -295
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +2 -0
- package/lib-esm/sync/processors/subscription.js +214 -245
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +2 -1
- package/lib-esm/sync/processors/sync.js +127 -195
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +3 -2
- package/lib-esm/sync/utils.js +45 -57
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +65 -26
- package/lib-esm/types.js +10 -38
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +67 -24
- package/lib-esm/util.js +420 -233
- package/lib-esm/util.js.map +1 -1
- package/package.json +14 -7
- package/src/authModeStrategies/multiAuthStrategy.ts +12 -1
- package/src/datastore/datastore.ts +798 -397
- package/src/predicates/index.ts +32 -10
- package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
- package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
- package/src/storage/adapter/IndexedDBAdapter.ts +358 -134
- package/src/storage/adapter/index.ts +1 -1
- package/src/storage/storage.ts +69 -22
- package/src/sync/datastoreConnectivity.ts +6 -0
- package/src/sync/index.ts +521 -412
- package/src/sync/merger.ts +20 -4
- package/src/sync/outbox.ts +22 -9
- package/src/sync/processors/mutation.ts +188 -150
- package/src/sync/processors/subscription.ts +289 -253
- package/src/sync/processors/sync.ts +151 -138
- package/src/sync/utils.ts +67 -12
- package/src/types.ts +182 -30
- package/src/util.ts +505 -176
- package/build.js +0 -5
- package/dist/aws-amplify-datastore.js +0 -98255
- package/dist/aws-amplify-datastore.js.map +0 -1
- package/dist/aws-amplify-datastore.min.js +0 -66
- package/dist/aws-amplify-datastore.min.js.map +0 -1
- package/index.js +0 -7
- package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
- package/lib/authModeStrategies/index.d.ts +0 -2
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -2
- package/lib/datastore/datastore.d.ts +0 -66
- package/lib/index.d.ts +0 -31
- package/lib/predicates/index.d.ts +0 -15
- package/lib/predicates/sort.d.ts +0 -8
- package/lib/ssr/index.d.ts +0 -3
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -40
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -29
- package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
- package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -37
- package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
- package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
- package/lib/storage/adapter/index.d.ts +0 -9
- package/lib/storage/storage.d.ts +0 -49
- package/lib/sync/datastoreConnectivity.d.ts +0 -15
- package/lib/sync/datastoreReachability/index.d.ts +0 -3
- package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
- package/lib/sync/index.d.ts +0 -63
- package/lib/sync/merger.d.ts +0 -11
- package/lib/sync/outbox.d.ts +0 -27
- package/lib/sync/processors/errorMaps.d.ts +0 -17
- package/lib/sync/processors/mutation.d.ts +0 -56
- package/lib/sync/processors/subscription.d.ts +0 -31
- package/lib/sync/processors/sync.d.ts +0 -27
- package/lib/sync/utils.d.ts +0 -41
- package/lib/types.d.ts +0 -462
- package/lib/util.d.ts +0 -113
- package/webpack.config.dev.js +0 -6
package/src/sync/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
browserOrNode,
|
|
3
|
+
ConsoleLogger as Logger,
|
|
4
|
+
BackgroundProcessManager,
|
|
5
|
+
} from '@aws-amplify/core';
|
|
2
6
|
import { CONTROL_MSG as PUBSUB_CONTROL_MSG } from '@aws-amplify/pubsub';
|
|
3
7
|
import Observable, { ZenObservable } from 'zen-observable-ts';
|
|
4
8
|
import { ModelInstanceCreator } from '../datastore/datastore';
|
|
@@ -21,8 +25,13 @@ import {
|
|
|
21
25
|
TypeConstructorMap,
|
|
22
26
|
ModelPredicate,
|
|
23
27
|
AuthModeStrategy,
|
|
28
|
+
ManagedIdentifier,
|
|
29
|
+
OptionallyManagedIdentifier,
|
|
24
30
|
AmplifyContext,
|
|
25
31
|
} from '../types';
|
|
32
|
+
// tslint:disable:no-duplicate-imports
|
|
33
|
+
import type { __modelMeta__ } from '../types';
|
|
34
|
+
|
|
26
35
|
import { exhaustiveCheck, getNow, SYNC, USER } from '../util';
|
|
27
36
|
import DataStoreConnectivity from './datastoreConnectivity';
|
|
28
37
|
import { ModelMerger } from './merger';
|
|
@@ -32,6 +41,7 @@ import { CONTROL_MSG, SubscriptionProcessor } from './processors/subscription';
|
|
|
32
41
|
import { SyncProcessor } from './processors/sync';
|
|
33
42
|
import {
|
|
34
43
|
createMutationInstanceFromModelOperation,
|
|
44
|
+
getIdentifierValue,
|
|
35
45
|
predicateToGraphQLCondition,
|
|
36
46
|
TransformerMutationType,
|
|
37
47
|
} from './utils';
|
|
@@ -46,25 +56,26 @@ type StartParams = {
|
|
|
46
56
|
};
|
|
47
57
|
|
|
48
58
|
export declare class MutationEvent {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
mutator: (draft: MutableModel<MutationEvent>) => void | MutationEvent
|
|
53
|
-
): MutationEvent;
|
|
59
|
+
readonly [__modelMeta__]: {
|
|
60
|
+
identifier: OptionallyManagedIdentifier<MutationEvent, 'id'>;
|
|
61
|
+
};
|
|
54
62
|
public readonly id: string;
|
|
55
63
|
public readonly model: string;
|
|
56
64
|
public readonly operation: TransformerMutationType;
|
|
57
65
|
public readonly modelId: string;
|
|
58
66
|
public readonly condition: string;
|
|
59
|
-
public data: string;
|
|
67
|
+
public readonly data: string;
|
|
68
|
+
constructor(init: ModelInit<MutationEvent>);
|
|
69
|
+
static copyOf(
|
|
70
|
+
src: MutationEvent,
|
|
71
|
+
mutator: (draft: MutableModel<MutationEvent>) => void | MutationEvent
|
|
72
|
+
): MutationEvent;
|
|
60
73
|
}
|
|
61
74
|
|
|
62
|
-
declare class ModelMetadata {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
mutator: (draft: MutableModel<ModelMetadata>) => void | ModelMetadata
|
|
67
|
-
): ModelMetadata;
|
|
75
|
+
export declare class ModelMetadata {
|
|
76
|
+
readonly [__modelMeta__]: {
|
|
77
|
+
identifier: ManagedIdentifier<ModelMetadata, 'id'>;
|
|
78
|
+
};
|
|
68
79
|
public readonly id: string;
|
|
69
80
|
public readonly namespace: string;
|
|
70
81
|
public readonly model: string;
|
|
@@ -72,6 +83,11 @@ declare class ModelMetadata {
|
|
|
72
83
|
public readonly lastSync?: number;
|
|
73
84
|
public readonly lastFullSync?: number;
|
|
74
85
|
public readonly lastSyncPredicate?: null | string;
|
|
86
|
+
constructor(init: ModelInit<ModelMetadata>);
|
|
87
|
+
static copyOf(
|
|
88
|
+
src: ModelMetadata,
|
|
89
|
+
mutator: (draft: MutableModel<ModelMetadata>) => void | ModelMetadata
|
|
90
|
+
): ModelMetadata;
|
|
75
91
|
}
|
|
76
92
|
|
|
77
93
|
export enum ControlMessage {
|
|
@@ -101,6 +117,8 @@ export class SyncEngine {
|
|
|
101
117
|
boolean
|
|
102
118
|
> = new WeakMap();
|
|
103
119
|
|
|
120
|
+
private runningProcesses: BackgroundProcessManager;
|
|
121
|
+
|
|
104
122
|
public getModelSyncedStatus(
|
|
105
123
|
modelConstructor: PersistentModelConstructor<any>
|
|
106
124
|
): boolean {
|
|
@@ -119,11 +137,14 @@ export class SyncEngine {
|
|
|
119
137
|
private readonly syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>>,
|
|
120
138
|
private readonly amplifyConfig: Record<string, any> = {},
|
|
121
139
|
private readonly authModeStrategy: AuthModeStrategy,
|
|
122
|
-
private readonly amplifyContext: AmplifyContext
|
|
140
|
+
private readonly amplifyContext: AmplifyContext,
|
|
141
|
+
private readonly connectivityMonitor?: DataStoreConnectivity
|
|
123
142
|
) {
|
|
143
|
+
this.runningProcesses = new BackgroundProcessManager();
|
|
144
|
+
|
|
124
145
|
const MutationEvent = this.modelClasses[
|
|
125
146
|
'MutationEvent'
|
|
126
|
-
] as PersistentModelConstructor<
|
|
147
|
+
] as PersistentModelConstructor<MutationEvent>;
|
|
127
148
|
|
|
128
149
|
this.outbox = new MutationEventOutbox(
|
|
129
150
|
this.schema,
|
|
@@ -166,7 +187,8 @@ export class SyncEngine {
|
|
|
166
187
|
this.amplifyContext
|
|
167
188
|
);
|
|
168
189
|
|
|
169
|
-
this.datastoreConnectivity =
|
|
190
|
+
this.datastoreConnectivity =
|
|
191
|
+
this.connectivityMonitor || new DataStoreConnectivity();
|
|
170
192
|
}
|
|
171
193
|
|
|
172
194
|
start(params: StartParams) {
|
|
@@ -175,7 +197,7 @@ export class SyncEngine {
|
|
|
175
197
|
|
|
176
198
|
let subscriptions: ZenObservable.Subscription[] = [];
|
|
177
199
|
|
|
178
|
-
(async () => {
|
|
200
|
+
this.runningProcesses.add(async () => {
|
|
179
201
|
try {
|
|
180
202
|
await this.setupModels(params);
|
|
181
203
|
} catch (err) {
|
|
@@ -183,230 +205,259 @@ export class SyncEngine {
|
|
|
183
205
|
return;
|
|
184
206
|
}
|
|
185
207
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
>;
|
|
203
|
-
|
|
204
|
-
if (isNode) {
|
|
205
|
-
logger.warn(
|
|
206
|
-
'Realtime disabled when in a server-side environment'
|
|
207
|
-
);
|
|
208
|
-
} else {
|
|
209
|
-
//#region GraphQL Subscriptions
|
|
210
|
-
[
|
|
211
|
-
// const ctlObservable: Observable<CONTROL_MSG>
|
|
212
|
-
ctlSubsObservable,
|
|
213
|
-
// const dataObservable: Observable<[TransformerMutationType, SchemaModel, Readonly<{
|
|
214
|
-
// id: string;
|
|
215
|
-
// } & Record<string, any>>]>
|
|
216
|
-
dataSubsObservable,
|
|
217
|
-
] = this.subscriptionsProcessor.start();
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
await new Promise((resolve, reject) => {
|
|
221
|
-
const ctlSubsSubscription = ctlSubsObservable.subscribe({
|
|
222
|
-
next: msg => {
|
|
223
|
-
if (msg === CONTROL_MSG.CONNECTED) {
|
|
224
|
-
resolve();
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
error: err => {
|
|
228
|
-
reject(err);
|
|
229
|
-
const handleDisconnect = this.disconnectionHandler();
|
|
230
|
-
handleDisconnect(err);
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
subscriptions.push(ctlSubsSubscription);
|
|
208
|
+
// this is awaited at the bottom. so, we don't need to register
|
|
209
|
+
// this explicitly with the context. it's already contained.
|
|
210
|
+
const startPromise = new Promise((doneStarting, failedStarting) => {
|
|
211
|
+
this.datastoreConnectivity.status().subscribe(
|
|
212
|
+
async ({ online }) =>
|
|
213
|
+
this.runningProcesses.isOpen &&
|
|
214
|
+
this.runningProcesses.add(async onTerminate => {
|
|
215
|
+
// From offline to online
|
|
216
|
+
if (online && !this.online) {
|
|
217
|
+
this.online = online;
|
|
218
|
+
|
|
219
|
+
observer.next({
|
|
220
|
+
type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS,
|
|
221
|
+
data: {
|
|
222
|
+
active: this.online,
|
|
223
|
+
},
|
|
235
224
|
});
|
|
236
|
-
} catch (err) {
|
|
237
|
-
observer.error(err);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
logger.log('Realtime ready');
|
|
242
225
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
226
|
+
let ctlSubsObservable: Observable<CONTROL_MSG>;
|
|
227
|
+
let dataSubsObservable: Observable<
|
|
228
|
+
[TransformerMutationType, SchemaModel, PersistentModel]
|
|
229
|
+
>;
|
|
246
230
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
await new Promise((resolve, reject) => {
|
|
253
|
-
const syncQuerySubscription =
|
|
254
|
-
this.syncQueriesObservable().subscribe({
|
|
255
|
-
next: message => {
|
|
256
|
-
const { type } = message;
|
|
257
|
-
|
|
258
|
-
if (
|
|
259
|
-
type === ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY
|
|
260
|
-
) {
|
|
261
|
-
resolve();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
observer.next(message);
|
|
265
|
-
},
|
|
266
|
-
complete: () => {
|
|
267
|
-
resolve();
|
|
268
|
-
},
|
|
269
|
-
error: error => {
|
|
270
|
-
reject(error);
|
|
271
|
-
},
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
if (syncQuerySubscription) {
|
|
275
|
-
subscriptions.push(syncQuerySubscription);
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
} catch (error) {
|
|
279
|
-
observer.error(error);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
//#endregion
|
|
283
|
-
|
|
284
|
-
//#region process mutations
|
|
285
|
-
subscriptions.push(
|
|
286
|
-
this.mutationsProcessor
|
|
287
|
-
.start()
|
|
288
|
-
.subscribe(({ modelDefinition, model: item, hasMore }) => {
|
|
289
|
-
const modelConstructor = this.userModelClasses[
|
|
290
|
-
modelDefinition.name
|
|
291
|
-
] as PersistentModelConstructor<any>;
|
|
292
|
-
|
|
293
|
-
const model = this.modelInstanceCreator(
|
|
294
|
-
modelConstructor,
|
|
295
|
-
item
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
this.storage.runExclusive(storage =>
|
|
299
|
-
this.modelMerger.merge(storage, model)
|
|
231
|
+
// NOTE: need a way to override this conditional for testing.
|
|
232
|
+
if (isNode) {
|
|
233
|
+
logger.warn(
|
|
234
|
+
'Realtime disabled when in a server-side environment'
|
|
300
235
|
);
|
|
236
|
+
} else {
|
|
237
|
+
//#region GraphQL Subscriptions
|
|
238
|
+
[
|
|
239
|
+
// const ctlObservable: Observable<CONTROL_MSG>
|
|
240
|
+
ctlSubsObservable,
|
|
241
|
+
// const dataObservable: Observable<[TransformerMutationType, SchemaModel, Readonly<{
|
|
242
|
+
// id: string;
|
|
243
|
+
// } & Record<string, any>>]>
|
|
244
|
+
dataSubsObservable,
|
|
245
|
+
] = this.subscriptionsProcessor.start();
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
await new Promise((resolve, reject) => {
|
|
249
|
+
onTerminate.then(reject);
|
|
250
|
+
const ctlSubsSubscription = ctlSubsObservable.subscribe(
|
|
251
|
+
{
|
|
252
|
+
next: msg => {
|
|
253
|
+
if (msg === CONTROL_MSG.CONNECTED) {
|
|
254
|
+
resolve();
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
error: err => {
|
|
258
|
+
reject(err);
|
|
259
|
+
const handleDisconnect =
|
|
260
|
+
this.disconnectionHandler();
|
|
261
|
+
handleDisconnect(err);
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
subscriptions.push(ctlSubsSubscription);
|
|
267
|
+
});
|
|
268
|
+
} catch (err) {
|
|
269
|
+
observer.error(err);
|
|
270
|
+
failedStarting();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
301
273
|
|
|
302
|
-
|
|
303
|
-
type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,
|
|
304
|
-
data: {
|
|
305
|
-
model: modelConstructor,
|
|
306
|
-
element: model,
|
|
307
|
-
},
|
|
308
|
-
});
|
|
274
|
+
logger.log('Realtime ready');
|
|
309
275
|
|
|
310
276
|
observer.next({
|
|
311
|
-
type: ControlMessage.
|
|
312
|
-
data: {
|
|
313
|
-
isEmpty: !hasMore,
|
|
314
|
-
},
|
|
277
|
+
type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED,
|
|
315
278
|
});
|
|
316
|
-
})
|
|
317
|
-
);
|
|
318
|
-
//#endregion
|
|
319
|
-
|
|
320
|
-
//#region Merge subscriptions buffer
|
|
321
|
-
// TODO: extract to function
|
|
322
|
-
if (!isNode) {
|
|
323
|
-
subscriptions.push(
|
|
324
|
-
dataSubsObservable.subscribe(
|
|
325
|
-
([_transformerMutationType, modelDefinition, item]) => {
|
|
326
|
-
const modelConstructor = this.userModelClasses[
|
|
327
|
-
modelDefinition.name
|
|
328
|
-
] as PersistentModelConstructor<any>;
|
|
329
|
-
|
|
330
|
-
const model = this.modelInstanceCreator(
|
|
331
|
-
modelConstructor,
|
|
332
|
-
item
|
|
333
|
-
);
|
|
334
279
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
)
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
//#endregion
|
|
343
|
-
} else if (!online) {
|
|
344
|
-
this.online = online;
|
|
280
|
+
//#endregion
|
|
281
|
+
}
|
|
345
282
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
283
|
+
//#region Base & Sync queries
|
|
284
|
+
try {
|
|
285
|
+
await new Promise((resolve, reject) => {
|
|
286
|
+
const syncQuerySubscription =
|
|
287
|
+
this.syncQueriesObservable().subscribe({
|
|
288
|
+
next: message => {
|
|
289
|
+
const { type } = message;
|
|
290
|
+
|
|
291
|
+
if (
|
|
292
|
+
type ===
|
|
293
|
+
ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY
|
|
294
|
+
) {
|
|
295
|
+
resolve();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
observer.next(message);
|
|
299
|
+
},
|
|
300
|
+
complete: () => {
|
|
301
|
+
resolve();
|
|
302
|
+
},
|
|
303
|
+
error: error => {
|
|
304
|
+
reject(error);
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if (syncQuerySubscription) {
|
|
309
|
+
subscriptions.push(syncQuerySubscription);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
} catch (error) {
|
|
313
|
+
observer.error(error);
|
|
314
|
+
failedStarting();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
|
|
319
|
+
//#region process mutations (outbox)
|
|
320
|
+
subscriptions.push(
|
|
321
|
+
this.mutationsProcessor
|
|
322
|
+
.start()
|
|
323
|
+
.subscribe(({ modelDefinition, model: item, hasMore }) =>
|
|
324
|
+
this.runningProcesses.add(async () => {
|
|
325
|
+
const modelConstructor = this.userModelClasses[
|
|
326
|
+
modelDefinition.name
|
|
327
|
+
] as PersistentModelConstructor<any>;
|
|
328
|
+
|
|
329
|
+
const model = this.modelInstanceCreator(
|
|
330
|
+
modelConstructor,
|
|
331
|
+
item
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
await this.storage.runExclusive(storage =>
|
|
335
|
+
this.modelMerger.merge(
|
|
336
|
+
storage,
|
|
337
|
+
model,
|
|
338
|
+
modelDefinition
|
|
339
|
+
)
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
observer.next({
|
|
343
|
+
type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,
|
|
344
|
+
data: {
|
|
345
|
+
model: modelConstructor,
|
|
346
|
+
element: model,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
observer.next({
|
|
351
|
+
type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
|
|
352
|
+
data: {
|
|
353
|
+
isEmpty: !hasMore,
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}, 'mutation processor event')
|
|
357
|
+
)
|
|
358
|
+
);
|
|
359
|
+
//#endregion
|
|
360
|
+
|
|
361
|
+
//#region Merge subscriptions buffer
|
|
362
|
+
// TODO: extract to function
|
|
363
|
+
if (!isNode) {
|
|
364
|
+
subscriptions.push(
|
|
365
|
+
dataSubsObservable.subscribe(
|
|
366
|
+
([_transformerMutationType, modelDefinition, item]) =>
|
|
367
|
+
this.runningProcesses.add(async () => {
|
|
368
|
+
const modelConstructor = this.userModelClasses[
|
|
369
|
+
modelDefinition.name
|
|
370
|
+
] as PersistentModelConstructor<any>;
|
|
371
|
+
|
|
372
|
+
const model = this.modelInstanceCreator(
|
|
373
|
+
modelConstructor,
|
|
374
|
+
item
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
await this.storage.runExclusive(storage =>
|
|
378
|
+
this.modelMerger.merge(
|
|
379
|
+
storage,
|
|
380
|
+
model,
|
|
381
|
+
modelDefinition
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
}, 'subscription dataSubsObservable event')
|
|
385
|
+
)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
//#endregion
|
|
389
|
+
} else if (!online) {
|
|
390
|
+
this.online = online;
|
|
391
|
+
|
|
392
|
+
observer.next({
|
|
393
|
+
type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS,
|
|
394
|
+
data: {
|
|
395
|
+
active: this.online,
|
|
396
|
+
},
|
|
397
|
+
});
|
|
352
398
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
399
|
+
subscriptions.forEach(sub => sub.unsubscribe());
|
|
400
|
+
subscriptions = [];
|
|
401
|
+
}
|
|
356
402
|
|
|
357
|
-
|
|
358
|
-
|
|
403
|
+
doneStarting();
|
|
404
|
+
}, 'datastore connectivity event')
|
|
405
|
+
);
|
|
359
406
|
});
|
|
360
407
|
|
|
361
408
|
this.storage
|
|
362
409
|
.observe(null, null, ownSymbol)
|
|
363
410
|
.filter(({ model }) => {
|
|
364
411
|
const modelDefinition = this.getModelDefinition(model);
|
|
365
|
-
|
|
366
412
|
return modelDefinition.syncable === true;
|
|
367
413
|
})
|
|
368
414
|
.subscribe({
|
|
369
|
-
next: async ({ opType, model, element, condition }) =>
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
await this.outbox.enqueue(this.storage, mutationEvent);
|
|
388
|
-
|
|
389
|
-
observer.next({
|
|
390
|
-
type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_ENQUEUED,
|
|
391
|
-
data: {
|
|
415
|
+
next: async ({ opType, model, element, condition }) =>
|
|
416
|
+
this.runningProcesses.add(async () => {
|
|
417
|
+
const namespace =
|
|
418
|
+
this.schema.namespaces[this.namespaceResolver(model)];
|
|
419
|
+
const MutationEventConstructor = this.modelClasses[
|
|
420
|
+
'MutationEvent'
|
|
421
|
+
] as PersistentModelConstructor<MutationEvent>;
|
|
422
|
+
const modelDefinition = this.getModelDefinition(model);
|
|
423
|
+
const graphQLCondition = predicateToGraphQLCondition(
|
|
424
|
+
condition,
|
|
425
|
+
modelDefinition
|
|
426
|
+
);
|
|
427
|
+
const mutationEvent = createMutationInstanceFromModelOperation(
|
|
428
|
+
namespace.relationships,
|
|
429
|
+
this.getModelDefinition(model),
|
|
430
|
+
opType,
|
|
392
431
|
model,
|
|
393
432
|
element,
|
|
394
|
-
|
|
395
|
-
|
|
433
|
+
graphQLCondition,
|
|
434
|
+
MutationEventConstructor,
|
|
435
|
+
this.modelInstanceCreator
|
|
436
|
+
);
|
|
396
437
|
|
|
397
|
-
|
|
398
|
-
type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
|
|
399
|
-
data: {
|
|
400
|
-
isEmpty: false,
|
|
401
|
-
},
|
|
402
|
-
});
|
|
438
|
+
await this.outbox.enqueue(this.storage, mutationEvent);
|
|
403
439
|
|
|
404
|
-
|
|
440
|
+
observer.next({
|
|
441
|
+
type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_ENQUEUED,
|
|
442
|
+
data: {
|
|
443
|
+
model,
|
|
444
|
+
element,
|
|
445
|
+
},
|
|
446
|
+
});
|
|
405
447
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
448
|
+
observer.next({
|
|
449
|
+
type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
|
|
450
|
+
data: {
|
|
451
|
+
isEmpty: false,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
await startPromise;
|
|
456
|
+
|
|
457
|
+
if (this.online) {
|
|
458
|
+
this.mutationsProcessor.resume();
|
|
459
|
+
}
|
|
460
|
+
}, 'storage event'),
|
|
410
461
|
});
|
|
411
462
|
|
|
412
463
|
observer.next({
|
|
@@ -427,11 +478,7 @@ export class SyncEngine {
|
|
|
427
478
|
observer.next({
|
|
428
479
|
type: ControlMessage.SYNC_ENGINE_READY,
|
|
429
480
|
});
|
|
430
|
-
})
|
|
431
|
-
|
|
432
|
-
return () => {
|
|
433
|
-
subscriptions.forEach(sub => sub.unsubscribe());
|
|
434
|
-
};
|
|
481
|
+
}, 'sync start');
|
|
435
482
|
});
|
|
436
483
|
}
|
|
437
484
|
|
|
@@ -439,7 +486,12 @@ export class SyncEngine {
|
|
|
439
486
|
currentTimeStamp: number
|
|
440
487
|
): Promise<Map<SchemaModel, [string, number]>> {
|
|
441
488
|
const modelLastSync: Map<SchemaModel, [string, number]> = new Map(
|
|
442
|
-
(
|
|
489
|
+
(
|
|
490
|
+
await this.runningProcesses.add(
|
|
491
|
+
() => this.getModelsMetadata(),
|
|
492
|
+
'sync/index getModelsMetadataWithNextFullSync'
|
|
493
|
+
)
|
|
494
|
+
).map(
|
|
443
495
|
({
|
|
444
496
|
namespace,
|
|
445
497
|
model,
|
|
@@ -474,220 +526,249 @@ export class SyncEngine {
|
|
|
474
526
|
|
|
475
527
|
return new Observable<ControlMessageType<ControlMessage>>(observer => {
|
|
476
528
|
let syncQueriesSubscription: ZenObservable.Subscription;
|
|
477
|
-
let waitTimeoutId: ReturnType<typeof setTimeout>;
|
|
478
|
-
|
|
479
|
-
(async () => {
|
|
480
|
-
while (!observer.closed) {
|
|
481
|
-
const count: WeakMap<
|
|
482
|
-
PersistentModelConstructor<any>,
|
|
483
|
-
{
|
|
484
|
-
new: number;
|
|
485
|
-
updated: number;
|
|
486
|
-
deleted: number;
|
|
487
|
-
}
|
|
488
|
-
> = new WeakMap();
|
|
489
529
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
530
|
+
this.runningProcesses.isOpen &&
|
|
531
|
+
this.runningProcesses.add(async onTerminate => {
|
|
532
|
+
let terminated = false;
|
|
533
|
+
|
|
534
|
+
while (!observer.closed && !terminated) {
|
|
535
|
+
const count: WeakMap<
|
|
536
|
+
PersistentModelConstructor<any>,
|
|
537
|
+
{
|
|
538
|
+
new: number;
|
|
539
|
+
updated: number;
|
|
540
|
+
deleted: number;
|
|
541
|
+
}
|
|
542
|
+
> = new WeakMap();
|
|
543
|
+
|
|
544
|
+
const modelLastSync = await this.getModelsMetadataWithNextFullSync(
|
|
545
|
+
Date.now()
|
|
546
|
+
);
|
|
547
|
+
const paginatingModels = new Set(modelLastSync.keys());
|
|
548
|
+
|
|
549
|
+
let newestFullSyncStartedAt: number;
|
|
550
|
+
let theInterval: number;
|
|
551
|
+
|
|
552
|
+
let start: number;
|
|
553
|
+
let duration: number;
|
|
554
|
+
let newestStartedAt: number;
|
|
555
|
+
await new Promise((resolve, reject) => {
|
|
556
|
+
if (!this.runningProcesses.isOpen) resolve();
|
|
557
|
+
onTerminate.then(() => resolve());
|
|
558
|
+
syncQueriesSubscription = this.syncQueriesProcessor
|
|
559
|
+
.start(modelLastSync)
|
|
560
|
+
.subscribe({
|
|
561
|
+
next: async ({
|
|
562
|
+
namespace,
|
|
563
|
+
modelDefinition,
|
|
564
|
+
items,
|
|
565
|
+
done,
|
|
566
|
+
startedAt,
|
|
567
|
+
isFullSync,
|
|
568
|
+
}) => {
|
|
569
|
+
const modelConstructor = this.userModelClasses[
|
|
570
|
+
modelDefinition.name
|
|
571
|
+
] as PersistentModelConstructor<any>;
|
|
523
572
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
:
|
|
529
|
-
|
|
573
|
+
if (!count.has(modelConstructor)) {
|
|
574
|
+
count.set(modelConstructor, {
|
|
575
|
+
new: 0,
|
|
576
|
+
updated: 0,
|
|
577
|
+
deleted: 0,
|
|
578
|
+
});
|
|
530
579
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const oneByOne: ModelInstanceMetadata[] = [];
|
|
539
|
-
const page = items.filter(item => {
|
|
540
|
-
if (!idsInOutbox.has(item.id)) {
|
|
541
|
-
return true;
|
|
542
|
-
}
|
|
580
|
+
start = getNow();
|
|
581
|
+
newestStartedAt =
|
|
582
|
+
newestStartedAt === undefined
|
|
583
|
+
? startedAt
|
|
584
|
+
: Math.max(newestStartedAt, startedAt);
|
|
585
|
+
}
|
|
543
586
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
587
|
+
/**
|
|
588
|
+
* If there are mutations in the outbox for a given id, those need to be
|
|
589
|
+
* merged individually. Otherwise, we can merge them in batches.
|
|
590
|
+
*/
|
|
591
|
+
await this.storage.runExclusive(async storage => {
|
|
592
|
+
const idsInOutbox = await this.outbox.getModelIds(
|
|
593
|
+
storage
|
|
594
|
+
);
|
|
547
595
|
|
|
548
|
-
|
|
596
|
+
const oneByOne: ModelInstanceMetadata[] = [];
|
|
597
|
+
const page = items.filter(item => {
|
|
598
|
+
const itemId = getIdentifierValue(
|
|
599
|
+
modelDefinition,
|
|
600
|
+
item
|
|
601
|
+
);
|
|
549
602
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
item
|
|
554
|
-
);
|
|
603
|
+
if (!idsInOutbox.has(itemId)) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
555
606
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
}
|
|
607
|
+
oneByOne.push(item);
|
|
608
|
+
return false;
|
|
609
|
+
});
|
|
560
610
|
|
|
561
|
-
|
|
562
|
-
...(await this.modelMerger.mergePage(
|
|
563
|
-
storage,
|
|
564
|
-
modelConstructor,
|
|
565
|
-
page
|
|
566
|
-
))
|
|
567
|
-
);
|
|
611
|
+
const opTypeCount: [any, OpType][] = [];
|
|
568
612
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
break;
|
|
576
|
-
case OpType.UPDATE:
|
|
577
|
-
counts.updated++;
|
|
578
|
-
break;
|
|
579
|
-
case OpType.DELETE:
|
|
580
|
-
counts.deleted++;
|
|
581
|
-
break;
|
|
582
|
-
default:
|
|
583
|
-
exhaustiveCheck(opType);
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
});
|
|
613
|
+
for (const item of oneByOne) {
|
|
614
|
+
const opType = await this.modelMerger.merge(
|
|
615
|
+
storage,
|
|
616
|
+
item,
|
|
617
|
+
modelDefinition
|
|
618
|
+
);
|
|
587
619
|
|
|
588
|
-
|
|
589
|
-
|
|
620
|
+
if (opType !== undefined) {
|
|
621
|
+
opTypeCount.push([item, opType]);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
590
624
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
625
|
+
opTypeCount.push(
|
|
626
|
+
...(await this.modelMerger.mergePage(
|
|
627
|
+
storage,
|
|
628
|
+
modelConstructor,
|
|
629
|
+
page,
|
|
630
|
+
modelDefinition
|
|
631
|
+
))
|
|
632
|
+
);
|
|
596
633
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
draft.lastFullSync = isFullSync
|
|
615
|
-
? startedAt
|
|
616
|
-
: modelMetadata.lastFullSync;
|
|
634
|
+
const counts = count.get(modelConstructor);
|
|
635
|
+
|
|
636
|
+
opTypeCount.forEach(([, opType]) => {
|
|
637
|
+
switch (opType) {
|
|
638
|
+
case OpType.INSERT:
|
|
639
|
+
counts.new++;
|
|
640
|
+
break;
|
|
641
|
+
case OpType.UPDATE:
|
|
642
|
+
counts.updated++;
|
|
643
|
+
break;
|
|
644
|
+
case OpType.DELETE:
|
|
645
|
+
counts.deleted++;
|
|
646
|
+
break;
|
|
647
|
+
default:
|
|
648
|
+
exhaustiveCheck(opType);
|
|
649
|
+
}
|
|
650
|
+
});
|
|
617
651
|
});
|
|
618
652
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
undefined,
|
|
622
|
-
ownSymbol
|
|
623
|
-
);
|
|
624
|
-
//#endregion
|
|
653
|
+
if (done) {
|
|
654
|
+
const { name: modelName } = modelDefinition;
|
|
625
655
|
|
|
626
|
-
|
|
656
|
+
//#region update last sync for type
|
|
657
|
+
let modelMetadata = await this.getModelMetadata(
|
|
658
|
+
namespace,
|
|
659
|
+
modelName
|
|
660
|
+
);
|
|
627
661
|
|
|
628
|
-
|
|
662
|
+
const { lastFullSync, fullSyncInterval } = modelMetadata;
|
|
663
|
+
|
|
664
|
+
theInterval = fullSyncInterval;
|
|
665
|
+
|
|
666
|
+
newestFullSyncStartedAt =
|
|
667
|
+
newestFullSyncStartedAt === undefined
|
|
668
|
+
? lastFullSync
|
|
669
|
+
: Math.max(
|
|
670
|
+
newestFullSyncStartedAt,
|
|
671
|
+
isFullSync ? startedAt : lastFullSync
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
modelMetadata = (
|
|
675
|
+
this.modelClasses
|
|
676
|
+
.ModelMetadata as PersistentModelConstructor<ModelMetadata>
|
|
677
|
+
).copyOf(modelMetadata, draft => {
|
|
678
|
+
draft.lastSync = startedAt;
|
|
679
|
+
draft.lastFullSync = isFullSync
|
|
680
|
+
? startedAt
|
|
681
|
+
: modelMetadata.lastFullSync;
|
|
682
|
+
});
|
|
629
683
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
counts,
|
|
637
|
-
},
|
|
638
|
-
});
|
|
684
|
+
await this.storage.save(
|
|
685
|
+
modelMetadata,
|
|
686
|
+
undefined,
|
|
687
|
+
ownSymbol
|
|
688
|
+
);
|
|
689
|
+
//#endregion
|
|
639
690
|
|
|
640
|
-
|
|
691
|
+
const counts = count.get(modelConstructor);
|
|
692
|
+
|
|
693
|
+
this.modelSyncedStatus.set(modelConstructor, true);
|
|
641
694
|
|
|
642
|
-
if (paginatingModels.size === 0) {
|
|
643
|
-
duration = getNow() - start;
|
|
644
|
-
resolve();
|
|
645
695
|
observer.next({
|
|
646
|
-
type: ControlMessage.
|
|
696
|
+
type: ControlMessage.SYNC_ENGINE_MODEL_SYNCED,
|
|
697
|
+
data: {
|
|
698
|
+
model: modelConstructor,
|
|
699
|
+
isFullSync,
|
|
700
|
+
isDeltaSync: !isFullSync,
|
|
701
|
+
counts,
|
|
702
|
+
},
|
|
647
703
|
});
|
|
648
|
-
|
|
704
|
+
|
|
705
|
+
paginatingModels.delete(modelDefinition);
|
|
706
|
+
|
|
707
|
+
if (paginatingModels.size === 0) {
|
|
708
|
+
duration = getNow() - start;
|
|
709
|
+
resolve();
|
|
710
|
+
observer.next({
|
|
711
|
+
type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY,
|
|
712
|
+
});
|
|
713
|
+
syncQueriesSubscription.unsubscribe();
|
|
714
|
+
}
|
|
649
715
|
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
716
|
+
},
|
|
717
|
+
error: error => {
|
|
718
|
+
observer.error(error);
|
|
719
|
+
},
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
observer.next({
|
|
723
|
+
type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_STARTED,
|
|
724
|
+
data: {
|
|
725
|
+
models: Array.from(paginatingModels).map(({ name }) => name),
|
|
654
726
|
},
|
|
655
727
|
});
|
|
656
|
-
|
|
657
|
-
observer.next({
|
|
658
|
-
type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_STARTED,
|
|
659
|
-
data: {
|
|
660
|
-
models: Array.from(paginatingModels).map(({ name }) => name),
|
|
661
|
-
},
|
|
662
728
|
});
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
const msNextFullSync =
|
|
666
|
-
newestFullSyncStartedAt +
|
|
667
|
-
theInterval -
|
|
668
|
-
(newestStartedAt + duration);
|
|
669
|
-
|
|
670
|
-
logger.debug(
|
|
671
|
-
`Next fullSync in ${msNextFullSync / 1000} seconds. (${new Date(
|
|
672
|
-
Date.now() + msNextFullSync
|
|
673
|
-
)})`
|
|
674
|
-
);
|
|
675
729
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
730
|
+
const msNextFullSync =
|
|
731
|
+
newestFullSyncStartedAt +
|
|
732
|
+
theInterval -
|
|
733
|
+
(newestStartedAt + duration);
|
|
734
|
+
|
|
735
|
+
logger.debug(
|
|
736
|
+
`Next fullSync in ${msNextFullSync / 1000} seconds. (${new Date(
|
|
737
|
+
Date.now() + msNextFullSync
|
|
738
|
+
)})`
|
|
739
|
+
);
|
|
740
|
+
|
|
741
|
+
// TODO: create `BackgroundProcessManager.sleep()` ... but, need to put
|
|
742
|
+
// a lot of thought into what that contract looks like to
|
|
743
|
+
// support possible use-cases:
|
|
744
|
+
//
|
|
745
|
+
// 1. non-cancelable
|
|
746
|
+
// 2. cancelable, unsleep on exit()
|
|
747
|
+
// 3. cancelable, throw Error on exit()
|
|
748
|
+
// 4. cancelable, callback first on exit()?
|
|
749
|
+
// 5. ... etc. ? ...
|
|
750
|
+
//
|
|
751
|
+
// TLDR; this is a lot of complexity here for a sleep(),
|
|
752
|
+
// but, it's not clear to me yet how to support an
|
|
753
|
+
// extensible, centralized cancelable `sleep()` elegantly.
|
|
754
|
+
await this.runningProcesses.add(async onTerminate => {
|
|
755
|
+
let sleepTimer;
|
|
756
|
+
let unsleep;
|
|
757
|
+
|
|
758
|
+
const sleep = new Promise(_unsleep => {
|
|
759
|
+
unsleep = _unsleep;
|
|
760
|
+
sleepTimer = setTimeout(unsleep, msNextFullSync);
|
|
761
|
+
});
|
|
681
762
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
763
|
+
onTerminate.then(() => {
|
|
764
|
+
terminated = true;
|
|
765
|
+
unsleep();
|
|
766
|
+
});
|
|
686
767
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
768
|
+
return sleep;
|
|
769
|
+
}, 'syncQueriesObservable sleep');
|
|
770
|
+
}
|
|
771
|
+
}, 'syncQueriesObservable main');
|
|
691
772
|
});
|
|
692
773
|
}
|
|
693
774
|
|
|
@@ -707,9 +788,39 @@ export class SyncEngine {
|
|
|
707
788
|
this.datastoreConnectivity.unsubscribe();
|
|
708
789
|
}
|
|
709
790
|
|
|
791
|
+
/**
|
|
792
|
+
* Stops all subscription activities and resolves when all activies report
|
|
793
|
+
* that they're disconnected, done retrying, etc..
|
|
794
|
+
*/
|
|
795
|
+
public async stop() {
|
|
796
|
+
logger.debug('stopping sync engine');
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Gracefully disconnecting subscribers first just prevents *more* work
|
|
800
|
+
* from entering the pipelines.
|
|
801
|
+
*/
|
|
802
|
+
this.unsubscribeConnectivity();
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* aggressively shut down any lingering background processes.
|
|
806
|
+
* some of this might be semi-redundant with unsubscribing. however,
|
|
807
|
+
* unsubscribing doesn't allow us to wait for settling.
|
|
808
|
+
* (Whereas `stop()` does.)
|
|
809
|
+
*/
|
|
810
|
+
|
|
811
|
+
await this.mutationsProcessor.stop();
|
|
812
|
+
await this.subscriptionsProcessor.stop();
|
|
813
|
+
await this.datastoreConnectivity.stop();
|
|
814
|
+
await this.syncQueriesProcessor.stop();
|
|
815
|
+
await this.runningProcesses.close();
|
|
816
|
+
await this.runningProcesses.open();
|
|
817
|
+
|
|
818
|
+
logger.debug('sync engine stopped and ready to restart');
|
|
819
|
+
}
|
|
820
|
+
|
|
710
821
|
private async setupModels(params: StartParams) {
|
|
711
822
|
const { fullSyncInterval } = params;
|
|
712
|
-
const
|
|
823
|
+
const ModelMetadataConstructor = this.modelClasses
|
|
713
824
|
.ModelMetadata as PersistentModelConstructor<ModelMetadata>;
|
|
714
825
|
|
|
715
826
|
const models: [string, SchemaModel][] = [];
|
|
@@ -741,7 +852,7 @@ export class SyncEngine {
|
|
|
741
852
|
|
|
742
853
|
if (modelMetadata === undefined) {
|
|
743
854
|
[[savedModel]] = await this.storage.save(
|
|
744
|
-
this.modelInstanceCreator(
|
|
855
|
+
this.modelInstanceCreator(ModelMetadataConstructor, {
|
|
745
856
|
model: model.name,
|
|
746
857
|
namespace,
|
|
747
858
|
lastSync: null,
|
|
@@ -759,9 +870,7 @@ export class SyncEngine {
|
|
|
759
870
|
const syncPredicateUpdated = prevSyncPredicate !== lastSyncPredicate;
|
|
760
871
|
|
|
761
872
|
[[savedModel]] = await this.storage.save(
|
|
762
|
-
(
|
|
763
|
-
this.modelClasses.ModelMetadata as PersistentModelConstructor<any>
|
|
764
|
-
).copyOf(modelMetadata, draft => {
|
|
873
|
+
ModelMetadataConstructor.copyOf(modelMetadata, draft => {
|
|
765
874
|
draft.fullSyncInterval = fullSyncInterval;
|
|
766
875
|
// perform a base sync if the syncPredicate changed in between calls to DataStore.start
|
|
767
876
|
// ensures that the local store contains all the data specified by the syncExpression
|