@g9tm/syncdata 1.5.20 → 1.5.21
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/PARITY.md +7 -0
- package/dist/client/engine.d.ts +7 -0
- package/dist/client/engine.js +54 -1
- package/dist/client/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol/dtos.d.ts +16 -0
- package/dist/protocol/dtos.js.map +1 -1
- package/dist/protocol/wire.js +6 -0
- package/dist/protocol/wire.js.map +1 -1
- package/dist/signalr/transport.d.ts +9 -0
- package/dist/signalr/transport.js +34 -3
- package/dist/signalr/transport.js.map +1 -1
- package/dist/sqlite/snapshotFiles.js.map +1 -1
- package/dist/sqlite/store.js +6 -3
- package/dist/sqlite/store.js.map +1 -1
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -79,6 +79,13 @@ it). This file maps every .NET piece to its twin and lists every deliberate diff
|
|
|
79
79
|
would hand out, in model order) so its triggers are written in the same batch. Same objects, rows, tids and counter
|
|
80
80
|
(`test/provision.test.ts` compares both paths on fresh, reopened, evolved and repaired databases). AgriPad's 47 tables:
|
|
81
81
|
14 driver calls on every start instead of 296, 17 instead of 534 on the first.
|
|
82
|
+
- **Hello sends the model once per connection (1.5.21).** `G9CSignalRSyncTransport.HelloAsync` / `G9SignalRSyncTransport.hello`
|
|
83
|
+
strip the tables on a later Hello of the same connection (`_modelOnConnection`, keyed by the connection id and the engine's one
|
|
84
|
+
model list/array) and resend them inside the call on `ModelRequired`; the engines keep the last answer in memory
|
|
85
|
+
(`G9CSyncEngine.HelloCache` / `_lastAnswer`), send `KnownTablesHash` / `KnownDefinitionsHash` and put back what an "unchanged"
|
|
86
|
+
answer left out (`RestoreUnchanged` / `restoreUnchanged`); both stores skip `definitions_json`, `tables_json` and the per-table
|
|
87
|
+
updates of an unchanged answer. Tests: `HelloModelOnceTests` / `test/transportModelOnce.test.ts`; the golden Hello samples carry
|
|
88
|
+
the six new fields.
|
|
82
89
|
- **Per-run bookkeeping.** `saveHello` writes the meta rows, the definitions' move-out policies and every negotiated
|
|
83
90
|
table (with its capture triggers) in ONE batch, after one read for the only per-table question (does a table with
|
|
84
91
|
newly negotiated columns hold rows?); the rows an apply protects (`readRowsChangedAfter`) are read with one
|
package/dist/client/engine.d.ts
CHANGED
|
@@ -32,6 +32,13 @@ export declare class G9SyncEngine {
|
|
|
32
32
|
private _disposed;
|
|
33
33
|
/** The model problems last logged, so the warning is said once per distinct set (ADR-0061). */
|
|
34
34
|
private _reportedModelProblems;
|
|
35
|
+
/**
|
|
36
|
+
* What Hello does not send again (1.5.21, TR-0069; twin of .NET `G9CSyncEngine.HelloCache`): the last negotiated tables and definitions,
|
|
37
|
+
* with the server's ids for them. In memory only: the first Hello of a process gets them in full.
|
|
38
|
+
*/
|
|
39
|
+
private _lastAnswer;
|
|
40
|
+
/** The model as Hello sends it, built once: the transport recognises an unchanged model by this very array. */
|
|
41
|
+
private _wireTables;
|
|
35
42
|
constructor(store: G9SyncClientStore, transport: G9SyncTransport, model: G9SyncClientModel, options?: G9SyncEngineOptions);
|
|
36
43
|
get clientId(): string;
|
|
37
44
|
get isRunning(): boolean;
|
package/dist/client/engine.js
CHANGED
|
@@ -42,6 +42,13 @@ export class G9SyncEngine {
|
|
|
42
42
|
this._disposed = false;
|
|
43
43
|
/** The model problems last logged, so the warning is said once per distinct set (ADR-0061). */
|
|
44
44
|
this._reportedModelProblems = null;
|
|
45
|
+
/**
|
|
46
|
+
* What Hello does not send again (1.5.21, TR-0069; twin of .NET `G9CSyncEngine.HelloCache`): the last negotiated tables and definitions,
|
|
47
|
+
* with the server's ids for them. In memory only: the first Hello of a process gets them in full.
|
|
48
|
+
*/
|
|
49
|
+
this._lastAnswer = null;
|
|
50
|
+
/** The model as Hello sends it, built once: the transport recognises an unchanged model by this very array. */
|
|
51
|
+
this._wireTables = null;
|
|
45
52
|
if (!store)
|
|
46
53
|
throw new TypeError('A store is required.');
|
|
47
54
|
if (!transport)
|
|
@@ -459,6 +466,7 @@ export class G9SyncEngine {
|
|
|
459
466
|
}
|
|
460
467
|
// ================================================================ execution
|
|
461
468
|
async _executeRun(run) {
|
|
469
|
+
var _a, _b, _c;
|
|
462
470
|
const request = run.request;
|
|
463
471
|
const startedUtcMs = Date.now();
|
|
464
472
|
const changes = new G9ApplyReport();
|
|
@@ -493,10 +501,19 @@ export class G9SyncEngine {
|
|
|
493
501
|
hostScope = await this._options.hooks.beginRun(run, ct);
|
|
494
502
|
// ---------------------------------------------------------------- 1. connect + hello
|
|
495
503
|
tracker.enter(G9ESyncPhase.Connecting);
|
|
504
|
+
// Where the Connecting phase goes (AgriPad TR-0069, 2026-11-13: «Connecting takes long»): each step is timed and the
|
|
505
|
+
// split logged once per run (event 20030) — the transport, the ephemeral sweep, reading the subscriptions, the
|
|
506
|
+
// Hello round trip and the save of its answer are separate costs with separate cures.
|
|
507
|
+
const connectT0 = Date.now();
|
|
496
508
|
await this._ensureConnected(ct);
|
|
509
|
+
const connectT1 = Date.now();
|
|
497
510
|
await this._store.collectExpiredEphemeral(Date.now(), ct);
|
|
511
|
+
const connectT2 = Date.now();
|
|
498
512
|
const subscriptions = await this._store.getSubscriptions(ct);
|
|
513
|
+
const connectT3 = Date.now();
|
|
499
514
|
let helloMidpointMs = Date.now();
|
|
515
|
+
const known = this._lastAnswer;
|
|
516
|
+
const wireTables = (_a = this._wireTables) !== null && _a !== void 0 ? _a : (this._wireTables = this.model.tables.map((t) => t.toWire()));
|
|
500
517
|
const hello = await this._withRetry(async (token) => {
|
|
501
518
|
// The round trip of the attempt that answered: its midpoint is the local time of the server's clock reading (LES-0075).
|
|
502
519
|
const sent = Date.now();
|
|
@@ -504,15 +521,34 @@ export class G9SyncEngine {
|
|
|
504
521
|
clientId: this._clientId,
|
|
505
522
|
appVersion: this._options.appVersion,
|
|
506
523
|
protocolVersion: 1,
|
|
507
|
-
tables:
|
|
524
|
+
tables: wireTables,
|
|
508
525
|
subscriptions: subscriptions.map(toSubscriptionRef),
|
|
526
|
+
knownTablesHash: known ? known.tablesHash : null,
|
|
527
|
+
knownDefinitionsHash: known ? known.definitionsHash : null,
|
|
509
528
|
}, token);
|
|
510
529
|
helloMidpointMs = sent + (Date.now() - sent) / 2;
|
|
511
530
|
return answer;
|
|
512
531
|
}, ct);
|
|
513
532
|
if (hello.error)
|
|
514
533
|
throw syncErrorToException(hello.error);
|
|
534
|
+
const unchanged = (hello.tablesUnchanged ? 'tables' : '') + (hello.definitionsUnchanged ? (hello.tablesUnchanged ? '+' : '') + 'definitions' : '');
|
|
535
|
+
restoreUnchanged(hello, known);
|
|
536
|
+
const connectT4 = Date.now();
|
|
515
537
|
await this._store.saveHello(hello, ct, helloMidpointMs);
|
|
538
|
+
this._lastAnswer =
|
|
539
|
+
hello.tablesHash || hello.definitionsHash
|
|
540
|
+
? {
|
|
541
|
+
tablesHash: (_b = hello.tablesHash) !== null && _b !== void 0 ? _b : null,
|
|
542
|
+
tables: hello.tables,
|
|
543
|
+
unknownTables: hello.unknownTables,
|
|
544
|
+
definitionsHash: (_c = hello.definitionsHash) !== null && _c !== void 0 ? _c : null,
|
|
545
|
+
definitions: hello.definitions,
|
|
546
|
+
}
|
|
547
|
+
: null;
|
|
548
|
+
const connectT5 = Date.now();
|
|
549
|
+
this._log('information', 20030, `Connecting ${connectT5 - connectT0} ms: transport ${connectT1 - connectT0}, ephemeral sweep ${connectT2 - connectT1}, ` +
|
|
550
|
+
`subscriptions ${connectT3 - connectT2} (${subscriptions.length}), hello ${connectT4 - connectT3} ` +
|
|
551
|
+
`(${this.model.tables.length} tables; unchanged: ${unchanged || 'none'}), save ${connectT5 - connectT4}`);
|
|
516
552
|
if (hello.unknownTables.length > 0) {
|
|
517
553
|
this._log('warning', 20016, 'The server does not know these client tables (they will not sync): ' + hello.unknownTables.join(', '));
|
|
518
554
|
}
|
|
@@ -1258,4 +1294,21 @@ async function* toChunks(batch) {
|
|
|
1258
1294
|
for (const frame of batch.frames)
|
|
1259
1295
|
yield { sequence: sequence++, payload: frame, rowCount: 0, isFinal: false };
|
|
1260
1296
|
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Puts back what an "unchanged" Hello answer left out (1.5.21; twin of .NET `RestoreUnchanged`), so the rest of a run reads a complete
|
|
1299
|
+
* answer either way. An answer that says unchanged for something this client never held is a server fault.
|
|
1300
|
+
*/
|
|
1301
|
+
function restoreUnchanged(hello, known) {
|
|
1302
|
+
if (hello.tablesUnchanged) {
|
|
1303
|
+
if (!known)
|
|
1304
|
+
throw new G9SyncException(G9SyncErrorCodes.ProtocolMismatch, "The server answered 'tables unchanged' to a client that holds none.");
|
|
1305
|
+
hello.tables = known.tables;
|
|
1306
|
+
hello.unknownTables = known.unknownTables;
|
|
1307
|
+
}
|
|
1308
|
+
if (hello.definitionsUnchanged) {
|
|
1309
|
+
if (!known)
|
|
1310
|
+
throw new G9SyncException(G9SyncErrorCodes.ProtocolMismatch, "The server answered 'definitions unchanged' to a client that holds none.");
|
|
1311
|
+
hello.definitions = known.definitions;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1261
1314
|
//# sourceMappingURL=engine.js.map
|