@powersync/service-module-postgres-storage 0.1.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.
Files changed (157) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +67 -0
  3. package/README.md +67 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/@types/index.d.ts +7 -0
  6. package/dist/@types/migrations/PostgresMigrationAgent.d.ts +12 -0
  7. package/dist/@types/migrations/PostgresMigrationStore.d.ts +14 -0
  8. package/dist/@types/migrations/migration-utils.d.ts +3 -0
  9. package/dist/@types/migrations/scripts/1684951997326-init.d.ts +3 -0
  10. package/dist/@types/module/PostgresStorageModule.d.ts +6 -0
  11. package/dist/@types/storage/PostgresBucketStorageFactory.d.ts +42 -0
  12. package/dist/@types/storage/PostgresCompactor.d.ts +40 -0
  13. package/dist/@types/storage/PostgresStorageProvider.d.ts +5 -0
  14. package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +46 -0
  15. package/dist/@types/storage/PostgresTestStorageFactoryGenerator.d.ts +13 -0
  16. package/dist/@types/storage/batch/OperationBatch.d.ts +47 -0
  17. package/dist/@types/storage/batch/PostgresBucketBatch.d.ts +90 -0
  18. package/dist/@types/storage/batch/PostgresPersistedBatch.d.ts +64 -0
  19. package/dist/@types/storage/checkpoints/PostgresWriteCheckpointAPI.d.ts +20 -0
  20. package/dist/@types/storage/storage-index.d.ts +5 -0
  21. package/dist/@types/storage/sync-rules/PostgresPersistedSyncRulesContent.d.ts +17 -0
  22. package/dist/@types/types/codecs.d.ts +61 -0
  23. package/dist/@types/types/models/ActiveCheckpoint.d.ts +12 -0
  24. package/dist/@types/types/models/ActiveCheckpointNotification.d.ts +19 -0
  25. package/dist/@types/types/models/BucketData.d.ts +22 -0
  26. package/dist/@types/types/models/BucketParameters.d.ts +11 -0
  27. package/dist/@types/types/models/CurrentData.d.ts +22 -0
  28. package/dist/@types/types/models/Instance.d.ts +6 -0
  29. package/dist/@types/types/models/Migration.d.ts +12 -0
  30. package/dist/@types/types/models/SourceTable.d.ts +31 -0
  31. package/dist/@types/types/models/SyncRules.d.ts +47 -0
  32. package/dist/@types/types/models/WriteCheckpoint.d.ts +15 -0
  33. package/dist/@types/types/models/models-index.d.ts +10 -0
  34. package/dist/@types/types/types.d.ts +94 -0
  35. package/dist/@types/utils/bson.d.ts +6 -0
  36. package/dist/@types/utils/bucket-data.d.ts +18 -0
  37. package/dist/@types/utils/db.d.ts +8 -0
  38. package/dist/@types/utils/ts-codec.d.ts +5 -0
  39. package/dist/@types/utils/utils-index.d.ts +4 -0
  40. package/dist/index.js +8 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/migrations/PostgresMigrationAgent.js +36 -0
  43. package/dist/migrations/PostgresMigrationAgent.js.map +1 -0
  44. package/dist/migrations/PostgresMigrationStore.js +60 -0
  45. package/dist/migrations/PostgresMigrationStore.js.map +1 -0
  46. package/dist/migrations/migration-utils.js +13 -0
  47. package/dist/migrations/migration-utils.js.map +1 -0
  48. package/dist/migrations/scripts/1684951997326-init.js +196 -0
  49. package/dist/migrations/scripts/1684951997326-init.js.map +1 -0
  50. package/dist/module/PostgresStorageModule.js +23 -0
  51. package/dist/module/PostgresStorageModule.js.map +1 -0
  52. package/dist/storage/PostgresBucketStorageFactory.js +433 -0
  53. package/dist/storage/PostgresBucketStorageFactory.js.map +1 -0
  54. package/dist/storage/PostgresCompactor.js +298 -0
  55. package/dist/storage/PostgresCompactor.js.map +1 -0
  56. package/dist/storage/PostgresStorageProvider.js +35 -0
  57. package/dist/storage/PostgresStorageProvider.js.map +1 -0
  58. package/dist/storage/PostgresSyncRulesStorage.js +619 -0
  59. package/dist/storage/PostgresSyncRulesStorage.js.map +1 -0
  60. package/dist/storage/PostgresTestStorageFactoryGenerator.js +110 -0
  61. package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +1 -0
  62. package/dist/storage/batch/OperationBatch.js +93 -0
  63. package/dist/storage/batch/OperationBatch.js.map +1 -0
  64. package/dist/storage/batch/PostgresBucketBatch.js +732 -0
  65. package/dist/storage/batch/PostgresBucketBatch.js.map +1 -0
  66. package/dist/storage/batch/PostgresPersistedBatch.js +367 -0
  67. package/dist/storage/batch/PostgresPersistedBatch.js.map +1 -0
  68. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js +148 -0
  69. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js.map +1 -0
  70. package/dist/storage/storage-index.js +6 -0
  71. package/dist/storage/storage-index.js.map +1 -0
  72. package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js +58 -0
  73. package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js.map +1 -0
  74. package/dist/types/codecs.js +97 -0
  75. package/dist/types/codecs.js.map +1 -0
  76. package/dist/types/models/ActiveCheckpoint.js +12 -0
  77. package/dist/types/models/ActiveCheckpoint.js.map +1 -0
  78. package/dist/types/models/ActiveCheckpointNotification.js +8 -0
  79. package/dist/types/models/ActiveCheckpointNotification.js.map +1 -0
  80. package/dist/types/models/BucketData.js +23 -0
  81. package/dist/types/models/BucketData.js.map +1 -0
  82. package/dist/types/models/BucketParameters.js +11 -0
  83. package/dist/types/models/BucketParameters.js.map +1 -0
  84. package/dist/types/models/CurrentData.js +16 -0
  85. package/dist/types/models/CurrentData.js.map +1 -0
  86. package/dist/types/models/Instance.js +5 -0
  87. package/dist/types/models/Instance.js.map +1 -0
  88. package/dist/types/models/Migration.js +12 -0
  89. package/dist/types/models/Migration.js.map +1 -0
  90. package/dist/types/models/SourceTable.js +24 -0
  91. package/dist/types/models/SourceTable.js.map +1 -0
  92. package/dist/types/models/SyncRules.js +47 -0
  93. package/dist/types/models/SyncRules.js.map +1 -0
  94. package/dist/types/models/WriteCheckpoint.js +13 -0
  95. package/dist/types/models/WriteCheckpoint.js.map +1 -0
  96. package/dist/types/models/models-index.js +11 -0
  97. package/dist/types/models/models-index.js.map +1 -0
  98. package/dist/types/types.js +46 -0
  99. package/dist/types/types.js.map +1 -0
  100. package/dist/utils/bson.js +16 -0
  101. package/dist/utils/bson.js.map +1 -0
  102. package/dist/utils/bucket-data.js +25 -0
  103. package/dist/utils/bucket-data.js.map +1 -0
  104. package/dist/utils/db.js +24 -0
  105. package/dist/utils/db.js.map +1 -0
  106. package/dist/utils/ts-codec.js +11 -0
  107. package/dist/utils/ts-codec.js.map +1 -0
  108. package/dist/utils/utils-index.js +5 -0
  109. package/dist/utils/utils-index.js.map +1 -0
  110. package/package.json +50 -0
  111. package/src/index.ts +10 -0
  112. package/src/migrations/PostgresMigrationAgent.ts +46 -0
  113. package/src/migrations/PostgresMigrationStore.ts +70 -0
  114. package/src/migrations/migration-utils.ts +14 -0
  115. package/src/migrations/scripts/1684951997326-init.ts +141 -0
  116. package/src/module/PostgresStorageModule.ts +30 -0
  117. package/src/storage/PostgresBucketStorageFactory.ts +496 -0
  118. package/src/storage/PostgresCompactor.ts +366 -0
  119. package/src/storage/PostgresStorageProvider.ts +42 -0
  120. package/src/storage/PostgresSyncRulesStorage.ts +666 -0
  121. package/src/storage/PostgresTestStorageFactoryGenerator.ts +61 -0
  122. package/src/storage/batch/OperationBatch.ts +101 -0
  123. package/src/storage/batch/PostgresBucketBatch.ts +885 -0
  124. package/src/storage/batch/PostgresPersistedBatch.ts +441 -0
  125. package/src/storage/checkpoints/PostgresWriteCheckpointAPI.ts +176 -0
  126. package/src/storage/storage-index.ts +5 -0
  127. package/src/storage/sync-rules/PostgresPersistedSyncRulesContent.ts +67 -0
  128. package/src/types/codecs.ts +136 -0
  129. package/src/types/models/ActiveCheckpoint.ts +15 -0
  130. package/src/types/models/ActiveCheckpointNotification.ts +14 -0
  131. package/src/types/models/BucketData.ts +26 -0
  132. package/src/types/models/BucketParameters.ts +14 -0
  133. package/src/types/models/CurrentData.ts +23 -0
  134. package/src/types/models/Instance.ts +8 -0
  135. package/src/types/models/Migration.ts +19 -0
  136. package/src/types/models/SourceTable.ts +32 -0
  137. package/src/types/models/SyncRules.ts +50 -0
  138. package/src/types/models/WriteCheckpoint.ts +20 -0
  139. package/src/types/models/models-index.ts +10 -0
  140. package/src/types/types.ts +73 -0
  141. package/src/utils/bson.ts +17 -0
  142. package/src/utils/bucket-data.ts +25 -0
  143. package/src/utils/db.ts +27 -0
  144. package/src/utils/ts-codec.ts +14 -0
  145. package/src/utils/utils-index.ts +4 -0
  146. package/test/src/__snapshots__/storage.test.ts.snap +9 -0
  147. package/test/src/__snapshots__/storage_sync.test.ts.snap +332 -0
  148. package/test/src/env.ts +6 -0
  149. package/test/src/migrations.test.ts +34 -0
  150. package/test/src/setup.ts +16 -0
  151. package/test/src/storage.test.ts +131 -0
  152. package/test/src/storage_compacting.test.ts +5 -0
  153. package/test/src/storage_sync.test.ts +12 -0
  154. package/test/src/util.ts +34 -0
  155. package/test/tsconfig.json +20 -0
  156. package/tsconfig.json +36 -0
  157. package/vitest.config.ts +13 -0
