@powersync/service-core 0.0.0-dev-20240725112650 → 0.0.0-dev-20240918092408

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 (331) hide show
  1. package/CHANGELOG.md +80 -2
  2. package/dist/api/RouteAPI.d.ts +68 -0
  3. package/dist/api/RouteAPI.js +2 -0
  4. package/dist/api/RouteAPI.js.map +1 -0
  5. package/dist/api/api-index.d.ts +1 -0
  6. package/dist/api/api-index.js +1 -0
  7. package/dist/api/api-index.js.map +1 -1
  8. package/dist/api/diagnostics.d.ts +4 -4
  9. package/dist/api/diagnostics.js +11 -65
  10. package/dist/api/diagnostics.js.map +1 -1
  11. package/dist/api/schema.d.ts +3 -5
  12. package/dist/api/schema.js +9 -79
  13. package/dist/api/schema.js.map +1 -1
  14. package/dist/auth/KeyStore.d.ts +7 -4
  15. package/dist/auth/KeyStore.js +1 -1
  16. package/dist/auth/KeyStore.js.map +1 -1
  17. package/dist/auth/auth-index.d.ts +0 -1
  18. package/dist/auth/auth-index.js +0 -1
  19. package/dist/auth/auth-index.js.map +1 -1
  20. package/dist/entry/cli-entry.js +3 -2
  21. package/dist/entry/cli-entry.js.map +1 -1
  22. package/dist/entry/commands/compact-action.js +12 -8
  23. package/dist/entry/commands/compact-action.js.map +1 -1
  24. package/dist/entry/commands/migrate-action.js +4 -5
  25. package/dist/entry/commands/migrate-action.js.map +1 -1
  26. package/dist/entry/commands/teardown-action.js +2 -2
  27. package/dist/entry/commands/teardown-action.js.map +1 -1
  28. package/dist/index.d.ts +4 -2
  29. package/dist/index.js +4 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/metrics/Metrics.d.ts +2 -2
  32. package/dist/metrics/Metrics.js +5 -13
  33. package/dist/metrics/Metrics.js.map +1 -1
  34. package/dist/migrations/db/migrations/1684951997326-init.d.ts +2 -2
  35. package/dist/migrations/db/migrations/1684951997326-init.js +4 -2
  36. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -1
  37. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +2 -2
  38. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +4 -2
  39. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  40. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +2 -2
  41. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +4 -2
  42. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -1
  43. package/dist/migrations/migrations.d.ts +8 -0
  44. package/dist/migrations/migrations.js +19 -7
  45. package/dist/migrations/migrations.js.map +1 -1
  46. package/dist/modules/AbstractModule.d.ts +26 -0
  47. package/dist/modules/AbstractModule.js +11 -0
  48. package/dist/modules/AbstractModule.js.map +1 -0
  49. package/dist/modules/ModuleManager.d.ts +11 -0
  50. package/dist/modules/ModuleManager.js +32 -0
  51. package/dist/modules/ModuleManager.js.map +1 -0
  52. package/dist/modules/modules-index.d.ts +2 -0
  53. package/dist/modules/modules-index.js +3 -0
  54. package/dist/modules/modules-index.js.map +1 -0
  55. package/dist/replication/AbstractReplicationJob.d.ts +38 -0
  56. package/dist/replication/AbstractReplicationJob.js +51 -0
  57. package/dist/replication/AbstractReplicationJob.js.map +1 -0
  58. package/dist/replication/AbstractReplicator.d.ts +53 -0
  59. package/dist/replication/AbstractReplicator.js +187 -0
  60. package/dist/replication/AbstractReplicator.js.map +1 -0
  61. package/dist/replication/ErrorRateLimiter.d.ts +0 -9
  62. package/dist/replication/ErrorRateLimiter.js +1 -42
  63. package/dist/replication/ErrorRateLimiter.js.map +1 -1
  64. package/dist/replication/ReplicationEngine.d.ts +18 -0
  65. package/dist/replication/ReplicationEngine.js +41 -0
  66. package/dist/replication/ReplicationEngine.js.map +1 -0
  67. package/dist/replication/ReplicationModule.d.ts +39 -0
  68. package/dist/replication/ReplicationModule.js +65 -0
  69. package/dist/replication/ReplicationModule.js.map +1 -0
  70. package/dist/replication/replication-index.d.ts +4 -6
  71. package/dist/replication/replication-index.js +4 -6
  72. package/dist/replication/replication-index.js.map +1 -1
  73. package/dist/routes/RouterEngine.d.ts +42 -0
  74. package/dist/routes/RouterEngine.js +80 -0
  75. package/dist/routes/RouterEngine.js.map +1 -0
  76. package/dist/routes/auth.d.ts +2 -2
  77. package/dist/routes/auth.js +11 -11
  78. package/dist/routes/auth.js.map +1 -1
  79. package/dist/routes/configure-fastify.d.ts +30 -176
  80. package/dist/routes/configure-fastify.js +10 -11
  81. package/dist/routes/configure-fastify.js.map +1 -1
  82. package/dist/routes/configure-rsocket.d.ts +3 -3
  83. package/dist/routes/configure-rsocket.js +6 -5
  84. package/dist/routes/configure-rsocket.js.map +1 -1
  85. package/dist/routes/endpoints/admin.d.ts +0 -34
  86. package/dist/routes/endpoints/admin.js +48 -89
  87. package/dist/routes/endpoints/admin.js.map +1 -1
  88. package/dist/routes/endpoints/checkpointing.d.ts +56 -16
  89. package/dist/routes/endpoints/checkpointing.js +33 -12
  90. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  91. package/dist/routes/endpoints/route-endpoints-index.d.ts +0 -1
  92. package/dist/routes/endpoints/route-endpoints-index.js +0 -1
  93. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -1
  94. package/dist/routes/endpoints/socket-route.js +40 -25
  95. package/dist/routes/endpoints/socket-route.js.map +1 -1
  96. package/dist/routes/endpoints/sync-rules.d.ts +1 -1
  97. package/dist/routes/endpoints/sync-rules.js +32 -23
  98. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  99. package/dist/routes/endpoints/sync-stream.d.ts +10 -0
  100. package/dist/routes/endpoints/sync-stream.js +13 -8
  101. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  102. package/dist/routes/router-socket.d.ts +1 -0
  103. package/dist/routes/router-socket.js +2 -1
  104. package/dist/routes/router-socket.js.map +1 -1
  105. package/dist/routes/router.d.ts +6 -2
  106. package/dist/routes/router.js.map +1 -1
  107. package/dist/routes/routes-index.d.ts +1 -0
  108. package/dist/routes/routes-index.js +1 -0
  109. package/dist/routes/routes-index.js.map +1 -1
  110. package/dist/runner/teardown.js +47 -76
  111. package/dist/runner/teardown.js.map +1 -1
  112. package/dist/storage/BucketStorage.d.ts +30 -19
  113. package/dist/storage/BucketStorage.js +0 -10
  114. package/dist/storage/BucketStorage.js.map +1 -1
  115. package/dist/storage/MongoBucketStorage.d.ts +4 -4
  116. package/dist/storage/MongoBucketStorage.js +19 -24
  117. package/dist/storage/MongoBucketStorage.js.map +1 -1
  118. package/dist/storage/SourceEntity.d.ts +20 -0
  119. package/dist/storage/SourceEntity.js +2 -0
  120. package/dist/storage/SourceEntity.js.map +1 -0
  121. package/dist/storage/SourceTable.d.ts +4 -5
  122. package/dist/storage/SourceTable.js +3 -4
  123. package/dist/storage/SourceTable.js.map +1 -1
  124. package/dist/storage/StorageEngine.d.ts +24 -0
  125. package/dist/storage/StorageEngine.js +43 -0
  126. package/dist/storage/StorageEngine.js.map +1 -0
  127. package/dist/storage/StorageProvider.d.ts +21 -0
  128. package/dist/storage/StorageProvider.js +2 -0
  129. package/dist/storage/StorageProvider.js.map +1 -0
  130. package/dist/storage/mongo/MongoBucketBatch.d.ts +1 -1
  131. package/dist/storage/mongo/MongoBucketBatch.js +6 -7
  132. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
  133. package/dist/storage/mongo/MongoCompactor.js +2 -1
  134. package/dist/storage/mongo/MongoCompactor.js.map +1 -1
  135. package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +2 -2
  136. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +2 -2
  137. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -1
  138. package/dist/storage/mongo/MongoStorageProvider.d.ts +5 -0
  139. package/dist/storage/mongo/MongoStorageProvider.js +26 -0
  140. package/dist/storage/mongo/MongoStorageProvider.js.map +1 -0
  141. package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +7 -6
  142. package/dist/storage/mongo/MongoSyncBucketStorage.js +24 -15
  143. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
  144. package/dist/storage/mongo/MongoSyncRulesLock.js +1 -1
  145. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -1
  146. package/dist/storage/mongo/OperationBatch.d.ts +7 -3
  147. package/dist/storage/mongo/OperationBatch.js +16 -7
  148. package/dist/storage/mongo/OperationBatch.js.map +1 -1
  149. package/dist/storage/mongo/PersistedBatch.d.ts +3 -3
  150. package/dist/storage/mongo/PersistedBatch.js +2 -2
  151. package/dist/storage/mongo/PersistedBatch.js.map +1 -1
  152. package/dist/storage/mongo/models.d.ts +13 -4
  153. package/dist/storage/mongo/models.js.map +1 -1
  154. package/dist/storage/mongo/util.d.ts +12 -1
  155. package/dist/storage/mongo/util.js +50 -2
  156. package/dist/storage/mongo/util.js.map +1 -1
  157. package/dist/storage/storage-index.d.ts +5 -2
  158. package/dist/storage/storage-index.js +5 -2
  159. package/dist/storage/storage-index.js.map +1 -1
  160. package/dist/sync/sync.d.ts +2 -1
  161. package/dist/sync/sync.js +36 -10
  162. package/dist/sync/sync.js.map +1 -1
  163. package/dist/sync/util.js.map +1 -1
  164. package/dist/system/ServiceContext.d.ts +37 -0
  165. package/dist/system/ServiceContext.js +48 -0
  166. package/dist/system/ServiceContext.js.map +1 -0
  167. package/dist/system/system-index.d.ts +1 -1
  168. package/dist/system/system-index.js +1 -1
  169. package/dist/system/system-index.js.map +1 -1
  170. package/dist/util/config/compound-config-collector.d.ts +9 -2
  171. package/dist/util/config/compound-config-collector.js +14 -23
  172. package/dist/util/config/compound-config-collector.js.map +1 -1
  173. package/dist/util/config/sync-rules/sync-rules-provider.d.ts +9 -0
  174. package/dist/util/config/sync-rules/sync-rules-provider.js +15 -0
  175. package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -0
  176. package/dist/util/config/types.d.ts +6 -4
  177. package/dist/util/config/types.js.map +1 -1
  178. package/dist/util/config.d.ts +3 -4
  179. package/dist/util/config.js +5 -20
  180. package/dist/util/config.js.map +1 -1
  181. package/dist/util/protocol-types.d.ts +4 -0
  182. package/dist/util/protocol-types.js +5 -1
  183. package/dist/util/protocol-types.js.map +1 -1
  184. package/dist/util/util-index.d.ts +3 -6
  185. package/dist/util/util-index.js +3 -6
  186. package/dist/util/util-index.js.map +1 -1
  187. package/dist/util/utils.d.ts +10 -6
  188. package/dist/util/utils.js +45 -25
  189. package/dist/util/utils.js.map +1 -1
  190. package/package.json +5 -7
  191. package/src/api/RouteAPI.ts +78 -0
  192. package/src/api/api-index.ts +1 -0
  193. package/src/api/diagnostics.ts +16 -71
  194. package/src/api/schema.ts +13 -89
  195. package/src/auth/KeyStore.ts +9 -6
  196. package/src/auth/auth-index.ts +0 -1
  197. package/src/entry/cli-entry.ts +3 -2
  198. package/src/entry/commands/compact-action.ts +12 -9
  199. package/src/entry/commands/migrate-action.ts +5 -8
  200. package/src/entry/commands/teardown-action.ts +2 -2
  201. package/src/index.ts +5 -2
  202. package/src/metrics/Metrics.ts +6 -16
  203. package/src/migrations/db/migrations/1684951997326-init.ts +9 -4
  204. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -4
  205. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +6 -4
  206. package/src/migrations/migrations.ts +24 -8
  207. package/src/modules/AbstractModule.ts +37 -0
  208. package/src/modules/ModuleManager.ts +34 -0
  209. package/src/modules/modules-index.ts +2 -0
  210. package/src/replication/AbstractReplicationJob.ts +79 -0
  211. package/src/replication/AbstractReplicator.ts +227 -0
  212. package/src/replication/ErrorRateLimiter.ts +0 -44
  213. package/src/replication/ReplicationEngine.ts +43 -0
  214. package/src/replication/ReplicationModule.ts +101 -0
  215. package/src/replication/replication-index.ts +4 -6
  216. package/src/routes/RouterEngine.ts +120 -0
  217. package/src/routes/auth.ts +21 -12
  218. package/src/routes/configure-fastify.ts +13 -14
  219. package/src/routes/configure-rsocket.ts +9 -8
  220. package/src/routes/endpoints/admin.ts +74 -100
  221. package/src/routes/endpoints/checkpointing.ts +46 -12
  222. package/src/routes/endpoints/route-endpoints-index.ts +0 -1
  223. package/src/routes/endpoints/socket-route.ts +44 -27
  224. package/src/routes/endpoints/sync-rules.ts +41 -25
  225. package/src/routes/endpoints/sync-stream.ts +13 -8
  226. package/src/routes/router-socket.ts +2 -1
  227. package/src/routes/router.ts +6 -3
  228. package/src/routes/routes-index.ts +1 -0
  229. package/src/runner/teardown.ts +50 -88
  230. package/src/storage/BucketStorage.ts +38 -25
  231. package/src/storage/MongoBucketStorage.ts +23 -26
  232. package/src/storage/SourceEntity.ts +22 -0
  233. package/src/storage/SourceTable.ts +4 -6
  234. package/src/storage/StorageEngine.ts +55 -0
  235. package/src/storage/StorageProvider.ts +27 -0
  236. package/src/storage/mongo/MongoBucketBatch.ts +8 -8
  237. package/src/storage/mongo/MongoCompactor.ts +2 -1
  238. package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +3 -3
  239. package/src/storage/mongo/MongoStorageProvider.ts +31 -0
  240. package/src/storage/mongo/MongoSyncBucketStorage.ts +39 -20
  241. package/src/storage/mongo/MongoSyncRulesLock.ts +1 -1
  242. package/src/storage/mongo/OperationBatch.ts +18 -11
  243. package/src/storage/mongo/PersistedBatch.ts +6 -5
  244. package/src/storage/mongo/models.ts +13 -4
  245. package/src/storage/mongo/util.ts +49 -4
  246. package/src/storage/storage-index.ts +5 -2
  247. package/src/sync/sync.ts +46 -11
  248. package/src/sync/util.ts +0 -1
  249. package/src/system/ServiceContext.ts +68 -0
  250. package/src/system/system-index.ts +1 -1
  251. package/src/util/config/compound-config-collector.ts +30 -31
  252. package/src/util/config/sync-rules/sync-rules-provider.ts +18 -0
  253. package/src/util/config/types.ts +6 -5
  254. package/src/util/config.ts +6 -23
  255. package/src/util/protocol-types.ts +6 -1
  256. package/src/util/util-index.ts +3 -6
  257. package/src/util/utils.ts +55 -39
  258. package/test/src/__snapshots__/sync.test.ts.snap +7 -7
  259. package/test/src/auth.test.ts +7 -7
  260. package/test/src/broadcast_iterable.test.ts +1 -1
  261. package/test/src/checksum_cache.test.ts +3 -3
  262. package/test/src/compacting.test.ts +26 -17
  263. package/test/src/data_storage.test.ts +258 -146
  264. package/test/src/env.ts +1 -3
  265. package/test/src/merge_iterable.test.ts +1 -6
  266. package/test/src/setup.ts +1 -1
  267. package/test/src/stream_utils.ts +42 -0
  268. package/test/src/sync.test.ts +52 -31
  269. package/test/src/util.ts +48 -51
  270. package/test/tsconfig.json +1 -1
  271. package/tsconfig.tsbuildinfo +1 -1
  272. package/dist/auth/SupabaseKeyCollector.d.ts +0 -22
  273. package/dist/auth/SupabaseKeyCollector.js +0 -61
  274. package/dist/auth/SupabaseKeyCollector.js.map +0 -1
  275. package/dist/replication/PgRelation.d.ts +0 -16
  276. package/dist/replication/PgRelation.js +0 -26
  277. package/dist/replication/PgRelation.js.map +0 -1
  278. package/dist/replication/WalConnection.d.ts +0 -34
  279. package/dist/replication/WalConnection.js +0 -190
  280. package/dist/replication/WalConnection.js.map +0 -1
  281. package/dist/replication/WalStream.d.ts +0 -57
  282. package/dist/replication/WalStream.js +0 -515
  283. package/dist/replication/WalStream.js.map +0 -1
  284. package/dist/replication/WalStreamManager.d.ts +0 -30
  285. package/dist/replication/WalStreamManager.js +0 -198
  286. package/dist/replication/WalStreamManager.js.map +0 -1
  287. package/dist/replication/WalStreamRunner.d.ts +0 -38
  288. package/dist/replication/WalStreamRunner.js +0 -155
  289. package/dist/replication/WalStreamRunner.js.map +0 -1
  290. package/dist/replication/util.d.ts +0 -9
  291. package/dist/replication/util.js +0 -62
  292. package/dist/replication/util.js.map +0 -1
  293. package/dist/routes/endpoints/dev.d.ts +0 -312
  294. package/dist/routes/endpoints/dev.js +0 -172
  295. package/dist/routes/endpoints/dev.js.map +0 -1
  296. package/dist/system/CorePowerSyncSystem.d.ts +0 -23
  297. package/dist/system/CorePowerSyncSystem.js +0 -52
  298. package/dist/system/CorePowerSyncSystem.js.map +0 -1
  299. package/dist/util/PgManager.d.ts +0 -24
  300. package/dist/util/PgManager.js +0 -55
  301. package/dist/util/PgManager.js.map +0 -1
  302. package/dist/util/migration_lib.d.ts +0 -11
  303. package/dist/util/migration_lib.js +0 -64
  304. package/dist/util/migration_lib.js.map +0 -1
  305. package/dist/util/pgwire_utils.d.ts +0 -24
  306. package/dist/util/pgwire_utils.js +0 -117
  307. package/dist/util/pgwire_utils.js.map +0 -1
  308. package/dist/util/populate_test_data.d.ts +0 -8
  309. package/dist/util/populate_test_data.js +0 -65
  310. package/dist/util/populate_test_data.js.map +0 -1
  311. package/src/auth/SupabaseKeyCollector.ts +0 -67
  312. package/src/replication/PgRelation.ts +0 -42
  313. package/src/replication/WalConnection.ts +0 -227
  314. package/src/replication/WalStream.ts +0 -624
  315. package/src/replication/WalStreamManager.ts +0 -213
  316. package/src/replication/WalStreamRunner.ts +0 -180
  317. package/src/replication/util.ts +0 -76
  318. package/src/routes/endpoints/dev.ts +0 -199
  319. package/src/system/CorePowerSyncSystem.ts +0 -64
  320. package/src/util/PgManager.ts +0 -64
  321. package/src/util/migration_lib.ts +0 -79
  322. package/src/util/pgwire_utils.ts +0 -139
  323. package/src/util/populate_test_data.ts +0 -78
  324. package/test/src/__snapshots__/pg_test.test.ts.snap +0 -256
  325. package/test/src/large_batch.test.ts +0 -194
  326. package/test/src/pg_test.test.ts +0 -450
  327. package/test/src/schema_changes.test.ts +0 -545
  328. package/test/src/slow_tests.test.ts +0 -338
  329. package/test/src/validation.test.ts +0 -63
  330. package/test/src/wal_stream.test.ts +0 -319
  331. package/test/src/wal_stream_utils.ts +0 -156
