@syncular/server 0.37.0 → 0.38.0
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/d1-storage.js +23 -18
- package/dist/postgres-storage.js +31 -11
- package/dist/relational-rows.d.ts +33 -13
- package/dist/relational-rows.js +85 -17
- package/dist/schema.d.ts +19 -0
- package/dist/schema.js +22 -0
- package/dist/sqlite-bun-driver.js +1 -1
- package/dist/sqlite-node-driver.d.ts +1 -0
- package/dist/sqlite-node-driver.js +13 -3
- package/dist/sqlite-storage.js +20 -18
- package/dist/storage.d.ts +8 -1
- package/dist/storage.js +3 -0
- package/package.json +2 -2
- package/src/d1-storage.ts +35 -21
- package/src/postgres-storage.ts +58 -16
- package/src/relational-rows.ts +129 -19
- package/src/schema.ts +55 -0
- package/src/sqlite-bun-driver.ts +1 -1
- package/src/sqlite-node-driver.ts +18 -3
- package/src/sqlite-storage.ts +44 -31
- package/src/storage.ts +9 -1
package/src/sqlite-storage.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { retainRollbackFailure, rollbackAfterError } from './storage-errors';
|
|
|
2
2
|
import { validateCommitPruneQuery } from './prune';
|
|
3
3
|
import { StorageQueryError } from './storage-errors';
|
|
4
4
|
import type { CommitPruneQuery, CommitPruneResult } from './storage';
|
|
5
|
-
import { serveGateRefusal } from './storage';
|
|
5
|
+
import { PARTITION_TOUCH_INTERVAL_MS, serveGateRefusal } from './storage';
|
|
6
6
|
import { serveNotReadyError } from './readiness';
|
|
7
7
|
/**
|
|
8
8
|
* SQLite server storage over the shared synchronous driver.
|
|
@@ -45,6 +45,9 @@ import {
|
|
|
45
45
|
type StoredColumnLayout,
|
|
46
46
|
scanRowPageSql,
|
|
47
47
|
schemaDdl,
|
|
48
|
+
SQLITE_TABLE_INDEXES_SQL,
|
|
49
|
+
type SqliteTableIndexRow,
|
|
50
|
+
sqliteIndexSignatures,
|
|
48
51
|
selectRowScopesSql,
|
|
49
52
|
selectRowSql,
|
|
50
53
|
selectRowsForRewriteSql,
|
|
@@ -656,7 +659,7 @@ END`);
|
|
|
656
659
|
const layouts = parseLayouts(marker?.layouts);
|
|
657
660
|
const retiredTables = retiredTableNames(schema, layouts);
|
|
658
661
|
const existing = new Map<string, ReadonlySet<string>>();
|
|
659
|
-
const existingIndexes = new Map<string,
|
|
662
|
+
const existingIndexes = new Map<string, ReadonlyMap<string, string>>();
|
|
660
663
|
for (const table of schema.tables.values()) {
|
|
661
664
|
const escapedTableName = table.name.replaceAll('"', '""');
|
|
662
665
|
const columns = this.db
|
|
@@ -666,15 +669,19 @@ END`);
|
|
|
666
669
|
.all();
|
|
667
670
|
if (columns.length > 0) {
|
|
668
671
|
existing.set(table.name, new Set(columns.map((c) => c.name)));
|
|
669
|
-
const indexes = this.db
|
|
670
|
-
.query<{ name: string; origin: string }, []>(
|
|
671
|
-
`PRAGMA index_list("${escapedTableName}")`,
|
|
672
|
-
)
|
|
673
|
-
.all()
|
|
674
|
-
.filter((index) => index.origin === 'c');
|
|
675
672
|
existingIndexes.set(
|
|
676
673
|
table.name,
|
|
677
|
-
new
|
|
674
|
+
new Map(
|
|
675
|
+
Object.entries(
|
|
676
|
+
sqliteIndexSignatures(
|
|
677
|
+
this.db
|
|
678
|
+
.query<SqliteTableIndexRow, [string]>(
|
|
679
|
+
SQLITE_TABLE_INDEXES_SQL,
|
|
680
|
+
)
|
|
681
|
+
.all(table.name),
|
|
682
|
+
),
|
|
683
|
+
),
|
|
684
|
+
),
|
|
678
685
|
);
|
|
679
686
|
}
|
|
680
687
|
}
|
|
@@ -763,28 +770,34 @@ END`);
|
|
|
763
770
|
if (initialLogEpoch.length === 0) {
|
|
764
771
|
throw new Error('initial log epoch must be non-empty');
|
|
765
772
|
}
|
|
766
|
-
this.db
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
773
|
+
const select = this.db.query<
|
|
774
|
+
{
|
|
775
|
+
log_epoch: string;
|
|
776
|
+
epoch_required: number;
|
|
777
|
+
last_authenticated_at_ms: number;
|
|
778
|
+
},
|
|
779
|
+
[string]
|
|
780
|
+
>(
|
|
781
|
+
`SELECT log_epoch, epoch_required, last_authenticated_at_ms
|
|
782
|
+
FROM sync_partition_registry WHERE partition=?`,
|
|
783
|
+
);
|
|
784
|
+
let row = select.get(partition);
|
|
785
|
+
if (
|
|
786
|
+
row === null ||
|
|
787
|
+
authenticatedAtMs - row.last_authenticated_at_ms >=
|
|
788
|
+
PARTITION_TOUCH_INTERVAL_MS
|
|
789
|
+
) {
|
|
790
|
+
this.db
|
|
791
|
+
.query(
|
|
792
|
+
`INSERT INTO sync_partition_registry(
|
|
793
|
+
partition, log_epoch, last_authenticated_at_ms
|
|
794
|
+
) VALUES (?,?,?)
|
|
795
|
+
ON CONFLICT(partition) DO UPDATE SET
|
|
796
|
+
last_authenticated_at_ms=excluded.last_authenticated_at_ms`,
|
|
797
|
+
)
|
|
798
|
+
.run(partition, initialLogEpoch, authenticatedAtMs);
|
|
799
|
+
row = select.get(partition);
|
|
800
|
+
}
|
|
788
801
|
if (row === null)
|
|
789
802
|
throw new Error('partition registry write did not persist');
|
|
790
803
|
return {
|
package/src/storage.ts
CHANGED
|
@@ -358,6 +358,9 @@ export interface ClientCursorInfo {
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
/** Durable partition identity refreshed after host authentication (§2.1). */
|
|
361
|
+
/** Longest age of `lastAuthenticatedAtMs` that `touchPartition` leaves unwritten. */
|
|
362
|
+
export const PARTITION_TOUCH_INTERVAL_MS = 60_000;
|
|
363
|
+
|
|
361
364
|
export interface PartitionRegistryEntry {
|
|
362
365
|
readonly partition: string;
|
|
363
366
|
readonly logEpoch: string;
|
|
@@ -651,7 +654,12 @@ export interface ServerStorage {
|
|
|
651
654
|
checkpoints?: readonly CheckpointDeclaration[],
|
|
652
655
|
): Promise<void>;
|
|
653
656
|
|
|
654
|
-
/**
|
|
657
|
+
/**
|
|
658
|
+
* Create or refresh the authenticated partition registry row (§2.1). A
|
|
659
|
+
* row whose `lastAuthenticatedAtMs` is less than
|
|
660
|
+
* {@link PARTITION_TOUCH_INTERVAL_MS} behind `authenticatedAtMs` is read
|
|
661
|
+
* back without a write, so a request that only reads takes no write lock.
|
|
662
|
+
*/
|
|
655
663
|
touchPartition(
|
|
656
664
|
partition: string,
|
|
657
665
|
authenticatedAtMs: number,
|