@powersync/service-core 0.8.8 → 0.9.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 (372) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/api/RouteAPI.d.ts +67 -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 +170 -158
  10. package/dist/api/diagnostics.js.map +1 -1
  11. package/dist/api/schema.d.ts +3 -5
  12. package/dist/api/schema.js +14 -80
  13. package/dist/api/schema.js.map +1 -1
  14. package/dist/auth/CachedKeyCollector.js.map +1 -1
  15. package/dist/auth/KeySpec.js.map +1 -1
  16. package/dist/auth/KeyStore.d.ts +7 -4
  17. package/dist/auth/KeyStore.js +1 -1
  18. package/dist/auth/KeyStore.js.map +1 -1
  19. package/dist/auth/LeakyBucket.js.map +1 -1
  20. package/dist/auth/RemoteJWKSCollector.d.ts +0 -2
  21. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  22. package/dist/auth/auth-index.d.ts +0 -1
  23. package/dist/auth/auth-index.js +0 -1
  24. package/dist/auth/auth-index.js.map +1 -1
  25. package/dist/db/mongo.js +5 -3
  26. package/dist/db/mongo.js.map +1 -1
  27. package/dist/entry/cli-entry.js +3 -2
  28. package/dist/entry/cli-entry.js.map +1 -1
  29. package/dist/entry/commands/compact-action.js +90 -14
  30. package/dist/entry/commands/compact-action.js.map +1 -1
  31. package/dist/entry/commands/migrate-action.js +4 -5
  32. package/dist/entry/commands/migrate-action.js.map +1 -1
  33. package/dist/entry/commands/teardown-action.js +2 -2
  34. package/dist/entry/commands/teardown-action.js.map +1 -1
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.js +4 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/locks/MongoLocks.js.map +1 -1
  39. package/dist/metrics/Metrics.d.ts +2 -2
  40. package/dist/metrics/Metrics.js +5 -13
  41. package/dist/metrics/Metrics.js.map +1 -1
  42. package/dist/migrations/db/migrations/1684951997326-init.d.ts +2 -2
  43. package/dist/migrations/db/migrations/1684951997326-init.js +4 -2
  44. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -1
  45. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +2 -2
  46. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +4 -2
  47. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  48. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +2 -2
  49. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +4 -2
  50. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -1
  51. package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.d.ts +3 -0
  52. package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js +31 -0
  53. package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js.map +1 -0
  54. package/dist/migrations/executor.js.map +1 -1
  55. package/dist/migrations/migrations.d.ts +8 -0
  56. package/dist/migrations/migrations.js +19 -7
  57. package/dist/migrations/migrations.js.map +1 -1
  58. package/dist/migrations/store/migration-store.js.map +1 -1
  59. package/dist/modules/AbstractModule.d.ts +26 -0
  60. package/dist/modules/AbstractModule.js +11 -0
  61. package/dist/modules/AbstractModule.js.map +1 -0
  62. package/dist/modules/ModuleManager.d.ts +11 -0
  63. package/dist/modules/ModuleManager.js +32 -0
  64. package/dist/modules/ModuleManager.js.map +1 -0
  65. package/dist/modules/modules-index.d.ts +2 -0
  66. package/dist/modules/modules-index.js +3 -0
  67. package/dist/modules/modules-index.js.map +1 -0
  68. package/dist/replication/AbstractReplicationJob.d.ts +37 -0
  69. package/dist/replication/AbstractReplicationJob.js +51 -0
  70. package/dist/replication/AbstractReplicationJob.js.map +1 -0
  71. package/dist/replication/AbstractReplicator.d.ts +53 -0
  72. package/dist/replication/AbstractReplicator.js +250 -0
  73. package/dist/replication/AbstractReplicator.js.map +1 -0
  74. package/dist/replication/ErrorRateLimiter.d.ts +0 -10
  75. package/dist/replication/ErrorRateLimiter.js +1 -42
  76. package/dist/replication/ErrorRateLimiter.js.map +1 -1
  77. package/dist/replication/ReplicationEngine.d.ts +18 -0
  78. package/dist/replication/ReplicationEngine.js +41 -0
  79. package/dist/replication/ReplicationEngine.js.map +1 -0
  80. package/dist/replication/ReplicationModule.d.ts +51 -0
  81. package/dist/replication/ReplicationModule.js +68 -0
  82. package/dist/replication/ReplicationModule.js.map +1 -0
  83. package/dist/replication/replication-index.d.ts +4 -6
  84. package/dist/replication/replication-index.js +4 -6
  85. package/dist/replication/replication-index.js.map +1 -1
  86. package/dist/routes/RouterEngine.d.ts +42 -0
  87. package/dist/routes/RouterEngine.js +80 -0
  88. package/dist/routes/RouterEngine.js.map +1 -0
  89. package/dist/routes/auth.d.ts +2 -2
  90. package/dist/routes/auth.js +11 -11
  91. package/dist/routes/auth.js.map +1 -1
  92. package/dist/routes/configure-fastify.d.ts +37 -23
  93. package/dist/routes/configure-fastify.js +18 -18
  94. package/dist/routes/configure-fastify.js.map +1 -1
  95. package/dist/routes/configure-rsocket.d.ts +3 -4
  96. package/dist/routes/configure-rsocket.js +7 -4
  97. package/dist/routes/configure-rsocket.js.map +1 -1
  98. package/dist/routes/endpoints/admin.d.ts +30 -0
  99. package/dist/routes/endpoints/admin.js +46 -67
  100. package/dist/routes/endpoints/admin.js.map +1 -1
  101. package/dist/routes/endpoints/checkpointing.js +103 -15
  102. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  103. package/dist/routes/endpoints/socket-route.js +8 -6
  104. package/dist/routes/endpoints/socket-route.js.map +1 -1
  105. package/dist/routes/endpoints/sync-rules.d.ts +1 -1
  106. package/dist/routes/endpoints/sync-rules.js +32 -23
  107. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  108. package/dist/routes/endpoints/sync-stream.d.ts +0 -1
  109. package/dist/routes/endpoints/sync-stream.js +8 -8
  110. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  111. package/dist/routes/hooks.js.map +1 -1
  112. package/dist/routes/route-register.js.map +1 -1
  113. package/dist/routes/router.d.ts +9 -2
  114. package/dist/routes/router.js.map +1 -1
  115. package/dist/routes/routes-index.d.ts +1 -0
  116. package/dist/routes/routes-index.js +1 -0
  117. package/dist/routes/routes-index.js.map +1 -1
  118. package/dist/runner/teardown.js +109 -76
  119. package/dist/runner/teardown.js.map +1 -1
  120. package/dist/storage/BucketStorage.d.ts +86 -36
  121. package/dist/storage/BucketStorage.js +6 -10
  122. package/dist/storage/BucketStorage.js.map +1 -1
  123. package/dist/storage/ChecksumCache.js.map +1 -1
  124. package/dist/storage/MongoBucketStorage.d.ts +7 -11
  125. package/dist/storage/MongoBucketStorage.js +48 -41
  126. package/dist/storage/MongoBucketStorage.js.map +1 -1
  127. package/dist/storage/ReplicationEventPayload.d.ts +14 -0
  128. package/dist/storage/ReplicationEventPayload.js +2 -0
  129. package/dist/storage/ReplicationEventPayload.js.map +1 -0
  130. package/dist/storage/SourceEntity.d.ts +20 -0
  131. package/dist/storage/SourceEntity.js +2 -0
  132. package/dist/storage/SourceEntity.js.map +1 -0
  133. package/dist/storage/SourceTable.d.ts +12 -5
  134. package/dist/storage/SourceTable.js +12 -5
  135. package/dist/storage/SourceTable.js.map +1 -1
  136. package/dist/storage/StorageEngine.d.ts +28 -0
  137. package/dist/storage/StorageEngine.js +45 -0
  138. package/dist/storage/StorageEngine.js.map +1 -0
  139. package/dist/storage/StorageProvider.d.ts +21 -0
  140. package/dist/storage/StorageProvider.js +2 -0
  141. package/dist/storage/StorageProvider.js.map +1 -0
  142. package/dist/storage/WriteCheckpointAPI.d.ts +74 -0
  143. package/dist/storage/WriteCheckpointAPI.js +16 -0
  144. package/dist/storage/WriteCheckpointAPI.js.map +1 -0
  145. package/dist/storage/mongo/MongoBucketBatch.d.ts +24 -5
  146. package/dist/storage/mongo/MongoBucketBatch.js +119 -62
  147. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
  148. package/dist/storage/mongo/MongoCompactor.js +20 -3
  149. package/dist/storage/mongo/MongoCompactor.js.map +1 -1
  150. package/dist/storage/mongo/MongoIdSequence.js.map +1 -1
  151. package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +2 -2
  152. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +2 -2
  153. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -1
  154. package/dist/storage/mongo/MongoStorageProvider.d.ts +5 -0
  155. package/dist/storage/mongo/MongoStorageProvider.js +26 -0
  156. package/dist/storage/mongo/MongoStorageProvider.js.map +1 -0
  157. package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +18 -10
  158. package/dist/storage/mongo/MongoSyncBucketStorage.js +140 -25
  159. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
  160. package/dist/storage/mongo/MongoSyncRulesLock.js +1 -1
  161. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -1
  162. package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +20 -0
  163. package/dist/storage/mongo/MongoWriteCheckpointAPI.js +103 -0
  164. package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +1 -0
  165. package/dist/storage/mongo/OperationBatch.d.ts +13 -4
  166. package/dist/storage/mongo/OperationBatch.js +25 -7
  167. package/dist/storage/mongo/OperationBatch.js.map +1 -1
  168. package/dist/storage/mongo/PersistedBatch.d.ts +3 -3
  169. package/dist/storage/mongo/PersistedBatch.js +2 -2
  170. package/dist/storage/mongo/PersistedBatch.js.map +1 -1
  171. package/dist/storage/mongo/config.d.ts +19 -0
  172. package/dist/storage/mongo/config.js +26 -0
  173. package/dist/storage/mongo/config.js.map +1 -0
  174. package/dist/storage/mongo/db.d.ts +3 -2
  175. package/dist/storage/mongo/db.js +1 -0
  176. package/dist/storage/mongo/db.js.map +1 -1
  177. package/dist/storage/mongo/models.d.ts +20 -5
  178. package/dist/storage/mongo/models.js.map +1 -1
  179. package/dist/storage/mongo/util.d.ts +12 -1
  180. package/dist/storage/mongo/util.js +50 -2
  181. package/dist/storage/mongo/util.js.map +1 -1
  182. package/dist/storage/storage-index.d.ts +8 -2
  183. package/dist/storage/storage-index.js +8 -2
  184. package/dist/storage/storage-index.js.map +1 -1
  185. package/dist/sync/BroadcastIterable.d.ts +0 -1
  186. package/dist/sync/BroadcastIterable.js.map +1 -1
  187. package/dist/sync/LastValueSink.d.ts +0 -1
  188. package/dist/sync/LastValueSink.js.map +1 -1
  189. package/dist/sync/merge.d.ts +0 -1
  190. package/dist/sync/merge.js.map +1 -1
  191. package/dist/sync/safeRace.js.map +1 -1
  192. package/dist/sync/sync.d.ts +1 -1
  193. package/dist/sync/sync.js +5 -5
  194. package/dist/sync/sync.js.map +1 -1
  195. package/dist/sync/util.d.ts +0 -2
  196. package/dist/sync/util.js.map +1 -1
  197. package/dist/system/ServiceContext.d.ts +37 -0
  198. package/dist/system/ServiceContext.js +48 -0
  199. package/dist/system/ServiceContext.js.map +1 -0
  200. package/dist/system/system-index.d.ts +1 -1
  201. package/dist/system/system-index.js +1 -1
  202. package/dist/system/system-index.js.map +1 -1
  203. package/dist/util/Mutex.js.map +1 -1
  204. package/dist/util/config/collectors/config-collector.js.map +1 -1
  205. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
  206. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
  207. package/dist/util/config/compound-config-collector.d.ts +9 -2
  208. package/dist/util/config/compound-config-collector.js +16 -24
  209. package/dist/util/config/compound-config-collector.js.map +1 -1
  210. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
  211. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  212. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
  213. package/dist/util/config/sync-rules/sync-rules-provider.d.ts +9 -0
  214. package/dist/util/config/sync-rules/sync-rules-provider.js +15 -0
  215. package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -0
  216. package/dist/util/config/types.d.ts +7 -4
  217. package/dist/util/config/types.js.map +1 -1
  218. package/dist/util/config.d.ts +3 -4
  219. package/dist/util/config.js +5 -20
  220. package/dist/util/config.js.map +1 -1
  221. package/dist/util/memory-tracking.js.map +1 -1
  222. package/dist/util/secs.js.map +1 -1
  223. package/dist/util/util-index.d.ts +3 -6
  224. package/dist/util/util-index.js +3 -6
  225. package/dist/util/util-index.js.map +1 -1
  226. package/dist/util/utils.d.ts +10 -7
  227. package/dist/util/utils.js +36 -25
  228. package/dist/util/utils.js.map +1 -1
  229. package/package.json +8 -12
  230. package/src/api/RouteAPI.ts +78 -0
  231. package/src/api/api-index.ts +1 -0
  232. package/src/api/diagnostics.ts +18 -70
  233. package/src/api/schema.ts +18 -90
  234. package/src/auth/KeyStore.ts +9 -6
  235. package/src/auth/RemoteJWKSCollector.ts +4 -1
  236. package/src/auth/auth-index.ts +0 -1
  237. package/src/db/mongo.ts +5 -3
  238. package/src/entry/cli-entry.ts +3 -2
  239. package/src/entry/commands/compact-action.ts +24 -12
  240. package/src/entry/commands/migrate-action.ts +5 -8
  241. package/src/entry/commands/teardown-action.ts +2 -2
  242. package/src/index.ts +5 -2
  243. package/src/metrics/Metrics.ts +6 -16
  244. package/src/migrations/db/migrations/1684951997326-init.ts +9 -4
  245. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -4
  246. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +6 -4
  247. package/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts +37 -0
  248. package/src/migrations/migrations.ts +24 -8
  249. package/src/modules/AbstractModule.ts +37 -0
  250. package/src/modules/ModuleManager.ts +34 -0
  251. package/src/modules/modules-index.ts +2 -0
  252. package/src/replication/AbstractReplicationJob.ts +79 -0
  253. package/src/replication/AbstractReplicator.ts +228 -0
  254. package/src/replication/ErrorRateLimiter.ts +0 -44
  255. package/src/replication/ReplicationEngine.ts +43 -0
  256. package/src/replication/ReplicationModule.ts +122 -0
  257. package/src/replication/replication-index.ts +4 -6
  258. package/src/routes/RouterEngine.ts +120 -0
  259. package/src/routes/auth.ts +21 -12
  260. package/src/routes/configure-fastify.ts +26 -27
  261. package/src/routes/configure-rsocket.ts +13 -8
  262. package/src/routes/endpoints/admin.ts +72 -76
  263. package/src/routes/endpoints/checkpointing.ts +51 -11
  264. package/src/routes/endpoints/socket-route.ts +10 -6
  265. package/src/routes/endpoints/sync-rules.ts +41 -25
  266. package/src/routes/endpoints/sync-stream.ts +8 -8
  267. package/src/routes/router.ts +8 -3
  268. package/src/routes/routes-index.ts +1 -0
  269. package/src/runner/teardown.ts +50 -88
  270. package/src/storage/BucketStorage.ts +103 -41
  271. package/src/storage/MongoBucketStorage.ts +65 -53
  272. package/src/storage/ReplicationEventPayload.ts +16 -0
  273. package/src/storage/SourceEntity.ts +22 -0
  274. package/src/storage/SourceTable.ts +14 -7
  275. package/src/storage/StorageEngine.ts +62 -0
  276. package/src/storage/StorageProvider.ts +27 -0
  277. package/src/storage/WriteCheckpointAPI.ts +85 -0
  278. package/src/storage/mongo/MongoBucketBatch.ts +164 -84
  279. package/src/storage/mongo/MongoCompactor.ts +25 -4
  280. package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +7 -4
  281. package/src/storage/mongo/MongoStorageProvider.ts +31 -0
  282. package/src/storage/mongo/MongoSyncBucketStorage.ts +118 -41
  283. package/src/storage/mongo/MongoSyncRulesLock.ts +7 -3
  284. package/src/storage/mongo/MongoWriteCheckpointAPI.ts +151 -0
  285. package/src/storage/mongo/OperationBatch.ts +28 -12
  286. package/src/storage/mongo/PersistedBatch.ts +10 -6
  287. package/src/storage/mongo/config.ts +40 -0
  288. package/src/storage/mongo/db.ts +4 -1
  289. package/src/storage/mongo/models.ts +21 -5
  290. package/src/storage/mongo/util.ts +48 -3
  291. package/src/storage/storage-index.ts +8 -2
  292. package/src/sync/sync.ts +7 -4
  293. package/src/sync/util.ts +0 -1
  294. package/src/system/ServiceContext.ts +68 -0
  295. package/src/system/system-index.ts +1 -1
  296. package/src/util/config/compound-config-collector.ts +31 -31
  297. package/src/util/config/sync-rules/sync-rules-provider.ts +18 -0
  298. package/src/util/config/types.ts +7 -5
  299. package/src/util/config.ts +6 -23
  300. package/src/util/util-index.ts +3 -6
  301. package/src/util/utils.ts +48 -41
  302. package/test/src/__snapshots__/sync.test.ts.snap +14 -14
  303. package/test/src/auth.test.ts +7 -7
  304. package/test/src/broadcast_iterable.test.ts +1 -1
  305. package/test/src/compacting.test.ts +50 -40
  306. package/test/src/data_storage.test.ts +382 -202
  307. package/test/src/env.ts +1 -3
  308. package/test/src/merge_iterable.test.ts +1 -6
  309. package/test/src/routes/probes.integration.test.ts +34 -30
  310. package/test/src/setup.ts +1 -1
  311. package/test/src/stream_utils.ts +42 -0
  312. package/test/src/sync.test.ts +115 -39
  313. package/test/src/util.ts +48 -51
  314. package/test/tsconfig.json +1 -1
  315. package/tsconfig.tsbuildinfo +1 -1
  316. package/vitest.config.ts +7 -1
  317. package/dist/auth/SupabaseKeyCollector.d.ts +0 -22
  318. package/dist/auth/SupabaseKeyCollector.js +0 -61
  319. package/dist/auth/SupabaseKeyCollector.js.map +0 -1
  320. package/dist/replication/PgRelation.d.ts +0 -16
  321. package/dist/replication/PgRelation.js +0 -26
  322. package/dist/replication/PgRelation.js.map +0 -1
  323. package/dist/replication/WalConnection.d.ts +0 -34
  324. package/dist/replication/WalConnection.js +0 -190
  325. package/dist/replication/WalConnection.js.map +0 -1
  326. package/dist/replication/WalStream.d.ts +0 -57
  327. package/dist/replication/WalStream.js +0 -519
  328. package/dist/replication/WalStream.js.map +0 -1
  329. package/dist/replication/WalStreamManager.d.ts +0 -30
  330. package/dist/replication/WalStreamManager.js +0 -198
  331. package/dist/replication/WalStreamManager.js.map +0 -1
  332. package/dist/replication/WalStreamRunner.d.ts +0 -38
  333. package/dist/replication/WalStreamRunner.js +0 -155
  334. package/dist/replication/WalStreamRunner.js.map +0 -1
  335. package/dist/replication/util.d.ts +0 -9
  336. package/dist/replication/util.js +0 -62
  337. package/dist/replication/util.js.map +0 -1
  338. package/dist/system/CorePowerSyncSystem.d.ts +0 -23
  339. package/dist/system/CorePowerSyncSystem.js +0 -52
  340. package/dist/system/CorePowerSyncSystem.js.map +0 -1
  341. package/dist/util/PgManager.d.ts +0 -24
  342. package/dist/util/PgManager.js +0 -55
  343. package/dist/util/PgManager.js.map +0 -1
  344. package/dist/util/migration_lib.d.ts +0 -11
  345. package/dist/util/migration_lib.js +0 -64
  346. package/dist/util/migration_lib.js.map +0 -1
  347. package/dist/util/pgwire_utils.d.ts +0 -24
  348. package/dist/util/pgwire_utils.js +0 -117
  349. package/dist/util/pgwire_utils.js.map +0 -1
  350. package/dist/util/populate_test_data.d.ts +0 -8
  351. package/dist/util/populate_test_data.js +0 -65
  352. package/dist/util/populate_test_data.js.map +0 -1
  353. package/src/auth/SupabaseKeyCollector.ts +0 -67
  354. package/src/replication/PgRelation.ts +0 -42
  355. package/src/replication/WalConnection.ts +0 -227
  356. package/src/replication/WalStream.ts +0 -631
  357. package/src/replication/WalStreamManager.ts +0 -213
  358. package/src/replication/WalStreamRunner.ts +0 -180
  359. package/src/replication/util.ts +0 -76
  360. package/src/system/CorePowerSyncSystem.ts +0 -64
  361. package/src/util/PgManager.ts +0 -64
  362. package/src/util/migration_lib.ts +0 -79
  363. package/src/util/pgwire_utils.ts +0 -139
  364. package/src/util/populate_test_data.ts +0 -78
  365. package/test/src/__snapshots__/pg_test.test.ts.snap +0 -256
  366. package/test/src/large_batch.test.ts +0 -194
  367. package/test/src/pg_test.test.ts +0 -450
  368. package/test/src/schema_changes.test.ts +0 -545
  369. package/test/src/slow_tests.test.ts +0 -338
  370. package/test/src/validation.test.ts +0 -63
  371. package/test/src/wal_stream.test.ts +0 -319
  372. package/test/src/wal_stream_utils.ts +0 -156