@@ -1,194 +0,0 @@
1
- import { describe, expect, test } from 'vitest';
2
- import { env } from './env.js';
3
- import { MONGO_STORAGE_FACTORY, StorageFactory, TEST_CONNECTION_OPTIONS } from './util.js';
4
- import { walStreamTest } from './wal_stream_utils.js';
5
- import { populateData } from '../../dist/util/populate_test_data.js';
6
-
7
- describe('batch replication tests - mongodb', function () {
8
- // These are slow but consistent tests.
9
- // Not run on every test run, but we do run on CI, or when manually debugging issues.
10
- if (env.CI || env.SLOW_TESTS) {
11
- defineBatchTests(MONGO_STORAGE_FACTORY);
12
- } else {
13
- // Need something in this file.
14
- test('no-op', () => {});
15
- }
16
- });
17
-
18
- const BASIC_SYNC_RULES = `bucket_definitions:
19
- global:
20
- data:
21
- - SELECT id, description, other FROM "test_data"`;
22
-
23
- function defineBatchTests(factory: StorageFactory) {
24
- test(
25
- 'update large record',
26
- walStreamTest(factory, async (context) => {
27
- // This test generates a large transaction in MongoDB, despite the replicated data
28
- // not being that large.
29
- // If we don't limit transaction size, we could run into this error:
30
- // > -31800: transaction is too large and will not fit in the storage engine cache
31
- await context.updateSyncRules(BASIC_SYNC_RULES);
32
- const { pool } = context;
33
-
34
- await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`);
35
-
36
- await context.replicateSnapshot();
37
-
38
- let operation_count = await populateData({
39
- num_transactions: 1,
40
- per_transaction: 80,
41
- size: 4_000_000,
42
- connection: TEST_CONNECTION_OPTIONS
43
- });
44
-
45
- const start = Date.now();
46
-
47
- context.startStreaming();
48
-
49
- const checkpoint = await context.getCheckpoint({ timeout: 100_000 });
50
- const duration = Date.now() - start;
51
- const used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
52
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
53
- expect(checksum.get('global[]')!.count).toEqual(operation_count);
54
- const perSecond = Math.round((operation_count / duration) * 1000);
55
- console.log(`${operation_count} ops in ${duration}ms ${perSecond} ops/s. ${used}MB heap`);
56
- }),
57
- { timeout: 120_000 }
58
- );
59
-
60
- test(
61
- 'initial replication performance',
62
- walStreamTest(factory, async (context) => {
63
- // Manual test to check initial replication performance and memory usage
64
- await context.updateSyncRules(BASIC_SYNC_RULES);
65
- const { pool } = context;
66
-
67
- await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`);
68
-
69
- // Some stats (varies a lot):
70
- // Old 'postgres' driver, using cursor(2)
71
- // 15 ops in 19559ms 1 ops/s. 354MB RSS, 115MB heap, 137MB external
72
- // 25 ops in 42984ms 1 ops/s. 377MB RSS, 129MB heap, 137MB external
73
- // 35 ops in 41337ms 1 ops/s. 365MB RSS, 115MB heap, 137MB external
74
-
75
- // streaming with pgwire
76
- // 15 ops in 26423ms 1 ops/s. 379MB RSS, 128MB heap, 182MB external, 165MB ArrayBuffers
77
- // 35 ops in 78897ms 0 ops/s. 539MB RSS, 52MB heap, 87MB external, 83MB ArrayBuffers
78
-
79
- let operation_count = await populateData({
80
- num_transactions: 1,
81
- per_transaction: 35,
82
- size: 14_000_000,
83
- connection: TEST_CONNECTION_OPTIONS
84
- });
85
-
86
- global.gc?.();
87
-
88
- // Note that we could already have high memory usage at this point
89
- printMemoryUsage();
90
-
91
- let interval = setInterval(() => {
92
- printMemoryUsage();
93
- }, 2000);
94
- try {
95
- const start = Date.now();
96
-
97
- await context.replicateSnapshot();
98
- await context.storage!.autoActivate();
99
- context.startStreaming();
100
-
101
- const checkpoint = await context.getCheckpoint({ timeout: 100_000 });
102
- const duration = Date.now() - start;
103
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
104
- expect(checksum.get('global[]')!.count).toEqual(operation_count);
105
- const perSecond = Math.round((operation_count / duration) * 1000);
106
- console.log(`${operation_count} ops in ${duration}ms ${perSecond} ops/s.`);
107
- printMemoryUsage();
108
- } finally {
109
- clearInterval(interval);
110
- }
111
- }),
112
- { timeout: 120_000 }
113
- );
114
-
115
- test(
116
- 'large number of operations',
117
- walStreamTest(factory, async (context) => {
118
- // This just tests performance of a large number of operations inside a transaction.
119
- await context.updateSyncRules(BASIC_SYNC_RULES);
120
- const { pool } = context;
121
-
122
- await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`);
123
-
124
- await context.replicateSnapshot();
125
-
126
- const numTransactions = 20;
127
- const perTransaction = 1500;
128
- let operationCount = 0;
129
-
130
- const description = 'description';
131
-
132
- for (let i = 0; i < numTransactions; i++) {
133
- const prefix = `test${i}K`;
134
-
135
- await pool.query(
136
- {
137
- statement: `INSERT INTO test_data(id, description, other) SELECT $1 || i, $2 || i, 'foo' FROM generate_series(1, $3) i`,
138
- params: [
139
- { type: 'varchar', value: prefix },
140
- { type: 'varchar', value: description },
141
- { type: 'int4', value: perTransaction }
142
- ]
143
- },
144
- {
145
- statement: `UPDATE test_data SET other = other || '#' WHERE id LIKE $1 || '%'`,
146
- params: [{ type: 'varchar', value: prefix }]
147
- }
148
- );
149
- operationCount += perTransaction * 2;
150
- }
151
-
152
- const start = Date.now();
153
-
154
- context.startStreaming();
155
-
156
- const checkpoint = await context.getCheckpoint({ timeout: 50_000 });
157
- const duration = Date.now() - start;
158
- const used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
159
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
160
- expect(checksum.get('global[]')!.count).toEqual(operationCount);
161
- const perSecond = Math.round((operationCount / duration) * 1000);
162
- // This number depends on the test machine, so we keep the test significantly
163
- // lower than expected numbers.
164
- expect(perSecond).toBeGreaterThan(1000);
165
- console.log(`${operationCount} ops in ${duration}ms ${perSecond} ops/s. ${used}MB heap`);
166
-
167
- // Truncating is fast (~10k ops/second).
168
- // We'd need a really large set of data to actually run into limits when truncating,
169
- // but we just test with the data we have here.
170
- const truncateStart = Date.now();
171
- await pool.query(`TRUNCATE test_data`);
172
-
173
- const checkpoint2 = await context.getCheckpoint({ timeout: 20_000 });
174
- const truncateDuration = Date.now() - truncateStart;
175
-
176
- const checksum2 = await context.storage!.getChecksums(checkpoint2, ['global[]']);
177
- const truncateCount = checksum2.get('global[]')!.count - checksum.get('global[]')!.count;
178
- expect(truncateCount).toEqual(numTransactions * perTransaction);
179
- const truncatePerSecond = Math.round((truncateCount / truncateDuration) * 1000);
180
- console.log(`Truncated ${truncateCount} ops in ${truncateDuration}ms ${truncatePerSecond} ops/s. ${used}MB heap`);
181
- }),
182
- { timeout: 90_000 }
183
- );
184
-
185
- function printMemoryUsage() {
186
- const memoryUsage = process.memoryUsage();
187
-
188
- const rss = Math.round(memoryUsage.rss / 1024 / 1024);
189
- const heap = Math.round(memoryUsage.heapUsed / 1024 / 1024);
190
- const external = Math.round(memoryUsage.external / 1024 / 1024);
191
- const arrayBuffers = Math.round(memoryUsage.arrayBuffers / 1024 / 1024);
192
- console.log(`${rss}MB RSS, ${heap}MB heap, ${external}MB external, ${arrayBuffers}MB ArrayBuffers`);
193
- }
194
- }
@@ -1,450 +0,0 @@
1
- import { describe, expect, test } from 'vitest';
2
- import { WalStream } from '../../src/replication/WalStream.js';
3
- import * as pgwire from '@powersync/service-jpgwire';
4
- import { clearTestDb, connectPgPool, connectPgWire, TEST_URI } from './util.js';
5
- import { constructAfterRecord } from '../../src/util/pgwire_utils.js';
6
- import { SqliteRow } from '@powersync/service-sync-rules';
7
- import { getConnectionSchema } from '../../src/api/schema.js';
8
-
9
- describe('pg data types', () => {
10
- async function setupTable(db: pgwire.PgClient) {
11
- await clearTestDb(db);
12
- await db.query(`CREATE TABLE test_data(
13
- id serial primary key,
14
- text text,
15
- uuid uuid,
16
- varchar varchar(255),
17
- bool bool,
18
- bytea bytea,
19
- int2 int2,
20
- int4 int4,
21
- int8 int8,
22
- float4 float4,
23
- float8 float8,
24
- numeric numeric, -- same as decimal
25
- json json,
26
- jsonb jsonb,
27
- pg_lsn pg_lsn,
28
- date date,
29
- time time,
30
- timestamp timestamp,
31
- timestamptz timestamptz,
32
- interval interval,
33
- macaddr macaddr,
34
- inet inet,
35
- oid oid
36
- )`);
37
-
38
- await db.query(`DROP TABLE IF EXISTS test_data_arrays`);
39
- await db.query(`CREATE TABLE test_data_arrays(
40
- id serial primary key,
41
- text text[],
42
- uuid uuid[],
43
- varchar varchar(255)[],
44
- bool bool[],
45
- bytea bytea[],
46
- int2 int2[],
47
- int4 int4[],
48
- int8 int8[],
49
- float4 float4[],
50
- float8 float8[],
51
- numeric numeric[], -- same as decimal
52
- json json[],
53
- jsonb jsonb[],
54
- pg_lsn pg_lsn[],
55
- date date[],
56
- time time[],
57
- timestamp timestamp[],
58
- timestamptz timestamptz[],
59
- interval interval[],
60
- macaddr macaddr[],
61
- inet inet[],
62
- oid oid[],
63
- multidimensional text[][] -- same as text[]
64
- )`);
65
- }
66
-
67
- async function insert(db: pgwire.PgClient) {
68
- await db.query(`
69
- INSERT INTO test_data(id, text, uuid, varchar, bool, bytea, int2, int4, int8, numeric, float4, float8)
70
- VALUES(1, 'text', 'baeb2514-4c57-436d-b3cc-c1256211656d', 'varchar', true, 'test', 1000, 1000000, 9007199254740993, 18014398509481982123, 3.14, 314);
71
-
72
- INSERT INTO test_data(id, json, jsonb)
73
- VALUES(2, '{"test": "thing" }', '{"test": "thing" }');
74
-
75
- INSERT INTO test_data(id, date, time, timestamp, timestamptz)
76
- VALUES(3, '2023-03-06', '15:47', '2023-03-06 15:47', '2023-03-06 15:47+02');
77
-
78
- INSERT INTO test_data(id, pg_lsn, interval, macaddr, inet, oid)
79
- VALUES(4, '016/B374D848', '1 hour', '00:00:5e:00:53:af', '127.0.0.1', 1007);
80
-
81
- INSERT INTO test_data(id, date, time, timestamp, timestamptz)
82
- VALUES(5, '-infinity'::date, 'allballs'::time, '-infinity'::timestamp, '-infinity'::timestamptz);
83
-
84
- INSERT INTO test_data(id, timestamp, timestamptz)
85
- VALUES(6, 'epoch'::timestamp, 'epoch'::timestamptz);
86
-
87
- INSERT INTO test_data(id, timestamp, timestamptz)
88
- VALUES(7, 'infinity'::timestamp, 'infinity'::timestamptz);
89
-
90
- INSERT INTO test_data(id, timestamptz)
91
- VALUES(8, '0022-02-03 12:13:14+03'::timestamptz);
92
-
93
- INSERT INTO test_data(id, timestamptz)
94
- VALUES(9, '10022-02-03 12:13:14+03'::timestamptz);
95
- `);
96
- }
97
-
98
- async function insertArrays(db: pgwire.PgClient) {
99
- await db.query(`
100
- INSERT INTO test_data_arrays(id, text, uuid, varchar, bool, bytea, int2, int4, int8, numeric)
101
- VALUES(1, ARRAY['text', '}te][xt{"'], '{"baeb2514-4c57-436d-b3cc-c1256211656d"}', '{"varchar"}', '{true}', '{"test"}', '{1000}', '{1000000}', '{9007199254740993}', '{18014398509481982123}');
102
-
103
- INSERT INTO test_data_arrays(id, json, jsonb)
104
- VALUES(2, ARRAY['{"test": "thing"}' :: json, '{"test": "}te][xt{"}' :: json], ARRAY['{"test": "thing", "foo": 5.0, "bignum": 18014398509481982123, "bool":true}' :: jsonb]);
105
-
106
- INSERT INTO test_data_arrays(id, date, time, timestamp, timestamptz)
107
- VALUES(3, ARRAY['2023-03-06'::date], ARRAY['15:47'::time], ARRAY['2023-03-06 15:47'::timestamp], ARRAY['2023-03-06 15:47+02'::timestamptz, '2023-03-06 15:47:00.123450+02'::timestamptz]);
108
-
109
- INSERT INTO test_data_arrays(id, pg_lsn, interval, macaddr, inet, oid)
110
- VALUES(4, ARRAY['016/B374D848'::pg_lsn], ARRAY['1 hour'::interval], ARRAY['00:00:5e:00:53:af'::macaddr], ARRAY['127.0.0.1'::inet], ARRAY[1007::oid]);
111
-
112
- -- Empty arrays
113
- INSERT INTO test_data_arrays(id, text, uuid, varchar, bool, bytea, int2, int4, int8, numeric)
114
- VALUES(5, ARRAY[]::text[], ARRAY[]::uuid[], ARRAY[]::varchar[], ARRAY[]::bool[], ARRAY[]::bytea[], ARRAY[]::int2[], ARRAY[]::int4[], ARRAY[]::int8[], ARRAY[]::numeric[]);
115
-
116
- -- Two-dimentional array
117
- INSERT INTO test_data_arrays(id, multidimensional)
118
- VALUES(6, ARRAY[['one', 1], ['two', 2], ['three', Null]]::TEXT[]);
119
-
120
- -- Empty array
121
- INSERT INTO test_data_arrays(id, multidimensional)
122
- VALUES(7, ARRAY[[], [], []]::TEXT[]);
123
-
124
- -- Empty array
125
- INSERT INTO test_data_arrays(id, multidimensional)
126
- VALUES(8, ARRAY[]::TEXT[]);
127
-
128
- -- Array with only null
129
- INSERT INTO test_data_arrays(id, multidimensional)
130
- VALUES(9, ARRAY[NULL]::TEXT[]);
131
-
132
- -- Array with 'null'
133
- INSERT INTO test_data_arrays(id, multidimensional)
134
- VALUES(10, ARRAY['null']::TEXT[]);
135
- `);
136
- }
137
-
138
- function checkResults(transformed: Record<string, any>[]) {
139
- expect(transformed[0]).toMatchObject({
140
- id: 1n,
141
- text: 'text',
142
- uuid: 'baeb2514-4c57-436d-b3cc-c1256211656d',
143
- varchar: 'varchar',
144
- bool: 1n,
145
- bytea: new Uint8Array([116, 101, 115, 116]),
146
- int2: 1000n,
147
- int4: 1000000n,
148
- int8: 9007199254740993n,
149
- float4: 3.14,
150
- float8: 314,
151
- numeric: '18014398509481982123'
152
- });
153
- expect(transformed[1]).toMatchObject({
154
- id: 2n,
155
- json: '{"test": "thing" }', // Whitespace preserved
156
- jsonb: '{"test": "thing"}' // Whitespace according to pg JSON conventions
157
- });
158
-
159
- expect(transformed[2]).toMatchObject({
160
- id: 3n,
161
- date: '2023-03-06',
162
- time: '15:47:00',
163
- timestamp: '2023-03-06 15:47:00',
164
- timestamptz: '2023-03-06 13:47:00Z'
165
- });
166
-
167
- expect(transformed[3]).toMatchObject({
168
- id: 4n,
169
- pg_lsn: '00000016/B374D848',
170
- interval: '01:00:00',
171
- macaddr: '00:00:5e:00:53:af',
172
- inet: '127.0.0.1',
173
- oid: 1007n
174
- });
175
-
176
- expect(transformed[4]).toMatchObject({
177
- id: 5n,
178
- date: '0000-01-01',
179
- time: '00:00:00',
180
- timestamp: '0000-01-01 00:00:00',
181
- timestamptz: '0000-01-01 00:00:00Z'
182
- });
183
-
184
- expect(transformed[5]).toMatchObject({
185
- id: 6n,
186
- timestamp: '1970-01-01 00:00:00',
187
- timestamptz: '1970-01-01 00:00:00Z'
188
- });
189
-
190
- expect(transformed[6]).toMatchObject({
191
- id: 7n,
192
- timestamp: '9999-12-31 23:59:59',
193
- timestamptz: '9999-12-31 23:59:59Z'
194
- });
195
-
196
- expect(transformed[7]).toMatchObject({
197
- id: 8n,
198
- timestamptz: '0022-02-03 09:13:14Z'
199
- });
200
-
201
- expect(transformed[8]).toMatchObject({
202
- id: 9n,
203
- // 10022-02-03 12:13:14+03 - out of range of both our date parsing logic, and sqlite's date functions
204
- // We can consider just preserving the source string as an alternative if this causes issues.
205
- timestamptz: null
206
- });
207
- }
208
-
209
- function checkResultArrays(transformed: Record<string, any>[]) {
210
- expect(transformed[0]).toMatchObject({
211
- id: 1n,
212
- text: `["text","}te][xt{\\""]`,
213
- uuid: '["baeb2514-4c57-436d-b3cc-c1256211656d"]',
214
- varchar: '["varchar"]',
215
- bool: '[1]',
216
- bytea: '[null]',
217
- int2: '[1000]',
218
- int4: '[1000000]',
219
- int8: `[9007199254740993]`,
220
- numeric: `["18014398509481982123"]`
221
- });
222
-
223
- // Note: Depending on to what extent we use the original postgres value, the whitespace may change, and order may change.
224
- // We do expect that decimals and big numbers are preserved.
225
- expect(transformed[1]).toMatchObject({
226
- id: 2n,
227
- // Expected output after a serialize + parse cycle:
228
- // json: `[{"test":"thing"},{"test":"}te][xt{"}]`,
229
- // jsonb: `[{"foo":5.0,"bool":true,"test":"thing","bignum":18014398509481982123}]`
230
- // Expected using direct PG values:
231
- json: `[{"test": "thing"},{"test": "}te][xt{"}]`,
232
- jsonb: `[{"foo": 5.0, "bool": true, "test": "thing", "bignum": 18014398509481982123}]`
233
- });
234
-
235
- expect(transformed[2]).toMatchObject({
236
- id: 3n,
237
- date: `["2023-03-06"]`,
238
- time: `["15:47:00"]`,
239
- timestamp: `["2023-03-06 15:47:00"]`,
240
- timestamptz: `["2023-03-06 13:47:00Z","2023-03-06 13:47:00.12345Z"]`
241
- });
242
-
243
- expect(transformed[3]).toMatchObject({
244
- id: 4n,
245
- pg_lsn: `["00000016/B374D848"]`,
246
- interval: `["01:00:00"]`,
247
- macaddr: `["00:00:5e:00:53:af"]`,
248
- inet: `["127.0.0.1"]`,
249
- oid: `[1007]`
250
- });
251
-
252
- expect(transformed[4]).toMatchObject({
253
- id: 5n,
254
- text: '[]',
255
- uuid: '[]',
256
- varchar: '[]',
257
- bool: '[]',
258
- bytea: '[]',
259
- int2: '[]',
260
- int4: '[]',
261
- int8: '[]',
262
- numeric: '[]'
263
- });
264
-
265
- expect(transformed[5]).toMatchObject({
266
- id: 6n,
267
- multidimensional: '[["one","1"],["two","2"],["three",null]]'
268
- });
269
-
270
- expect(transformed[6]).toMatchObject({
271
- id: 7n,
272
- multidimensional: '[]'
273
- });
274
-
275
- expect(transformed[7]).toMatchObject({
276
- id: 8n,
277
- multidimensional: '[]'
278
- });
279
-
280
- expect(transformed[8]).toMatchObject({
281
- id: 9n,
282
- multidimensional: '[null]'
283
- });
284
-
285
- expect(transformed[9]).toMatchObject({
286
- id: 10n,
287
- multidimensional: '["null"]'
288
- });
289
- }
290
-
291
- test('test direct queries', async () => {
292
- const db = await connectPgPool();
293
- try {
294
- await setupTable(db);
295
-
296
- await insert(db);
297
-
298
- const transformed = [
299
- ...WalStream.getQueryData(pgwire.pgwireRows(await db.query(`SELECT * FROM test_data ORDER BY id`)))
300
- ];
301
-
302
- checkResults(transformed);
303
- } finally {
304
- await db.end();
305
- }
306
- });
307
-
308
- test('test direct queries - parameterized', async () => {
309
- // Parameterized queries may use a different underlying protocol,
310
- // so we make sure it also gets the same results.
311
- const db = await connectPgPool();
312
- try {
313
- await setupTable(db);
314
-
315
- await insert(db);
316
-
317
- const transformed = [
318
- ...WalStream.getQueryData(
319
- pgwire.pgwireRows(
320
- await db.query({
321
- statement: `SELECT * FROM test_data WHERE $1 ORDER BY id`,
322
- params: [{ type: 'bool', value: true }]
323
- })
324
- )
325
- )
326
- ];
327
-
328
- checkResults(transformed);
329
- } finally {
330
- await db.end();
331
- }
332
- });
333
-
334
- test('test direct queries - arrays', async () => {
335
- const db = await connectPgPool();
336
- try {
337
- await setupTable(db);
338
-
339
- await insertArrays(db);
340
-
341
- const transformed = [
342
- ...WalStream.getQueryData(pgwire.pgwireRows(await db.query(`SELECT * FROM test_data_arrays ORDER BY id`)))
343
- ];
344
-
345
- checkResultArrays(transformed);
346
- } finally {
347
- await db.end();
348
- }
349
- });
350
-
351
- test('test replication', async () => {
352
- const db = await connectPgPool();
353
- try {
354
- await setupTable(db);
355
-
356
- const slotName = 'test_slot';
357
-
358
- await db.query({
359
- statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1',
360
- params: [{ type: 'varchar', value: slotName }]
361
- });
362
-
363
- await db.query({
364
- statement: `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')`,
365
- params: [{ type: 'varchar', value: slotName }]
366
- });
367
-
368
- await insert(db);
369
-
370
- const pg: pgwire.PgConnection = await pgwire.pgconnect({ replication: 'database' }, TEST_URI);
371
- const replicationStream = await pg.logicalReplication({
372
- slot: slotName,
373
- options: {
374
- proto_version: '1',
375
- publication_names: 'powersync'
376
- }
377
- });
378
-
379
- const transformed = await getReplicationTx(replicationStream);
380
- await pg.end();
381
-
382
- checkResults(transformed);
383
- } finally {
384
- await db.end();
385
- }
386
- });
387
-
388
- test('test replication - arrays', async () => {
389
- const db = await connectPgPool();
390
- try {
391
- await setupTable(db);
392
-
393
- const slotName = 'test_slot';
394
-
395
- await db.query({
396
- statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1',
397
- params: [{ type: 'varchar', value: slotName }]
398
- });
399
-
400
- await db.query({
401
- statement: `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')`,
402
- params: [{ type: 'varchar', value: slotName }]
403
- });
404
-
405
- await insertArrays(db);
406
-
407
- const pg: pgwire.PgConnection = await pgwire.pgconnect({ replication: 'database' }, TEST_URI);
408
- const replicationStream = await pg.logicalReplication({
409
- slot: slotName,
410
- options: {
411
- proto_version: '1',
412
- publication_names: 'powersync'
413
- }
414
- });
415
-
416
- const transformed = await getReplicationTx(replicationStream);
417
- await pg.end();
418
-
419
- checkResultArrays(transformed);
420
- } finally {
421
- await db.end();
422
- }
423
- });
424
-
425
- test('schema', async function () {
426
- const db = await connectPgWire();
427
-
428
- await setupTable(db);
429
-
430
- const schema = await getConnectionSchema(db);
431
- expect(schema).toMatchSnapshot();
432
- });
433
- });
434
-
435
- /**
436
- * Return all the inserts from the first transaction in the replication stream.
437
- */
438
- async function getReplicationTx(replicationStream: pgwire.ReplicationStream) {
439
- let transformed: SqliteRow[] = [];
440
- for await (const batch of replicationStream.pgoutputDecode()) {
441
- for (const msg of batch.messages) {
442
- if (msg.tag == 'insert') {
443
- transformed.push(constructAfterRecord(msg));
444
- } else if (msg.tag == 'commit') {
445
- return transformed;
446
- }
447
- }
448
- }
449
- return transformed;
450
- }