@@ -0,0 +1,70 @@
1
+ import * as lib_postgres from '@powersync/lib-service-postgres';
2
+ import { migrations } from '@powersync/lib-services-framework';
3
+ import { models } from '../types/types.js';
4
+ import { sql } from '../utils/db.js';
5
+
6
+ export type PostgresMigrationStoreOptions = {
7
+ db: lib_postgres.DatabaseClient;
8
+ };
9
+
10
+ export class PostgresMigrationStore implements migrations.MigrationStore {
11
+ constructor(protected options: PostgresMigrationStoreOptions) {}
12
+
13
+ protected get db() {
14
+ return this.options.db;
15
+ }
16
+
17
+ async init() {
18
+ await this.db.query(sql`
19
+ CREATE TABLE IF NOT EXISTS migrations (
20
+ id SERIAL PRIMARY KEY,
21
+ last_run TEXT,
22
+ LOG JSONB NOT NULL
23
+ );
24
+ `);
25
+ }
26
+
27
+ async clear() {
28
+ await this.db.query(sql`DELETE FROM migrations;`);
29
+ }
30
+
31
+ async load(): Promise<migrations.MigrationState | undefined> {
32
+ const res = await this.db.sql`
33
+ SELECT
34
+ last_run,
35
+ LOG
36
+ FROM
37
+ migrations
38
+ LIMIT
39
+ 1
40
+ `
41
+ .decoded(models.Migration)
42
+ .first();
43
+
44
+ if (!res) {
45
+ return;
46
+ }
47
+
48
+ return {
49
+ last_run: res.last_run,
50
+ log: res.log
51
+ };
52
+ }
53
+
54
+ async save(state: migrations.MigrationState): Promise<void> {
55
+ await this.db.query(sql`
56
+ INSERT INTO
57
+ migrations (id, last_run, LOG)
58
+ VALUES
59
+ (
60
+ 1,
61
+ ${{ type: 'varchar', value: state.last_run }},
62
+ ${{ type: 'jsonb', value: state.log }}
63
+ )
64
+ ON CONFLICT (id) DO UPDATE
65
+ SET
66
+ last_run = EXCLUDED.last_run,
67
+ LOG = EXCLUDED.log;
68
+ `);
69
+ }
70
+ }
@@ -0,0 +1,14 @@
1
+ import * as lib_postgres from '@powersync/lib-service-postgres';
2
+ import { configFile } from '@powersync/service-types';
3
+ import { isPostgresStorageConfig, normalizePostgresStorageConfig, PostgresStorageConfig } from '../types/types.js';
4
+ import { STORAGE_SCHEMA_NAME } from '../utils/db.js';
5
+
6
+ export const openMigrationDB = (config: configFile.BaseStorageConfig) => {
7
+ if (!isPostgresStorageConfig(config)) {
8
+ throw new Error(`Input storage configuration is not for Postgres`);
9
+ }
10
+ return new lib_postgres.DatabaseClient({
11
+ config: normalizePostgresStorageConfig(PostgresStorageConfig.decode(config)),
12
+ schema: STORAGE_SCHEMA_NAME
13
+ });
14
+ };
@@ -0,0 +1,141 @@
1
+ import { migrations } from '@powersync/service-core';
2
+
3
+ import { dropTables } from '../../utils/db.js';
4
+ import { openMigrationDB } from '../migration-utils.js';
5
+
6
+ export const up: migrations.PowerSyncMigrationFunction = async (context) => {
7
+ const {
8
+ service_context: { configuration }
9
+ } = context;
10
+ await using client = openMigrationDB(configuration.storage);
11
+
12
+ /**
13
+ * Request an explicit connection which will automatically set the search
14
+ * path to the powersync schema
15
+ */
16
+ await client.transaction(async (db) => {
17
+ await db.sql`
18
+ CREATE SEQUENCE op_id_sequence AS int8 START
19
+ WITH
20
+ 1
21
+ `.execute();
22
+
23
+ await db.sql`
24
+ CREATE SEQUENCE sync_rules_id_sequence AS int START
25
+ WITH
26
+ 1
27
+ `.execute();
28
+
29
+ await db.sql`
30
+ CREATE TABLE bucket_data (
31
+ group_id integer NOT NULL,
32
+ bucket_name TEXT NOT NULL,
33
+ op_id bigint NOT NULL,
34
+ CONSTRAINT unique_id PRIMARY KEY (group_id, bucket_name, op_id),
35
+ op text NOT NULL,
36
+ source_table TEXT,
37
+ source_key bytea,
38
+ table_name TEXT,
39
+ row_id TEXT,
40
+ checksum bigint NOT NULL,
41
+ data TEXT,
42
+ target_op bigint
43
+ )
44
+ `.execute();
45
+
46
+ await db.sql`CREATE TABLE instance (id TEXT PRIMARY KEY) `.execute();
47
+
48
+ await db.sql`
49
+ CREATE TABLE sync_rules (
50
+ id INTEGER PRIMARY KEY,
51
+ state TEXT NOT NULL,
52
+ snapshot_done BOOLEAN NOT NULL DEFAULT FALSE,
53
+ last_checkpoint BIGINT,
54
+ last_checkpoint_lsn TEXT,
55
+ no_checkpoint_before TEXT,
56
+ slot_name TEXT,
57
+ last_checkpoint_ts TIMESTAMP WITH TIME ZONE,
58
+ last_keepalive_ts TIMESTAMP WITH TIME ZONE,
59
+ keepalive_op BIGINT,
60
+ last_fatal_error TEXT,
61
+ content TEXT NOT NULL
62
+ );
63
+ `.execute();
64
+
65
+ await db.sql`
66
+ CREATE TABLE bucket_parameters (
67
+ id BIGINT DEFAULT nextval('op_id_sequence') PRIMARY KEY,
68
+ group_id integer NOT NULL,
69
+ source_table TEXT NOT NULL,
70
+ source_key bytea NOT NULL,
71
+ lookup bytea NOT NULL,
72
+ --- Stored as text which is stringified with JSONBig
73
+ --- BigInts are not standard JSON, storing as JSONB seems risky
74
+ bucket_parameters text NOT NULL
75
+ );
76
+ `.execute();
77
+
78
+ await db.sql`
79
+ CREATE INDEX bucket_parameters_lookup_index ON bucket_parameters (group_id ASC, lookup ASC, id DESC)
80
+ `.execute();
81
+
82
+ await db.sql`
83
+ CREATE INDEX bucket_parameters_source_index ON bucket_parameters (group_id, source_table, source_key)
84
+ `.execute();
85
+
86
+ await db.sql`
87
+ CREATE TABLE current_data (
88
+ group_id integer NOT NULL,
89
+ source_table TEXT NOT NULL,
90
+ source_key bytea NOT NULL,
91
+ CONSTRAINT unique_current_data_id PRIMARY KEY (group_id, source_table, source_key),
92
+ buckets jsonb NOT NULL,
93
+ data bytea NOT NULL,
94
+ lookups bytea[] NOT NULL
95
+ );
96
+ `.execute();
97
+
98
+ await db.sql`
99
+ CREATE TABLE source_tables (
100
+ --- This is currently a TEXT column to make the (shared) tests easier to integrate
101
+ --- we could improve this if necessary
102
+ id TEXT PRIMARY KEY,
103
+ group_id integer NOT NULL,
104
+ connection_id integer NOT NULL,
105
+ relation_id jsonb,
106
+ schema_name text NOT NULL,
107
+ table_name text NOT NULL,
108
+ replica_id_columns jsonb,
109
+ snapshot_done BOOLEAN NOT NULL DEFAULT FALSE
110
+ )
111
+ `.execute();
112
+
113
+ await db.sql`CREATE INDEX source_table_lookup ON source_tables (group_id, table_name)`.execute();
114
+
115
+ await db.sql`
116
+ CREATE TABLE write_checkpoints (
117
+ user_id text PRIMARY KEY,
118
+ lsns jsonb NOT NULL,
119
+ write_checkpoint BIGINT NOT NULL
120
+ )
121
+ `.execute();
122
+
123
+ await db.sql`
124
+ CREATE TABLE custom_write_checkpoints (
125
+ user_id text NOT NULL,
126
+ write_checkpoint BIGINT NOT NULL,
127
+ sync_rules_id integer NOT NULL,
128
+ CONSTRAINT unique_user_sync PRIMARY KEY (user_id, sync_rules_id)
129
+ );
130
+ `.execute();
131
+ });
132
+ };
133
+
134
+ export const down: migrations.PowerSyncMigrationFunction = async (context) => {
135
+ const {
136
+ service_context: { configuration }
137
+ } = context;
138
+ await using client = openMigrationDB(configuration.storage);
139
+
140
+ await dropTables(client);
141
+ };
@@ -0,0 +1,30 @@
1
+ import { modules, system } from '@powersync/service-core';
2
+
3
+ import { PostgresMigrationAgent } from '../migrations/PostgresMigrationAgent.js';
4
+ import { PostgresStorageProvider } from '../storage/PostgresStorageProvider.js';
5
+ import { isPostgresStorageConfig, PostgresStorageConfig } from '../types/types.js';
6
+
7
+ export class PostgresStorageModule extends modules.AbstractModule {
8
+ constructor() {
9
+ super({
10
+ name: 'Postgres Bucket Storage'
11
+ });
12
+ }
13
+
14
+ async initialize(context: system.ServiceContextContainer): Promise<void> {
15
+ const { storageEngine } = context;
16
+
17
+ // Register the ability to use Postgres as a BucketStorage
18
+ storageEngine.registerProvider(new PostgresStorageProvider());
19
+
20
+ if (isPostgresStorageConfig(context.configuration.storage)) {
21
+ context.migrations.registerMigrationAgent(
22
+ new PostgresMigrationAgent(PostgresStorageConfig.decode(context.configuration.storage))
23
+ );
24
+ }
25
+ }
26
+
27
+ async teardown(): Promise<void> {
28
+ // Teardown for this module is implemented in the storage engine
29
+ }
30
+ }