@@ -1,319 +0,0 @@
1
- import * as crypto from 'crypto';
2
- import { describe, expect, test } from 'vitest';
3
- import { BucketStorageFactory } from '@/storage/BucketStorage.js';
4
- import { MONGO_STORAGE_FACTORY } from './util.js';
5
- import { putOp, removeOp, walStreamTest } from './wal_stream_utils.js';
6
- import { pgwireRows } from '@powersync/service-jpgwire';
7
- import { Metrics } from '@/metrics/Metrics.js';
8
-
9
- type StorageFactory = () => Promise<BucketStorageFactory>;
10
-
11
- const BASIC_SYNC_RULES = `
12
- bucket_definitions:
13
- global:
14
- data:
15
- - SELECT id, description FROM "test_data"
16
- `;
17
-
18
- describe(
19
- 'wal stream - mongodb',
20
- function () {
21
- defineWalStreamTests(MONGO_STORAGE_FACTORY);
22
- },
23
- { timeout: 20_000 }
24
- );
25
-
26
- function defineWalStreamTests(factory: StorageFactory) {
27
- test(
28
- 'replicating basic values',
29
- walStreamTest(factory, async (context) => {
30
- const { pool } = context;
31
- await context.updateSyncRules(`
32
- bucket_definitions:
33
- global:
34
- data:
35
- - SELECT id, description, num FROM "test_data"`);
36
-
37
- await pool.query(`DROP TABLE IF EXISTS test_data`);
38
- await pool.query(
39
- `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text, num int8)`
40
- );
41
-
42
- await context.replicateSnapshot();
43
-
44
- const startRowCount =
45
- (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
46
- const startTxCount =
47
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
48
-
49
- context.startStreaming();
50
-
51
- const [{ test_id }] = pgwireRows(
52
- await pool.query(
53
- `INSERT INTO test_data(description, num) VALUES('test1', 1152921504606846976) returning id as test_id`
54
- )
55
- );
56
-
57
- const data = await context.getBucketData('global[]');
58
-
59
- expect(data).toMatchObject([
60
- putOp('test_data', { id: test_id, description: 'test1', num: 1152921504606846976n })
61
- ]);
62
- const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
63
- const endTxCount =
64
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
65
- expect(endRowCount - startRowCount).toEqual(1);
66
- expect(endTxCount - startTxCount).toEqual(1);
67
- })
68
- );
69
-
70
- test(
71
- 'replicating case sensitive table',
72
- walStreamTest(factory, async (context) => {
73
- const { pool } = context;
74
- await context.updateSyncRules(`
75
- bucket_definitions:
76
- global:
77
- data:
78
- - SELECT id, description FROM "test_DATA"
79
- `);
80
-
81
- await pool.query(`DROP TABLE IF EXISTS "test_DATA"`);
82
- await pool.query(`CREATE TABLE "test_DATA"(id uuid primary key default uuid_generate_v4(), description text)`);
83
-
84
- await context.replicateSnapshot();
85
-
86
- const startRowCount =
87
- (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
88
- const startTxCount =
89
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
90
-
91
- context.startStreaming();
92
-
93
- const [{ test_id }] = pgwireRows(
94
- await pool.query(`INSERT INTO "test_DATA"(description) VALUES('test1') returning id as test_id`)
95
- );
96
-
97
- const data = await context.getBucketData('global[]');
98
-
99
- expect(data).toMatchObject([putOp('test_DATA', { id: test_id, description: 'test1' })]);
100
- const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
101
- const endTxCount =
102
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
103
- expect(endRowCount - startRowCount).toEqual(1);
104
- expect(endTxCount - startTxCount).toEqual(1);
105
- })
106
- );
107
-
108
- test(
109
- 'replicating TOAST values',
110
- walStreamTest(factory, async (context) => {
111
- const { pool } = context;
112
- await context.updateSyncRules(`
113
- bucket_definitions:
114
- global:
115
- data:
116
- - SELECT id, name, description FROM "test_data"
117
- `);
118
-
119
- await pool.query(`DROP TABLE IF EXISTS test_data`);
120
- await pool.query(
121
- `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), name text, description text)`
122
- );
123
-
124
- await context.replicateSnapshot();
125
- context.startStreaming();
126
-
127
- // Must be > 8kb after compression
128
- const largeDescription = crypto.randomBytes(20_000).toString('hex');
129
- const [{ test_id }] = pgwireRows(
130
- await pool.query({
131
- statement: `INSERT INTO test_data(name, description) VALUES('test1', $1) returning id as test_id`,
132
- params: [{ type: 'varchar', value: largeDescription }]
133
- })
134
- );
135
-
136
- await pool.query(`UPDATE test_data SET name = 'test2' WHERE id = '${test_id}'`);
137
-
138
- const data = await context.getBucketData('global[]');
139
- expect(data.slice(0, 1)).toMatchObject([
140
- putOp('test_data', { id: test_id, name: 'test1', description: largeDescription })
141
- ]);
142
- expect(data.slice(1)).toMatchObject([
143
- putOp('test_data', { id: test_id, name: 'test2', description: largeDescription })
144
- ]);
145
- })
146
- );
147
-
148
- test(
149
- 'replicating TRUNCATE',
150
- walStreamTest(factory, async (context) => {
151
- const { pool } = context;
152
- const syncRuleContent = `
153
- bucket_definitions:
154
- global:
155
- data:
156
- - SELECT id, description FROM "test_data"
157
- by_test_data:
158
- parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id
159
- data: []
160
- `;
161
- await context.updateSyncRules(syncRuleContent);
162
- await pool.query(`DROP TABLE IF EXISTS test_data`);
163
- await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`);
164
-
165
- await context.replicateSnapshot();
166
- context.startStreaming();
167
-
168
- const [{ test_id }] = pgwireRows(
169
- await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`)
170
- );
171
- await pool.query(`TRUNCATE test_data`);
172
-
173
- const data = await context.getBucketData('global[]');
174
-
175
- expect(data).toMatchObject([
176
- putOp('test_data', { id: test_id, description: 'test1' }),
177
- removeOp('test_data', test_id)
178
- ]);
179
- })
180
- );
181
-
182
- test(
183
- 'replicating changing primary key',
184
- walStreamTest(factory, async (context) => {
185
- const { pool } = context;
186
- await context.updateSyncRules(BASIC_SYNC_RULES);
187
- await pool.query(`DROP TABLE IF EXISTS test_data`);
188
- await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`);
189
-
190
- await context.replicateSnapshot();
191
- context.startStreaming();
192
-
193
- const [{ test_id }] = pgwireRows(
194
- await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`)
195
- );
196
-
197
- const [{ test_id: test_id2 }] = pgwireRows(
198
- await pool.query(
199
- `UPDATE test_data SET id = uuid_generate_v4(), description = 'test2a' WHERE id = '${test_id}' returning id as test_id`
200
- )
201
- );
202
-
203
- // This update may fail replicating with:
204
- // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"}
205
- await pool.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${test_id2}'`);
206
-
207
- // Re-use old id again
208
- await pool.query(`INSERT INTO test_data(id, description) VALUES('${test_id}', 'test1b')`);
209
- await pool.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${test_id}'`);
210
-
211
- const data = await context.getBucketData('global[]');
212
- expect(data).toMatchObject([
213
- // Initial insert
214
- putOp('test_data', { id: test_id, description: 'test1' }),
215
- // Update id, then description
216
- removeOp('test_data', test_id),
217
- putOp('test_data', { id: test_id2, description: 'test2a' }),
218
- putOp('test_data', { id: test_id2, description: 'test2b' }),
219
- // Re-use old id
220
- putOp('test_data', { id: test_id, description: 'test1b' }),
221
- putOp('test_data', { id: test_id, description: 'test1c' })
222
- ]);
223
- })
224
- );
225
-
226
- test(
227
- 'initial sync',
228
- walStreamTest(factory, async (context) => {
229
- const { pool } = context;
230
- await context.updateSyncRules(BASIC_SYNC_RULES);
231
-
232
- await pool.query(`DROP TABLE IF EXISTS test_data`);
233
- await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`);
234
-
235
- const [{ test_id }] = pgwireRows(
236
- await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`)
237
- );
238
-
239
- await context.replicateSnapshot();
240
- context.startStreaming();
241
-
242
- const data = await context.getBucketData('global[]');
243
- expect(data).toMatchObject([putOp('test_data', { id: test_id, description: 'test1' })]);
244
- })
245
- );
246
-
247
- test(
248
- 'record too large',
249
- walStreamTest(factory, async (context) => {
250
- await context.updateSyncRules(`bucket_definitions:
251
- global:
252
- data:
253
- - SELECT id, description, other FROM "test_data"`);
254
- const { pool } = context;
255
-
256
- await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`);
257
-
258
- await context.replicateSnapshot();
259
-
260
- // 4MB
261
- const largeDescription = crypto.randomBytes(2_000_000).toString('hex');
262
- // 18MB
263
- const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex');
264
-
265
- await pool.query({
266
- statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`,
267
- params: [{ type: 'varchar', value: tooLargeDescription }]
268
- });
269
- await pool.query({
270
- statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`,
271
- params: [{ type: 'varchar', value: largeDescription }]
272
- });
273
-
274
- context.startStreaming();
275
-
276
- const data = await context.getBucketData('global[]');
277
- expect(data.length).toEqual(1);
278
- const row = JSON.parse(data[0].data as string);
279
- delete row.description;
280
- expect(row).toEqual({ id: 't1', other: 'foo' });
281
- delete data[0].data;
282
- expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' });
283
- })
284
- );
285
-
286
- test(
287
- 'table not in sync rules',
288
- walStreamTest(factory, async (context) => {
289
- const { pool } = context;
290
- await context.updateSyncRules(BASIC_SYNC_RULES);
291
-
292
- await pool.query(`CREATE TABLE test_donotsync(id uuid primary key default uuid_generate_v4(), description text)`);
293
-
294
- await context.replicateSnapshot();
295
-
296
- const startRowCount =
297
- (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
298
- const startTxCount =
299
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
300
-
301
- context.startStreaming();
302
-
303
- const [{ test_id }] = pgwireRows(
304
- await pool.query(`INSERT INTO test_donotsync(description) VALUES('test1') returning id as test_id`)
305
- );
306
-
307
- const data = await context.getBucketData('global[]');
308
-
309
- expect(data).toMatchObject([]);
310
- const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
311
- const endTxCount =
312
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
313
-
314
- // There was a transaction, but we should not replicate any actual data
315
- expect(endRowCount - startRowCount).toEqual(0);
316
- expect(endTxCount - startTxCount).toEqual(1);
317
- })
318
- );
319
- }
@@ -1,156 +0,0 @@
1
- import * as pgwire from '@powersync/service-jpgwire';
2
- import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js';
3
- import { BucketStorageFactory, SyncRulesBucketStorage } from '../../src/storage/BucketStorage.js';
4
- import { OplogEntry } from '../../src/util/protocol-types.js';
5
- import { getClientCheckpoint } from '../../src/util/utils.js';
6
- import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js';
7
- import { PgManager } from '../../src/util/PgManager.js';
8
- import { JSONBig } from '@powersync/service-jsonbig';
9
-
10
- /**
11
- * Tests operating on the wal stream need to configure the stream and manage asynchronous
12
- * replication, which gets a little tricky.
13
- *
14
- * This wraps a test in a function that configures all the context, and tears it down afterwards.
15
- */
16
- export function walStreamTest(
17
- factory: () => Promise<BucketStorageFactory>,
18
- test: (context: WalStreamTestContext) => Promise<void>
19
- ): () => Promise<void> {
20
- return async () => {
21
- const f = await factory();
22
- const connections = new PgManager(TEST_CONNECTION_OPTIONS, {});
23
-
24
- await clearTestDb(connections.pool);
25
- const context = new WalStreamTestContext(f, connections);
26
- try {
27
- await test(context);
28
- } finally {
29
- await context.dispose();
30
- }
31
- };
32
- }
33
-
34
- export class WalStreamTestContext {
35
- private _walStream?: WalStream;
36
- private abortController = new AbortController();
37
- private streamPromise?: Promise<void>;
38
- public storage?: SyncRulesBucketStorage;
39
- private replicationConnection?: pgwire.PgConnection;
40
-
41
- constructor(public factory: BucketStorageFactory, public connections: PgManager) {}
42
-
43
- async dispose() {
44
- this.abortController.abort();
45
- await this.streamPromise;
46
- this.connections.destroy();
47
- }
48
-
49
- get pool() {
50
- return this.connections.pool;
51
- }
52
-
53
- async updateSyncRules(content: string) {
54
- const syncRules = await this.factory.updateSyncRules({ content: content });
55
- this.storage = this.factory.getInstance(syncRules.parsed());
56
- return this.storage!;
57
- }
58
-
59
- get walStream() {
60
- if (this.storage == null) {
61
- throw new Error('updateSyncRules() first');
62
- }
63
- if (this._walStream) {
64
- return this._walStream;
65
- }
66
- const options: WalStreamOptions = {
67
- storage: this.storage,
68
- factory: this.factory,
69
- connections: this.connections,
70
- abort_signal: this.abortController.signal
71
- };
72
- this._walStream = new WalStream(options);
73
- return this._walStream!;
74
- }
75
-
76
- async replicateSnapshot() {
77
- this.replicationConnection = await this.connections.replicationConnection();
78
- await this.walStream.initReplication(this.replicationConnection);
79
- await this.storage!.autoActivate();
80
- }
81
-
82
- startStreaming() {
83
- if (this.replicationConnection == null) {
84
- throw new Error('Call replicateSnapshot() before startStreaming()');
85
- }
86
- this.streamPromise = this.walStream.streamChanges(this.replicationConnection!);
87
- }
88
-
89
- async getCheckpoint(options?: { timeout?: number }) {
90
- let checkpoint = await Promise.race([
91
- getClientCheckpoint(this.connections.pool, this.factory, { timeout: options?.timeout ?? 15_000 }),
92
- this.streamPromise
93
- ]);
94
- if (typeof checkpoint == undefined) {
95
- // This indicates an issue with the test setup - streamingPromise completed instead
96
- // of getClientCheckpoint()
97
- throw new Error('Test failure - streamingPromise completed');
98
- }
99
- return checkpoint as string;
100
- }
101
-
102
- async getBucketsDataBatch(buckets: Record<string, string>, options?: { timeout?: number }) {
103
- let checkpoint = await this.getCheckpoint(options);
104
- const map = new Map<string, string>(Object.entries(buckets));
105
- return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map));
106
- }
107
-
108
- async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) {
109
- start ??= '0';
110
- let checkpoint = await this.getCheckpoint(options);
111
- const map = new Map<string, string>([[bucket, start]]);
112
- const batch = await this.storage!.getBucketDataBatch(checkpoint, map);
113
- const batches = await fromAsync(batch);
114
- return batches[0]?.batch.data ?? [];
115
- }
116
- }
117
-
118
- export function putOp(table: string, data: Record<string, any>): Partial<OplogEntry> {
119
- return {
120
- op: 'PUT',
121
- object_type: table,
122
- object_id: data.id,
123
- data: JSONBig.stringify(data)
124
- };
125
- }
126
-
127
- export function removeOp(table: string, id: string): Partial<OplogEntry> {
128
- return {
129
- op: 'REMOVE',
130
- object_type: table,
131
- object_id: id
132
- };
133
- }
134
-
135
- export function compareIds(a: OplogEntry, b: OplogEntry) {
136
- return a.object_id!.localeCompare(b.object_id!);
137
- }
138
-
139
- export async function fromAsync<T>(source: Iterable<T> | AsyncIterable<T>): Promise<T[]> {
140
- const items: T[] = [];
141
- for await (const item of source) {
142
- items.push(item);
143
- }
144
- return items;
145
- }
146
-
147
- export async function oneFromAsync<T>(source: Iterable<T> | AsyncIterable<T>): Promise<T> {
148
- const items: T[] = [];
149
- for await (const item of source) {
150
- items.push(item);
151
- }
152
- if (items.length != 1) {
153
- throw new Error(`One item expected, got: ${items.length}`);
154
- }
155
- return items[0];
156
- }