@powersync/common 0.0.0-dev-20250715111940 → 0.0.0-dev-20250728083821
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/bundle.cjs +3 -3
- package/dist/bundle.mjs +3 -3
- package/lib/client/AbstractPowerSyncDatabase.d.ts +2 -3
- package/lib/client/AbstractPowerSyncDatabase.js +5 -7
- package/lib/client/sync/bucket/SqliteBucketStorage.js +14 -14
- package/lib/client/sync/stream/AbstractRemote.js +31 -19
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +2 -3
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +17 -9
- package/lib/db/schema/RawTable.d.ts +4 -0
- package/lib/db/schema/RawTable.js +4 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Mutex } from 'async-mutex';
|
|
2
|
-
import
|
|
2
|
+
import { ILogger } from 'js-logger';
|
|
3
3
|
import { DBAdapter, QueryResult, Transaction } from '../db/DBAdapter.js';
|
|
4
4
|
import { SyncStatus } from '../db/crud/SyncStatus.js';
|
|
5
5
|
import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
|
|
@@ -84,7 +84,6 @@ export declare const DEFAULT_POWERSYNC_CLOSE_OPTIONS: PowerSyncCloseOptions;
|
|
|
84
84
|
export declare const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
85
85
|
export declare const DEFAULT_POWERSYNC_DB_OPTIONS: {
|
|
86
86
|
retryDelayMs: number;
|
|
87
|
-
logger: Logger.ILogger;
|
|
88
87
|
crudUploadThrottleMs: number;
|
|
89
88
|
};
|
|
90
89
|
export declare const DEFAULT_CRUD_BATCH_LIMIT = 100;
|
|
@@ -118,6 +117,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
118
117
|
protected _schema: Schema;
|
|
119
118
|
private _database;
|
|
120
119
|
protected runExclusiveMutex: Mutex;
|
|
120
|
+
logger: ILogger;
|
|
121
121
|
constructor(options: PowerSyncDatabaseOptionsWithDBAdapter);
|
|
122
122
|
constructor(options: PowerSyncDatabaseOptionsWithOpenFactory);
|
|
123
123
|
constructor(options: PowerSyncDatabaseOptionsWithSettings);
|
|
@@ -180,7 +180,6 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
180
180
|
* Cannot be used while connected - this should only be called before {@link AbstractPowerSyncDatabase.connect}.
|
|
181
181
|
*/
|
|
182
182
|
updateSchema(schema: Schema): Promise<void>;
|
|
183
|
-
get logger(): Logger.ILogger;
|
|
184
183
|
/**
|
|
185
184
|
* Wait for initialization to complete.
|
|
186
185
|
* While initializing is automatic, this helps to catch and report initialization errors.
|
|
@@ -26,7 +26,6 @@ export const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
|
|
|
26
26
|
export const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
27
27
|
export const DEFAULT_POWERSYNC_DB_OPTIONS = {
|
|
28
28
|
retryDelayMs: 5000,
|
|
29
|
-
logger: Logger.get('PowerSyncDatabase'),
|
|
30
29
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
31
30
|
};
|
|
32
31
|
export const DEFAULT_CRUD_BATCH_LIMIT = 100;
|
|
@@ -64,6 +63,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
64
63
|
_schema;
|
|
65
64
|
_database;
|
|
66
65
|
runExclusiveMutex;
|
|
66
|
+
logger;
|
|
67
67
|
constructor(options) {
|
|
68
68
|
super();
|
|
69
69
|
this.options = options;
|
|
@@ -83,6 +83,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
83
83
|
else {
|
|
84
84
|
throw new Error('The provided `database` option is invalid.');
|
|
85
85
|
}
|
|
86
|
+
this.logger = options.logger ?? Logger.get(`PowerSyncDatabase[${this._database.name}]`);
|
|
86
87
|
this.bucketStorageAdapter = this.generateBucketStorageAdapter();
|
|
87
88
|
this.closed = false;
|
|
88
89
|
this.currentStatus = new SyncStatus({});
|
|
@@ -262,16 +263,13 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
262
263
|
schema.validate();
|
|
263
264
|
}
|
|
264
265
|
catch (ex) {
|
|
265
|
-
this.
|
|
266
|
+
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
266
267
|
}
|
|
267
268
|
this._schema = schema;
|
|
268
269
|
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
269
270
|
await this.database.refreshSchema();
|
|
270
271
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
271
272
|
}
|
|
272
|
-
get logger() {
|
|
273
|
-
return this.options.logger;
|
|
274
|
-
}
|
|
275
273
|
/**
|
|
276
274
|
* Wait for initialization to complete.
|
|
277
275
|
* While initializing is automatic, this helps to catch and report initialization errors.
|
|
@@ -609,7 +607,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
609
607
|
* @param options Options for configuring watch behavior
|
|
610
608
|
*/
|
|
611
609
|
watchWithCallback(sql, parameters, handler, options) {
|
|
612
|
-
const { onResult, onError = (e) => this.
|
|
610
|
+
const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
|
|
613
611
|
if (!onResult) {
|
|
614
612
|
throw new Error('onResult is required');
|
|
615
613
|
}
|
|
@@ -715,7 +713,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
715
713
|
* @returns A dispose function to stop watching for changes
|
|
716
714
|
*/
|
|
717
715
|
onChangeWithCallback(handler, options) {
|
|
718
|
-
const { onChange, onError = (e) => this.
|
|
716
|
+
const { onChange, onError = (e) => this.logger.error(e) } = handler ?? {};
|
|
719
717
|
if (!onChange) {
|
|
720
718
|
throw new Error('onChange is required');
|
|
721
719
|
}
|
|
@@ -64,11 +64,11 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
64
64
|
async saveSyncData(batch, fixedKeyFormat = false) {
|
|
65
65
|
await this.writeTransaction(async (tx) => {
|
|
66
66
|
for (const b of batch.buckets) {
|
|
67
|
-
|
|
67
|
+
await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', [
|
|
68
68
|
'save',
|
|
69
69
|
JSON.stringify({ buckets: [b.toJSON(fixedKeyFormat)] })
|
|
70
70
|
]);
|
|
71
|
-
this.logger.debug(
|
|
71
|
+
this.logger.debug(`Saved batch of data for bucket: ${b.bucket}, operations: ${b.data.length}`);
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
}
|
|
@@ -84,7 +84,7 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
84
84
|
await this.writeTransaction(async (tx) => {
|
|
85
85
|
await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', ['delete_bucket', bucket]);
|
|
86
86
|
});
|
|
87
|
-
this.logger.debug(
|
|
87
|
+
this.logger.debug(`Done deleting bucket ${bucket}`);
|
|
88
88
|
}
|
|
89
89
|
async hasCompletedSync() {
|
|
90
90
|
if (this._hasCompletedSync) {
|
|
@@ -106,6 +106,12 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
106
106
|
}
|
|
107
107
|
return { ready: false, checkpointValid: false, checkpointFailures: r.checkpointFailures };
|
|
108
108
|
}
|
|
109
|
+
if (priority == null) {
|
|
110
|
+
this.logger.debug(`Validated checksums checkpoint ${checkpoint.last_op_id}`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
this.logger.debug(`Validated checksums for partial checkpoint ${checkpoint.last_op_id}, priority ${priority}`);
|
|
114
|
+
}
|
|
109
115
|
let buckets = checkpoint.buckets;
|
|
110
116
|
if (priority !== undefined) {
|
|
111
117
|
buckets = buckets.filter((b) => hasMatchingPriority(priority, b));
|
|
@@ -122,7 +128,6 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
122
128
|
});
|
|
123
129
|
const valid = await this.updateObjectsFromBuckets(checkpoint, priority);
|
|
124
130
|
if (!valid) {
|
|
125
|
-
this.logger.debug('Not at a consistent checkpoint - cannot update local db');
|
|
126
131
|
return { ready: false, checkpointValid: true };
|
|
127
132
|
}
|
|
128
133
|
return {
|
|
@@ -175,7 +180,6 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
175
180
|
JSON.stringify({ ...checkpoint })
|
|
176
181
|
]);
|
|
177
182
|
const resultItem = rs.rows?.item(0);
|
|
178
|
-
this.logger.debug('validateChecksums priority, checkpoint, result item', priority, checkpoint, resultItem);
|
|
179
183
|
if (!resultItem) {
|
|
180
184
|
return {
|
|
181
185
|
checkpointValid: false,
|
|
@@ -208,30 +212,26 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
208
212
|
}
|
|
209
213
|
const seqBefore = rs[0]['seq'];
|
|
210
214
|
const opId = await cb();
|
|
211
|
-
this.logger.debug(`[updateLocalTarget] Updating target to checkpoint ${opId}`);
|
|
212
215
|
return this.writeTransaction(async (tx) => {
|
|
213
216
|
const anyData = await tx.execute('SELECT 1 FROM ps_crud LIMIT 1');
|
|
214
217
|
if (anyData.rows?.length) {
|
|
215
218
|
// if isNotEmpty
|
|
216
|
-
this.logger.debug(
|
|
219
|
+
this.logger.debug(`New data uploaded since write checkpoint ${opId} - need new write checkpoint`);
|
|
217
220
|
return false;
|
|
218
221
|
}
|
|
219
222
|
const rs = await tx.execute("SELECT seq FROM sqlite_sequence WHERE name = 'ps_crud'");
|
|
220
223
|
if (!rs.rows?.length) {
|
|
221
224
|
// assert isNotEmpty
|
|
222
|
-
throw new Error('
|
|
225
|
+
throw new Error('SQLite Sequence should not be empty');
|
|
223
226
|
}
|
|
224
227
|
const seqAfter = rs.rows?.item(0)['seq'];
|
|
225
|
-
this.logger.debug('seqAfter', JSON.stringify(rs.rows?.item(0)));
|
|
226
228
|
if (seqAfter != seqBefore) {
|
|
227
|
-
this.logger.debug(
|
|
229
|
+
this.logger.debug(`New data uploaded since write checpoint ${opId} - need new write checkpoint (sequence updated)`);
|
|
228
230
|
// New crud data may have been uploaded since we got the checkpoint. Abort.
|
|
229
231
|
return false;
|
|
230
232
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
]);
|
|
234
|
-
this.logger.debug(['[updateLocalTarget] Response from updating target_op ', JSON.stringify(response)]);
|
|
233
|
+
this.logger.debug(`Updating target write checkpoint to ${opId}`);
|
|
234
|
+
await tx.execute("UPDATE ps_buckets SET target_op = CAST(? as INTEGER) WHERE name='$local'", [opId]);
|
|
235
235
|
return true;
|
|
236
236
|
});
|
|
237
237
|
}
|
|
@@ -204,6 +204,22 @@ export class AbstractRemote {
|
|
|
204
204
|
// headers with websockets on web. The browser userAgent is however added
|
|
205
205
|
// automatically as a header.
|
|
206
206
|
const userAgent = this.getUserAgent();
|
|
207
|
+
const stream = new DataStream({
|
|
208
|
+
logger: this.logger,
|
|
209
|
+
pressure: {
|
|
210
|
+
lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER
|
|
211
|
+
},
|
|
212
|
+
mapLine: map
|
|
213
|
+
});
|
|
214
|
+
// Handle upstream abort
|
|
215
|
+
if (options.abortSignal?.aborted) {
|
|
216
|
+
throw new AbortOperation('Connection request aborted');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
options.abortSignal?.addEventListener('abort', () => {
|
|
220
|
+
stream.close();
|
|
221
|
+
}, { once: true });
|
|
222
|
+
}
|
|
207
223
|
let keepAliveTimeout;
|
|
208
224
|
const resetTimeout = () => {
|
|
209
225
|
clearTimeout(keepAliveTimeout);
|
|
@@ -213,12 +229,22 @@ export class AbstractRemote {
|
|
|
213
229
|
}, SOCKET_TIMEOUT_MS);
|
|
214
230
|
};
|
|
215
231
|
resetTimeout();
|
|
232
|
+
// Typescript complains about this being `never` if it's not assigned here.
|
|
233
|
+
// This is assigned in `wsCreator`.
|
|
234
|
+
let disposeSocketConnectionTimeout = () => { };
|
|
216
235
|
const url = this.options.socketUrlTransformer(request.url);
|
|
217
236
|
const connector = new RSocketConnector({
|
|
218
237
|
transport: new WebsocketClientTransport({
|
|
219
238
|
url,
|
|
220
239
|
wsCreator: (url) => {
|
|
221
240
|
const socket = this.createSocket(url);
|
|
241
|
+
disposeSocketConnectionTimeout = stream.registerListener({
|
|
242
|
+
closed: () => {
|
|
243
|
+
// Allow closing the underlying WebSocket if the stream was closed before the
|
|
244
|
+
// RSocket connect completed. This should effectively abort the request.
|
|
245
|
+
socket.close();
|
|
246
|
+
}
|
|
247
|
+
});
|
|
222
248
|
socket.addEventListener('message', (event) => {
|
|
223
249
|
resetTimeout();
|
|
224
250
|
});
|
|
@@ -242,20 +268,18 @@ export class AbstractRemote {
|
|
|
242
268
|
let rsocket;
|
|
243
269
|
try {
|
|
244
270
|
rsocket = await connector.connect();
|
|
271
|
+
// The connection is established, we no longer need to monitor the initial timeout
|
|
272
|
+
disposeSocketConnectionTimeout();
|
|
245
273
|
}
|
|
246
274
|
catch (ex) {
|
|
247
275
|
this.logger.error(`Failed to connect WebSocket`, ex);
|
|
248
276
|
clearTimeout(keepAliveTimeout);
|
|
277
|
+
if (!stream.closed) {
|
|
278
|
+
await stream.close();
|
|
279
|
+
}
|
|
249
280
|
throw ex;
|
|
250
281
|
}
|
|
251
282
|
resetTimeout();
|
|
252
|
-
const stream = new DataStream({
|
|
253
|
-
logger: this.logger,
|
|
254
|
-
pressure: {
|
|
255
|
-
lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER
|
|
256
|
-
},
|
|
257
|
-
mapLine: map
|
|
258
|
-
});
|
|
259
283
|
let socketIsClosed = false;
|
|
260
284
|
const closeSocket = () => {
|
|
261
285
|
clearTimeout(keepAliveTimeout);
|
|
@@ -341,18 +365,6 @@ export class AbstractRemote {
|
|
|
341
365
|
l();
|
|
342
366
|
}
|
|
343
367
|
});
|
|
344
|
-
/**
|
|
345
|
-
* Handle abort operations here.
|
|
346
|
-
* Unfortunately cannot insert them into the connection.
|
|
347
|
-
*/
|
|
348
|
-
if (options.abortSignal?.aborted) {
|
|
349
|
-
stream.close();
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
options.abortSignal?.addEventListener('abort', () => {
|
|
353
|
-
stream.close();
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
368
|
return stream;
|
|
357
369
|
}
|
|
358
370
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ILogger } from 'js-logger';
|
|
2
2
|
import { SyncStatus, SyncStatusOptions } from '../../../db/crud/SyncStatus.js';
|
|
3
3
|
import { BaseListener, BaseObserver, Disposable } from '../../../utils/BaseObserver.js';
|
|
4
4
|
import { BucketStorageAdapter } from '../bucket/BucketStorageAdapter.js';
|
|
@@ -160,7 +160,6 @@ export declare const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
|
160
160
|
export declare const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
161
161
|
export declare const DEFAULT_STREAMING_SYNC_OPTIONS: {
|
|
162
162
|
retryDelayMs: number;
|
|
163
|
-
logger: Logger.ILogger;
|
|
164
163
|
crudUploadThrottleMs: number;
|
|
165
164
|
};
|
|
166
165
|
export type RequiredPowerSyncConnectionOptions = Required<BaseConnectionOptions>;
|
|
@@ -171,6 +170,7 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
|
|
|
171
170
|
protected abortController: AbortController | null;
|
|
172
171
|
protected crudUpdateListener?: () => void;
|
|
173
172
|
protected streamingSyncPromise?: Promise<void>;
|
|
173
|
+
protected logger: ILogger;
|
|
174
174
|
private isUploadingCrud;
|
|
175
175
|
private notifyCompletedUploads?;
|
|
176
176
|
syncStatus: SyncStatus;
|
|
@@ -181,7 +181,6 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
|
|
|
181
181
|
waitUntilStatusMatches(predicate: (status: SyncStatus) => boolean): Promise<void>;
|
|
182
182
|
get lastSyncedAt(): Date | undefined;
|
|
183
183
|
get isConnected(): boolean;
|
|
184
|
-
protected get logger(): Logger.ILogger;
|
|
185
184
|
dispose(): Promise<void>;
|
|
186
185
|
abstract obtainLock<T>(lockOptions: LockOptions<T>): Promise<T>;
|
|
187
186
|
hasCompletedSync(): Promise<boolean>;
|
|
@@ -65,7 +65,6 @@ export const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
|
65
65
|
export const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
66
66
|
export const DEFAULT_STREAMING_SYNC_OPTIONS = {
|
|
67
67
|
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
68
|
-
logger: Logger.get('PowerSyncStream'),
|
|
69
68
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
70
69
|
};
|
|
71
70
|
export const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
@@ -86,6 +85,7 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
86
85
|
abortController;
|
|
87
86
|
crudUpdateListener;
|
|
88
87
|
streamingSyncPromise;
|
|
88
|
+
logger;
|
|
89
89
|
isUploadingCrud = false;
|
|
90
90
|
notifyCompletedUploads;
|
|
91
91
|
syncStatus;
|
|
@@ -93,6 +93,7 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
93
93
|
constructor(options) {
|
|
94
94
|
super();
|
|
95
95
|
this.options = { ...DEFAULT_STREAMING_SYNC_OPTIONS, ...options };
|
|
96
|
+
this.logger = options.logger ?? Logger.get('PowerSyncStream');
|
|
96
97
|
this.syncStatus = new SyncStatus({
|
|
97
98
|
connected: false,
|
|
98
99
|
connecting: false,
|
|
@@ -156,9 +157,6 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
156
157
|
get isConnected() {
|
|
157
158
|
return this.syncStatus.connected;
|
|
158
159
|
}
|
|
159
|
-
get logger() {
|
|
160
|
-
return this.options.logger;
|
|
161
|
-
}
|
|
162
160
|
async dispose() {
|
|
163
161
|
this.crudUpdateListener?.();
|
|
164
162
|
this.crudUpdateListener = undefined;
|
|
@@ -170,7 +168,9 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
170
168
|
const clientId = await this.options.adapter.getClientId();
|
|
171
169
|
let path = `/write-checkpoint2.json?client_id=${clientId}`;
|
|
172
170
|
const response = await this.options.remote.get(path);
|
|
173
|
-
|
|
171
|
+
const checkpoint = response['data']['write_checkpoint'];
|
|
172
|
+
this.logger.debug(`Created write checkpoint: ${checkpoint}`);
|
|
173
|
+
return checkpoint;
|
|
174
174
|
}
|
|
175
175
|
async _uploadAllCrud() {
|
|
176
176
|
return this.obtainLock({
|
|
@@ -209,7 +209,11 @@ The next upload iteration will be delayed.`);
|
|
|
209
209
|
}
|
|
210
210
|
else {
|
|
211
211
|
// Uploading is completed
|
|
212
|
-
await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
|
|
212
|
+
const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
|
|
213
|
+
if (neededUpdate == false && checkedCrudItem != null) {
|
|
214
|
+
// Only log this if there was something to upload
|
|
215
|
+
this.logger.debug('Upload complete, no write checkpoint needed.');
|
|
216
|
+
}
|
|
213
217
|
break;
|
|
214
218
|
}
|
|
215
219
|
}
|
|
@@ -435,6 +439,10 @@ The next upload iteration will be delayed.`);
|
|
|
435
439
|
});
|
|
436
440
|
}
|
|
437
441
|
async legacyStreamingSyncIteration(signal, resolvedOptions) {
|
|
442
|
+
const rawTables = resolvedOptions.serializedSchema?.raw_tables;
|
|
443
|
+
if (rawTables != null && rawTables.length) {
|
|
444
|
+
this.logger.warn('Raw tables require the Rust-based sync client. The JS client will ignore them.');
|
|
445
|
+
}
|
|
438
446
|
this.logger.debug('Streaming sync iteration started');
|
|
439
447
|
this.options.adapter.startSession();
|
|
440
448
|
let [req, bucketMap] = await this.collectLocalBucketState();
|
|
@@ -882,17 +890,17 @@ The next upload iteration will be delayed.`);
|
|
|
882
890
|
async applyCheckpoint(checkpoint) {
|
|
883
891
|
let result = await this.options.adapter.syncLocalDatabase(checkpoint);
|
|
884
892
|
if (!result.checkpointValid) {
|
|
885
|
-
this.logger.debug(
|
|
893
|
+
this.logger.debug(`Checksum mismatch in checkpoint ${checkpoint.last_op_id}, will reconnect`);
|
|
886
894
|
// This means checksums failed. Start again with a new checkpoint.
|
|
887
895
|
// TODO: better back-off
|
|
888
896
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
889
897
|
return { applied: false, endIteration: true };
|
|
890
898
|
}
|
|
891
899
|
else if (!result.ready) {
|
|
892
|
-
this.logger.debug(
|
|
900
|
+
this.logger.debug(`Could not apply checkpoint ${checkpoint.last_op_id} due to local data. We will retry applying the checkpoint after that upload is completed.`);
|
|
893
901
|
return { applied: false, endIteration: false };
|
|
894
902
|
}
|
|
895
|
-
this.logger.debug(
|
|
903
|
+
this.logger.debug(`Applied checkpoint ${checkpoint.last_op_id}`, checkpoint);
|
|
896
904
|
this.updateSyncStatus({
|
|
897
905
|
connected: true,
|
|
898
906
|
lastSyncedAt: new Date(),
|
|
@@ -37,6 +37,10 @@ export type PendingStatement = {
|
|
|
37
37
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
38
38
|
* using client-side table and column constraints.
|
|
39
39
|
*
|
|
40
|
+
* To collect local writes to raw tables with PowerSync, custom triggers are required. See
|
|
41
|
+
* {@link https://docs.powersync.com/usage/use-case-examples/raw-tables the documentation} for details and an example on
|
|
42
|
+
* using raw tables.
|
|
43
|
+
*
|
|
40
44
|
* Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
|
|
41
45
|
*
|
|
42
46
|
* @experimental Please note that this feature is experimental at the moment, and not covered by PowerSync semver or
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
5
5
|
* using client-side table and column constraints.
|
|
6
6
|
*
|
|
7
|
+
* To collect local writes to raw tables with PowerSync, custom triggers are required. See
|
|
8
|
+
* {@link https://docs.powersync.com/usage/use-case-examples/raw-tables the documentation} for details and an example on
|
|
9
|
+
* using raw tables.
|
|
10
|
+
*
|
|
7
11
|
* Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
|
|
8
12
|
*
|
|
9
13
|
* @experimental Please note that this feature is experimental at the moment, and not covered by PowerSync semver or
|