@powersync/service-core 0.2.1 → 0.3.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 (255) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/api/diagnostics.js +2 -2
  3. package/dist/api/diagnostics.js.map +1 -1
  4. package/dist/api/schema.js.map +1 -1
  5. package/dist/auth/CachedKeyCollector.js.map +1 -1
  6. package/dist/auth/KeySpec.js.map +1 -1
  7. package/dist/auth/KeyStore.js +2 -2
  8. package/dist/auth/KeyStore.js.map +1 -1
  9. package/dist/auth/LeakyBucket.js.map +1 -1
  10. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  11. package/dist/auth/SupabaseKeyCollector.js.map +1 -1
  12. package/dist/db/mongo.js.map +1 -1
  13. package/dist/entry/cli-entry.js +2 -2
  14. package/dist/entry/cli-entry.js.map +1 -1
  15. package/dist/entry/commands/config-command.js.map +1 -1
  16. package/dist/entry/commands/migrate-action.js.map +1 -1
  17. package/dist/entry/commands/start-action.js.map +1 -1
  18. package/dist/entry/commands/teardown-action.js.map +1 -1
  19. package/dist/index.d.ts +3 -2
  20. package/dist/index.js +4 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/locks/LockManager.d.ts +10 -0
  23. package/dist/locks/LockManager.js +7 -0
  24. package/dist/locks/LockManager.js.map +1 -0
  25. package/dist/locks/MongoLocks.d.ts +36 -0
  26. package/dist/locks/MongoLocks.js +81 -0
  27. package/dist/locks/MongoLocks.js.map +1 -0
  28. package/dist/locks/locks-index.d.ts +2 -0
  29. package/dist/locks/locks-index.js +3 -0
  30. package/dist/locks/locks-index.js.map +1 -0
  31. package/dist/metrics/Metrics.js +6 -6
  32. package/dist/metrics/Metrics.js.map +1 -1
  33. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -1
  34. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  35. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -1
  36. package/dist/migrations/definitions.d.ts +18 -0
  37. package/dist/migrations/definitions.js +6 -0
  38. package/dist/migrations/definitions.js.map +1 -0
  39. package/dist/migrations/executor.d.ts +16 -0
  40. package/dist/migrations/executor.js +64 -0
  41. package/dist/migrations/executor.js.map +1 -0
  42. package/dist/migrations/migrations-index.d.ts +3 -0
  43. package/dist/migrations/migrations-index.js +4 -0
  44. package/dist/migrations/migrations-index.js.map +1 -0
  45. package/dist/migrations/migrations.d.ts +1 -1
  46. package/dist/migrations/migrations.js +4 -8
  47. package/dist/migrations/migrations.js.map +1 -1
  48. package/dist/migrations/store/migration-store.d.ts +11 -0
  49. package/dist/migrations/store/migration-store.js +46 -0
  50. package/dist/migrations/store/migration-store.js.map +1 -0
  51. package/dist/replication/ErrorRateLimiter.js.map +1 -1
  52. package/dist/replication/PgRelation.js.map +1 -1
  53. package/dist/replication/WalConnection.js.map +1 -1
  54. package/dist/replication/WalStream.d.ts +0 -1
  55. package/dist/replication/WalStream.js +21 -25
  56. package/dist/replication/WalStream.js.map +1 -1
  57. package/dist/replication/WalStreamManager.js +12 -13
  58. package/dist/replication/WalStreamManager.js.map +1 -1
  59. package/dist/replication/WalStreamRunner.js +8 -8
  60. package/dist/replication/WalStreamRunner.js.map +1 -1
  61. package/dist/replication/util.js.map +1 -1
  62. package/dist/routes/auth.d.ts +8 -10
  63. package/dist/routes/auth.js.map +1 -1
  64. package/dist/routes/endpoints/admin.d.ts +1011 -0
  65. package/dist/routes/{admin.js → endpoints/admin.js} +33 -18
  66. package/dist/routes/endpoints/admin.js.map +1 -0
  67. package/dist/routes/endpoints/checkpointing.d.ts +76 -0
  68. package/dist/routes/endpoints/checkpointing.js +36 -0
  69. package/dist/routes/endpoints/checkpointing.js.map +1 -0
  70. package/dist/routes/endpoints/dev.d.ts +312 -0
  71. package/dist/routes/{dev.js → endpoints/dev.js} +25 -16
  72. package/dist/routes/endpoints/dev.js.map +1 -0
  73. package/dist/routes/endpoints/route-endpoints-index.d.ts +6 -0
  74. package/dist/routes/endpoints/route-endpoints-index.js +7 -0
  75. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -0
  76. package/dist/routes/endpoints/socket-route.d.ts +2 -0
  77. package/dist/routes/{socket-route.js → endpoints/socket-route.js} +10 -10
  78. package/dist/routes/endpoints/socket-route.js.map +1 -0
  79. package/dist/routes/endpoints/sync-rules.d.ts +174 -0
  80. package/dist/routes/{sync-rules.js → endpoints/sync-rules.js} +44 -24
  81. package/dist/routes/endpoints/sync-rules.js.map +1 -0
  82. package/dist/routes/endpoints/sync-stream.d.ts +132 -0
  83. package/dist/routes/{sync-stream.js → endpoints/sync-stream.js} +26 -17
  84. package/dist/routes/endpoints/sync-stream.js.map +1 -0
  85. package/dist/routes/hooks.d.ts +10 -0
  86. package/dist/routes/hooks.js +31 -0
  87. package/dist/routes/hooks.js.map +1 -0
  88. package/dist/routes/route-register.d.ts +10 -0
  89. package/dist/routes/route-register.js +87 -0
  90. package/dist/routes/route-register.js.map +1 -0
  91. package/dist/routes/router.d.ts +16 -4
  92. package/dist/routes/router.js +6 -1
  93. package/dist/routes/router.js.map +1 -1
  94. package/dist/routes/routes-index.d.ts +5 -3
  95. package/dist/routes/routes-index.js +5 -3
  96. package/dist/routes/routes-index.js.map +1 -1
  97. package/dist/runner/teardown.js +27 -12
  98. package/dist/runner/teardown.js.map +1 -1
  99. package/dist/storage/BucketStorage.d.ts +3 -0
  100. package/dist/storage/BucketStorage.js.map +1 -1
  101. package/dist/storage/ChecksumCache.js.map +1 -1
  102. package/dist/storage/MongoBucketStorage.js +5 -5
  103. package/dist/storage/MongoBucketStorage.js.map +1 -1
  104. package/dist/storage/SourceTable.js.map +1 -1
  105. package/dist/storage/mongo/MongoBucketBatch.js +23 -18
  106. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
  107. package/dist/storage/mongo/MongoIdSequence.js.map +1 -1
  108. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
  109. package/dist/storage/mongo/MongoSyncRulesLock.js +3 -3
  110. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -1
  111. package/dist/storage/mongo/OperationBatch.js.map +1 -1
  112. package/dist/storage/mongo/PersistedBatch.js +2 -2
  113. package/dist/storage/mongo/PersistedBatch.js.map +1 -1
  114. package/dist/storage/mongo/db.d.ts +2 -2
  115. package/dist/storage/mongo/db.js.map +1 -1
  116. package/dist/storage/mongo/util.js.map +1 -1
  117. package/dist/sync/BroadcastIterable.js.map +1 -1
  118. package/dist/sync/LastValueSink.js.map +1 -1
  119. package/dist/sync/merge.js.map +1 -1
  120. package/dist/sync/safeRace.js.map +1 -1
  121. package/dist/sync/sync.js +4 -4
  122. package/dist/sync/sync.js.map +1 -1
  123. package/dist/sync/util.js.map +1 -1
  124. package/dist/system/CorePowerSyncSystem.d.ts +12 -7
  125. package/dist/system/CorePowerSyncSystem.js +26 -2
  126. package/dist/system/CorePowerSyncSystem.js.map +1 -1
  127. package/dist/system/system-index.d.ts +1 -0
  128. package/dist/system/system-index.js +2 -0
  129. package/dist/system/system-index.js.map +1 -0
  130. package/dist/util/Mutex.js.map +1 -1
  131. package/dist/util/PgManager.js.map +1 -1
  132. package/dist/util/alerting.d.ts +0 -2
  133. package/dist/util/alerting.js +0 -6
  134. package/dist/util/alerting.js.map +1 -1
  135. package/dist/util/config/collectors/config-collector.js +3 -3
  136. package/dist/util/config/collectors/config-collector.js.map +1 -1
  137. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
  138. package/dist/util/config/collectors/impl/filesystem-config-collector.js +7 -5
  139. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
  140. package/dist/util/config/compound-config-collector.js +4 -4
  141. package/dist/util/config/compound-config-collector.js.map +1 -1
  142. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
  143. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  144. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
  145. package/dist/util/config.js.map +1 -1
  146. package/dist/util/env.d.ts +1 -2
  147. package/dist/util/env.js +3 -2
  148. package/dist/util/env.js.map +1 -1
  149. package/dist/util/memory-tracking.js +2 -2
  150. package/dist/util/memory-tracking.js.map +1 -1
  151. package/dist/util/migration_lib.js.map +1 -1
  152. package/dist/util/pgwire_utils.js +2 -2
  153. package/dist/util/pgwire_utils.js.map +1 -1
  154. package/dist/util/populate_test_data.js.map +1 -1
  155. package/dist/util/secs.js.map +1 -1
  156. package/dist/util/utils.js +4 -4
  157. package/dist/util/utils.js.map +1 -1
  158. package/package.json +13 -10
  159. package/src/api/diagnostics.ts +5 -5
  160. package/src/api/schema.ts +1 -1
  161. package/src/auth/KeyStore.ts +2 -2
  162. package/src/entry/cli-entry.ts +3 -4
  163. package/src/entry/commands/config-command.ts +1 -1
  164. package/src/entry/commands/migrate-action.ts +2 -2
  165. package/src/entry/commands/start-action.ts +1 -1
  166. package/src/entry/commands/teardown-action.ts +1 -1
  167. package/src/index.ts +5 -2
  168. package/src/locks/LockManager.ts +16 -0
  169. package/src/locks/MongoLocks.ts +142 -0
  170. package/src/locks/locks-index.ts +2 -0
  171. package/src/metrics/Metrics.ts +8 -8
  172. package/src/migrations/db/migrations/1684951997326-init.ts +3 -3
  173. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +3 -3
  174. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +2 -2
  175. package/src/migrations/definitions.ts +21 -0
  176. package/src/migrations/executor.ts +87 -0
  177. package/src/migrations/migrations-index.ts +3 -0
  178. package/src/migrations/migrations.ts +7 -11
  179. package/src/migrations/store/migration-store.ts +63 -0
  180. package/src/replication/WalConnection.ts +2 -2
  181. package/src/replication/WalStream.ts +24 -29
  182. package/src/replication/WalStreamManager.ts +14 -15
  183. package/src/replication/WalStreamRunner.ts +10 -10
  184. package/src/replication/util.ts +1 -1
  185. package/src/routes/auth.ts +22 -16
  186. package/src/routes/endpoints/admin.ts +237 -0
  187. package/src/routes/endpoints/checkpointing.ts +41 -0
  188. package/src/routes/endpoints/dev.ts +199 -0
  189. package/src/routes/endpoints/route-endpoints-index.ts +6 -0
  190. package/src/routes/{socket-route.ts → endpoints/socket-route.ts} +11 -11
  191. package/src/routes/endpoints/sync-rules.ts +227 -0
  192. package/src/routes/endpoints/sync-stream.ts +101 -0
  193. package/src/routes/hooks.ts +45 -0
  194. package/src/routes/route-register.ts +104 -0
  195. package/src/routes/router.ts +34 -6
  196. package/src/routes/routes-index.ts +5 -4
  197. package/src/runner/teardown.ts +30 -13
  198. package/src/storage/BucketStorage.ts +7 -2
  199. package/src/storage/ChecksumCache.ts +2 -2
  200. package/src/storage/MongoBucketStorage.ts +8 -8
  201. package/src/storage/SourceTable.ts +2 -2
  202. package/src/storage/mongo/MongoBucketBatch.ts +29 -22
  203. package/src/storage/mongo/MongoSyncBucketStorage.ts +3 -3
  204. package/src/storage/mongo/MongoSyncRulesLock.ts +3 -3
  205. package/src/storage/mongo/OperationBatch.ts +1 -1
  206. package/src/storage/mongo/PersistedBatch.ts +3 -3
  207. package/src/storage/mongo/db.ts +3 -4
  208. package/src/sync/sync.ts +8 -8
  209. package/src/sync/util.ts +2 -2
  210. package/src/system/CorePowerSyncSystem.ts +31 -10
  211. package/src/system/system-index.ts +1 -0
  212. package/src/util/alerting.ts +0 -8
  213. package/src/util/config/collectors/config-collector.ts +5 -3
  214. package/src/util/config/collectors/impl/filesystem-config-collector.ts +8 -6
  215. package/src/util/config/compound-config-collector.ts +4 -4
  216. package/src/util/env.ts +4 -2
  217. package/src/util/memory-tracking.ts +2 -2
  218. package/src/util/pgwire_utils.ts +3 -3
  219. package/src/util/utils.ts +5 -5
  220. package/test/src/auth.test.ts +4 -2
  221. package/test/src/data_storage.test.ts +177 -0
  222. package/test/src/env.ts +6 -6
  223. package/test/src/pg_test.test.ts +18 -0
  224. package/test/src/setup.ts +7 -0
  225. package/test/src/slow_tests.test.ts +45 -6
  226. package/test/tsconfig.json +1 -1
  227. package/tsconfig.json +5 -6
  228. package/tsconfig.tsbuildinfo +1 -1
  229. package/vitest.config.ts +1 -3
  230. package/dist/migrations/db/store.d.ts +0 -3
  231. package/dist/migrations/db/store.js +0 -10
  232. package/dist/migrations/db/store.js.map +0 -1
  233. package/dist/routes/admin.d.ts +0 -7
  234. package/dist/routes/admin.js.map +0 -1
  235. package/dist/routes/checkpointing.d.ts +0 -3
  236. package/dist/routes/checkpointing.js +0 -30
  237. package/dist/routes/checkpointing.js.map +0 -1
  238. package/dist/routes/dev.d.ts +0 -6
  239. package/dist/routes/dev.js.map +0 -1
  240. package/dist/routes/route-generators.d.ts +0 -15
  241. package/dist/routes/route-generators.js +0 -32
  242. package/dist/routes/route-generators.js.map +0 -1
  243. package/dist/routes/socket-route.d.ts +0 -2
  244. package/dist/routes/socket-route.js.map +0 -1
  245. package/dist/routes/sync-rules.d.ts +0 -6
  246. package/dist/routes/sync-rules.js.map +0 -1
  247. package/dist/routes/sync-stream.d.ts +0 -5
  248. package/dist/routes/sync-stream.js.map +0 -1
  249. package/src/migrations/db/store.ts +0 -11
  250. package/src/routes/admin.ts +0 -229
  251. package/src/routes/checkpointing.ts +0 -38
  252. package/src/routes/dev.ts +0 -194
  253. package/src/routes/route-generators.ts +0 -39
  254. package/src/routes/sync-rules.ts +0 -210
  255. package/src/routes/sync-stream.ts +0 -95
package/src/index.ts CHANGED
@@ -12,11 +12,14 @@ export * as db from './db/db-index.js';
12
12
  export * from './entry/entry-index.js';
13
13
  export * as entry from './entry/entry-index.js';
14
14
 
15
+ // Re-export framework for easy use of Container API
16
+ export * as framework from '@powersync/lib-services-framework';
17
+
15
18
  export * from './metrics/Metrics.js';
16
19
  export * as metrics from './metrics/Metrics.js';
17
20
 
18
21
  export * from './migrations/migrations.js';
19
- export * as migrations from './migrations/migrations.js';
22
+ export * as migrations from './migrations/migrations-index.js';
20
23
 
21
24
  export * from './replication/replication-index.js';
22
25
  export * as replication from './replication/replication-index.js';
@@ -31,7 +34,7 @@ export * from './sync/sync-index.js';
31
34
  export * as sync from './sync/sync-index.js';
32
35
 
33
36
  export * from './system/CorePowerSyncSystem.js';
34
- export * as system from './system/CorePowerSyncSystem.js';
37
+ export * as system from './system/system-index.js';
35
38
 
36
39
  export * from './util/util-index.js';
37
40
  export * as utils from './util/util-index.js';
@@ -0,0 +1,16 @@
1
+ import * as bson from 'bson';
2
+
3
+ export class LockActiveError extends Error {
4
+ constructor() {
5
+ super('Lock is already active');
6
+ this.name = this.constructor.name;
7
+ }
8
+ }
9
+
10
+ export type LockManager = {
11
+ acquire: () => Promise<bson.ObjectId | null>;
12
+ refresh: (lock_id: bson.ObjectId) => Promise<void>;
13
+ release: (lock_id: bson.ObjectId) => Promise<void>;
14
+
15
+ lock: (handler: (refresh: () => Promise<void>) => Promise<void>) => Promise<void>;
16
+ };
@@ -0,0 +1,142 @@
1
+ import * as mongo from 'mongodb';
2
+ import * as bson from 'bson';
3
+ import { LockActiveError, LockManager } from './LockManager.js';
4
+
5
+ /**
6
+ * Lock Document Schema
7
+ */
8
+ export type Lock = {
9
+ name: string;
10
+ active_lock?: {
11
+ lock_id: bson.ObjectId;
12
+ ts: Date;
13
+ };
14
+ };
15
+
16
+ export type Collection = mongo.Collection<Lock>;
17
+
18
+ export type AcquireLockParams = {
19
+ /**
20
+ * Name of the process/user trying to acquire the lock.
21
+ */
22
+ name: string;
23
+ /**
24
+ * The TTL of the lock (ms). Default: 60000 ms (1 min)
25
+ */
26
+ timeout?: number;
27
+ };
28
+
29
+ const DEFAULT_LOCK_TIMEOUT = 60 * 1000; // 1 minute
30
+
31
+ const acquireLock = async (collection: Collection, params: AcquireLockParams) => {
32
+ const now = new Date();
33
+ const lock_timeout = params.timeout ?? DEFAULT_LOCK_TIMEOUT;
34
+ const lock_id = new bson.ObjectId();
35
+
36
+ await collection.updateOne(
37
+ {
38
+ name: params.name
39
+ },
40
+ {
41
+ $setOnInsert: {
42
+ name: params.name
43
+ }
44
+ },
45
+ {
46
+ upsert: true
47
+ }
48
+ );
49
+
50
+ const expired_ts = now.getTime() - lock_timeout;
51
+
52
+ const res = await collection.updateOne(
53
+ {
54
+ $and: [
55
+ { name: params.name },
56
+ {
57
+ $or: [{ active_lock: { $exists: false } }, { 'active_lock.ts': { $lte: new Date(expired_ts) } }]
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ $set: {
63
+ active_lock: {
64
+ lock_id: lock_id,
65
+ ts: now
66
+ }
67
+ }
68
+ }
69
+ );
70
+
71
+ if (res.modifiedCount === 0) {
72
+ return null;
73
+ }
74
+
75
+ return lock_id;
76
+ };
77
+
78
+ const refreshLock = async (collection: Collection, lock_id: bson.ObjectId) => {
79
+ const res = await collection.updateOne(
80
+ {
81
+ 'active_lock.lock_id': lock_id
82
+ },
83
+ {
84
+ $set: {
85
+ 'active_lock.ts': new Date()
86
+ }
87
+ }
88
+ );
89
+
90
+ if (res.modifiedCount === 0) {
91
+ throw new Error('Lock not found, could not refresh');
92
+ }
93
+ };
94
+
95
+ export const releaseLock = async (collection: Collection, lock_id: bson.ObjectId) => {
96
+ const res = await collection.updateOne(
97
+ {
98
+ 'active_lock.lock_id': lock_id
99
+ },
100
+ {
101
+ $unset: {
102
+ active_lock: true
103
+ }
104
+ }
105
+ );
106
+
107
+ if (res.modifiedCount === 0) {
108
+ throw new Error('Lock not found, could not release');
109
+ }
110
+ };
111
+
112
+ export type CreateLockManagerParams = {
113
+ /**
114
+ * Name of the process/user trying to acquire the lock.
115
+ */
116
+ name: string;
117
+ /**
118
+ * The TTL for each lock (ms). Default: 60000 ms (1 min)
119
+ */
120
+ timeout?: number;
121
+ };
122
+
123
+ export const createMongoLockManager = (collection: Collection, params: CreateLockManagerParams): LockManager => {
124
+ return {
125
+ acquire: () => acquireLock(collection, params),
126
+ refresh: (lock_id: bson.ObjectId) => refreshLock(collection, lock_id),
127
+ release: (lock_id: bson.ObjectId) => releaseLock(collection, lock_id),
128
+
129
+ lock: async (handler) => {
130
+ const lock_id = await acquireLock(collection, params);
131
+ if (!lock_id) {
132
+ throw new LockActiveError();
133
+ }
134
+
135
+ try {
136
+ await handler(() => refreshLock(collection, lock_id));
137
+ } finally {
138
+ await releaseLock(collection, lock_id);
139
+ }
140
+ }
141
+ };
142
+ };
@@ -0,0 +1,2 @@
1
+ export * from './LockManager.js';
2
+ export * from './MongoLocks.js';
@@ -1,13 +1,13 @@
1
- import * as micro from '@journeyapps-platform/micro';
2
1
  import { Attributes, Counter, ObservableGauge, UpDownCounter, ValueType } from '@opentelemetry/api';
3
2
  import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
4
3
  import { MeterProvider, MetricReader, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
5
4
  import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
6
5
  import * as jpgwire from '@powersync/service-jpgwire';
7
- import * as util from '@/util/util-index.js';
8
- import * as storage from '@/storage/storage-index.js';
6
+ import * as util from '../util/util-index.js';
7
+ import * as storage from '../storage/storage-index.js';
9
8
  import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
10
9
  import { Resource } from '@opentelemetry/resources';
10
+ import { logger } from '@powersync/lib-services-framework';
11
11
 
12
12
  export interface MetricsOptions {
13
13
  disable_telemetry_sharing: boolean;
@@ -144,9 +144,9 @@ export class Metrics {
144
144
  if (Metrics.instance) {
145
145
  return;
146
146
  }
147
- micro.logger.info('Configuring telemetry.');
147
+ logger.info('Configuring telemetry.');
148
148
 
149
- micro.logger.info(
149
+ logger.info(
150
150
  `
151
151
  Attention:
152
152
  PowerSync collects completely anonymous telemetry regarding usage.
@@ -164,7 +164,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable
164
164
  configuredExporters.push(prometheusExporter);
165
165
 
166
166
  if (!options.disable_telemetry_sharing) {
167
- micro.logger.info('Sharing anonymous telemetry');
167
+ logger.info('Sharing anonymous telemetry');
168
168
  const periodicExporter = new PeriodicExportingMetricReader({
169
169
  exporter: new OTLPMetricExporter({
170
170
  url: options.internal_metrics_endpoint
@@ -189,7 +189,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable
189
189
 
190
190
  Metrics.instance = new Metrics(meterProvider, prometheusExporter);
191
191
 
192
- micro.logger.info('Telemetry configuration complete.');
192
+ logger.info('Telemetry configuration complete.');
193
193
  }
194
194
 
195
195
  public async shutdown(): Promise<void> {
@@ -212,7 +212,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable
212
212
  function getMetrics() {
213
213
  if (cachedRequest == null || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) {
214
214
  cachedRequest = system.storage.getStorageMetrics().catch((e) => {
215
- micro.logger.error(`Failed to get storage metrics`, e);
215
+ logger.error(`Failed to get storage metrics`, e);
216
216
  return null;
217
217
  });
218
218
  cacheTimestamp = Date.now();
@@ -1,6 +1,6 @@
1
- import * as mongo from '@/db/mongo.js';
2
- import * as storage from '@/storage/storage-index.js';
3
- import * as utils from '@/util/util-index.js';
1
+ import * as mongo from '../../../db/mongo.js';
2
+ import * as storage from '../../../storage/storage-index.js';
3
+ import * as utils from '../../../util/util-index.js';
4
4
 
5
5
  export const up = async (context?: utils.MigrationContext) => {
6
6
  const config = await utils.loadConfig(context?.runner_config);
@@ -1,6 +1,6 @@
1
- import * as mongo from '@/db/mongo.js';
2
- import * as storage from '@/storage/storage-index.js';
3
- import * as utils from '@/util/util-index.js';
1
+ import * as mongo from '../../../db/mongo.js';
2
+ import * as storage from '../../../storage/storage-index.js';
3
+ import * as utils from '../../../util/util-index.js';
4
4
 
5
5
  interface LegacySyncRulesDocument extends storage.SyncRuleDocument {
6
6
  /**
@@ -1,5 +1,5 @@
1
- import * as storage from '@/storage/storage-index.js';
2
- import * as utils from '@/util/util-index.js';
1
+ import * as storage from '../../../storage/storage-index.js';
2
+ import * as utils from '../../../util/util-index.js';
3
3
 
4
4
  export const up = async (context?: utils.MigrationContext) => {
5
5
  const config = await utils.loadConfig(context?.runner_config);
@@ -0,0 +1,21 @@
1
+ export type Migration = {
2
+ name: string;
3
+ up: () => Promise<void>;
4
+ down: () => Promise<void>;
5
+ };
6
+
7
+ export enum Direction {
8
+ Up = 'up',
9
+ Down = 'down'
10
+ }
11
+
12
+ export type ExecutedMigration = {
13
+ name: string;
14
+ direction: Direction;
15
+ timestamp: Date;
16
+ };
17
+
18
+ export type MigrationState = {
19
+ last_run: string;
20
+ log: ExecutedMigration[];
21
+ };
@@ -0,0 +1,87 @@
1
+ import { logger } from '@powersync/lib-services-framework';
2
+ import * as defs from './definitions.js';
3
+ import { MigrationStore } from './store/migration-store.js';
4
+
5
+ type ExecuteParams = {
6
+ migrations: defs.Migration[];
7
+ state?: defs.MigrationState;
8
+
9
+ direction: defs.Direction;
10
+ count?: number;
11
+ };
12
+
13
+ export async function* execute(params: ExecuteParams): AsyncGenerator<defs.ExecutedMigration> {
14
+ let migrations = [...params.migrations];
15
+ if (params.direction === defs.Direction.Down) {
16
+ migrations = migrations.reverse();
17
+ }
18
+
19
+ let index = 0;
20
+
21
+ if (params.state) {
22
+ // Find the index of the last run
23
+ index = migrations.findIndex((migration) => {
24
+ return migration.name === params.state!.last_run;
25
+ });
26
+
27
+ if (index === -1) {
28
+ throw new Error(`The last run migration ${params.state?.last_run} was not found in the given set of migrations`);
29
+ }
30
+
31
+ // If we are migrating down then we want to include the last run migration, otherwise we want to start at the next one
32
+ if (params.direction === defs.Direction.Up) {
33
+ index += 1;
34
+ }
35
+ }
36
+
37
+ migrations = migrations.slice(index);
38
+
39
+ let i = 0;
40
+ for (const migration of migrations) {
41
+ if (params.count && params.count === i) {
42
+ return;
43
+ }
44
+
45
+ logger.info(`Executing ${migration.name} (${params.direction})`);
46
+ try {
47
+ switch (params.direction) {
48
+ case defs.Direction.Up: {
49
+ await migration.up();
50
+ break;
51
+ }
52
+ case defs.Direction.Down: {
53
+ await migration.down();
54
+ break;
55
+ }
56
+ }
57
+ logger.debug(`Success`);
58
+ } catch (err) {
59
+ logger.error(`Failed`, err);
60
+ process.exit(1);
61
+ }
62
+
63
+ yield {
64
+ name: migration.name,
65
+ direction: params.direction,
66
+ timestamp: new Date()
67
+ };
68
+
69
+ i++;
70
+ }
71
+ }
72
+
73
+ type WriteLogsParams = {
74
+ store: MigrationStore;
75
+ state?: defs.MigrationState;
76
+ log_stream: Iterable<defs.ExecutedMigration> | AsyncIterable<defs.ExecutedMigration>;
77
+ };
78
+ export const writeLogsToStore = async (params: WriteLogsParams): Promise<void> => {
79
+ const log = [...(params.state?.log || [])];
80
+ for await (const migration of params.log_stream) {
81
+ log.push(migration);
82
+ await params.store.save({
83
+ last_run: migration.name,
84
+ log: log
85
+ });
86
+ }
87
+ };
@@ -0,0 +1,3 @@
1
+ export * from './definitions.js';
2
+ export * from './executor.js';
3
+ export * from './migrations.js';
@@ -1,12 +1,13 @@
1
- import { locks } from '@journeyapps-platform/micro';
2
1
  import * as fs from 'fs/promises';
3
2
  import * as path from 'path';
4
3
  import { fileURLToPath } from 'url';
5
4
 
6
- import { Direction, createMongoMigrationStore, execute, writeLogsToStore } from '@journeyapps-platform/micro-migrate';
7
-
8
- import * as db from '@/db/db-index.js';
9
- import * as util from '@/util/util-index.js';
5
+ import * as db from '../db/db-index.js';
6
+ import * as util from '../util/util-index.js';
7
+ import * as locks from '../locks/locks-index.js';
8
+ import { Direction } from './definitions.js';
9
+ import { createMongoMigrationStore } from './store/migration-store.js';
10
+ import { execute, writeLogsToStore } from './executor.js';
10
11
 
11
12
  const DEFAULT_MONGO_LOCK_COLLECTION = 'locks';
12
13
  const MONGO_LOCK_PROCESS = 'migrations';
@@ -94,12 +95,7 @@ export const migrate = async (options: MigrationOptions) => {
94
95
  const migrations = await loadMigrations(MIGRATIONS_DIR, runner_config);
95
96
 
96
97
  // Use the provided config to connect to Mongo
97
- const store = createMongoMigrationStore({
98
- uri: storage.uri,
99
- database: storage.database,
100
- username: storage.username,
101
- password: storage.password
102
- });
98
+ const store = createMongoMigrationStore(clientDB);
103
99
 
104
100
  const state = await store.load();
105
101
 
@@ -0,0 +1,63 @@
1
+ import { Db } from 'mongodb';
2
+ import * as path from 'path';
3
+ import * as defs from '../definitions.js';
4
+
5
+ export type MigrationStore = {
6
+ load: () => Promise<defs.MigrationState | undefined>;
7
+ save: (state: defs.MigrationState) => Promise<void>;
8
+ };
9
+
10
+ /**
11
+ * A custom store for node-migrate which is used to save and load migrations that have
12
+ * been operated on to mongo.
13
+ */
14
+ export const createMongoMigrationStore = (db: Db): MigrationStore => {
15
+ const collection = db.collection<defs.MigrationState>('migrations');
16
+
17
+ return {
18
+ load: async () => {
19
+ const state_entry = await collection.findOne();
20
+ if (!state_entry) {
21
+ return;
22
+ }
23
+
24
+ const { _id, ...state } = state_entry;
25
+
26
+ /**
27
+ * This is for backwards compatibility. A previous version of the migration tool used to save
28
+ * state as `lastRun`.
29
+ */
30
+ let last_run = state.last_run;
31
+ if ('lastRun' in state) {
32
+ last_run = (state as any).lastRun;
33
+ }
34
+
35
+ /**
36
+ * This is for backwards compatibility. A previous version of the migration tool used to include the
37
+ * file extension in migration names. This strips that extension off if it exists
38
+ */
39
+ const extension = path.extname(last_run);
40
+ if (extension) {
41
+ last_run = last_run.replace(extension, '');
42
+ }
43
+
44
+ return {
45
+ last_run,
46
+ log: state.log || []
47
+ };
48
+ },
49
+
50
+ save: async (state: defs.MigrationState) => {
51
+ await collection.replaceOne(
52
+ {},
53
+ {
54
+ last_run: state.last_run,
55
+ log: state.log
56
+ },
57
+ {
58
+ upsert: true
59
+ }
60
+ );
61
+ }
62
+ };
63
+ };
@@ -3,8 +3,8 @@ import { pgwireRows } from '@powersync/service-jpgwire';
3
3
  import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules';
4
4
  import { ReplicationError, TableInfo } from '@powersync/service-types';
5
5
 
6
- import * as storage from '@/storage/storage-index.js';
7
- import * as util from '@/util/util-index.js';
6
+ import * as storage from '../storage/storage-index.js';
7
+ import * as util from '../util/util-index.js';
8
8
 
9
9
  import { ReplicaIdentityResult, getReplicationIdentityColumns } from './util.js';
10
10
  /**
@@ -1,22 +1,19 @@
1
- import * as fs from 'fs/promises';
2
1
  import * as pgwire from '@powersync/service-jpgwire';
3
- import * as micro from '@journeyapps-platform/micro';
4
- import { logger } from '@journeyapps-platform/micro';
2
+ import { container, errors, logger } from '@powersync/lib-services-framework';
5
3
  import { SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules';
6
4
 
7
- import * as storage from '@/storage/storage-index.js';
8
- import * as util from '@/util/util-index.js';
5
+ import * as storage from '../storage/storage-index.js';
6
+ import * as util from '../util/util-index.js';
9
7
 
10
8
  import { getPgOutputRelation, getRelId, PgRelation } from './PgRelation.js';
11
9
  import { getReplicationIdentityColumns } from './util.js';
12
10
  import { WalConnection } from './WalConnection.js';
13
- import { Metrics } from '@/metrics/Metrics.js';
11
+ import { Metrics } from '../metrics/Metrics.js';
14
12
 
15
13
  export const ZERO_LSN = '00000000/00000000';
16
14
 
17
15
  export interface WalStreamOptions {
18
16
  connections: util.PgManager;
19
-
20
17
  factory: storage.BucketStorageFactory;
21
18
  storage: storage.SyncRulesBucketStorage;
22
19
  abort_signal: AbortSignal;
@@ -160,7 +157,7 @@ export class WalStream {
160
157
  ]
161
158
  });
162
159
  if (rs.rows.length == 0) {
163
- micro.logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`);
160
+ logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`);
164
161
  continue;
165
162
  }
166
163
 
@@ -190,7 +187,7 @@ export class WalStream {
190
187
 
191
188
  const status = await this.storage.getStatus();
192
189
  if (status.snapshot_done && status.checkpoint_lsn) {
193
- micro.logger.info(`${slotName} Initial replication already done`);
190
+ logger.info(`${slotName} Initial replication already done`);
194
191
 
195
192
  let last_error = null;
196
193
 
@@ -199,8 +196,8 @@ export class WalStream {
199
196
  await touch();
200
197
 
201
198
  if (i == 0) {
202
- util.captureException(last_error, {
203
- level: micro.errors.ErrorSeverity.ERROR,
199
+ container.reporter.captureException(last_error, {
200
+ level: errors.ErrorSeverity.ERROR,
204
201
  metadata: {
205
202
  replication_slot: slotName
206
203
  }
@@ -222,11 +219,11 @@ export class WalStream {
222
219
  ]
223
220
  });
224
221
  // Success
225
- micro.logger.info(`Slot ${slotName} appears healthy`);
222
+ logger.info(`Slot ${slotName} appears healthy`);
226
223
  return { needsInitialSync: false };
227
224
  } catch (e) {
228
225
  last_error = e;
229
- micro.logger.warn(`${slotName} Replication slot error`, e);
226
+ logger.warn(`${slotName} Replication slot error`, e);
230
227
 
231
228
  if (this.stopped) {
232
229
  throw e;
@@ -240,8 +237,8 @@ export class WalStream {
240
237
  /replication slot.*does not exist/.test(e.message) ||
241
238
  /publication.*does not exist/.test(e.message)
242
239
  ) {
243
- util.captureException(e, {
244
- level: micro.errors.ErrorSeverity.WARNING,
240
+ container.reporter.captureException(e, {
241
+ level: errors.ErrorSeverity.WARNING,
245
242
  metadata: {
246
243
  try_index: i,
247
244
  replication_slot: slotName
@@ -253,7 +250,7 @@ export class WalStream {
253
250
  // Sample: publication "powersync" does not exist
254
251
  // Happens when publication deleted or never created.
255
252
  // Slot must be re-created in this case.
256
- micro.logger.info(`${slotName} does not exist anymore, will create new slot`);
253
+ logger.info(`${slotName} does not exist anymore, will create new slot`);
257
254
 
258
255
  throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`);
259
256
  }
@@ -316,7 +313,7 @@ WHERE oid = $1::regclass`,
316
313
  // with streaming replication.
317
314
  const lsn = pgwire.lsnMakeComparable(row[1]);
318
315
  const snapshot = row[2];
319
- micro.logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`);
316
+ logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`);
320
317
 
321
318
  // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn
322
319
  await db.query('BEGIN');
@@ -338,9 +335,9 @@ WHERE oid = $1::regclass`,
338
335
  // On Supabase, the default is 2 minutes.
339
336
  await db.query(`set local statement_timeout = 0`);
340
337
 
341
- micro.logger.info(`${slotName} Starting initial replication`);
338
+ logger.info(`${slotName} Starting initial replication`);
342
339
  await this.initialReplication(db, lsn);
343
- micro.logger.info(`${slotName} Initial replication done`);
340
+ logger.info(`${slotName} Initial replication done`);
344
341
  await db.query('COMMIT');
345
342
  } catch (e) {
346
343
  await db.query('ROLLBACK');
@@ -371,7 +368,7 @@ WHERE oid = $1::regclass`,
371
368
  }
372
369
 
373
370
  private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) {
374
- micro.logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`);
371
+ logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`);
375
372
  const estimatedCount = await this.estimatedCount(db, table);
376
373
  let at = 0;
377
374
  let lastLogIndex = 0;
@@ -397,7 +394,7 @@ WHERE oid = $1::regclass`,
397
394
  return q;
398
395
  });
399
396
  if (rows.length > 0 && at - lastLogIndex >= 5000) {
400
- micro.logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`);
397
+ logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`);
401
398
  lastLogIndex = at;
402
399
  }
403
400
  if (this.abort_signal.aborted) {
@@ -586,7 +583,7 @@ WHERE oid = $1::regclass`,
586
583
  await this.ack(msg.lsn!, replicationStream);
587
584
  } else {
588
585
  if (count % 100 == 0) {
589
- micro.logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`);
586
+ logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`);
590
587
  }
591
588
 
592
589
  count += 1;
@@ -619,11 +616,9 @@ WHERE oid = $1::regclass`,
619
616
  }
620
617
  }
621
618
 
622
- export async function touch() {
623
- // FIXME: The probe does not actually check the timestamp on this.
624
- // FIXME: We need a timeout of around 5+ minutes if we do start checking the timestamp,
625
- // or reduce PING_INTERVAL.
626
- await micro.signals.getSystemProbe().touch();
627
- // FIXME: The above probe touches the wrong file
628
- await fs.writeFile('.probes/poll', `${Date.now()}`);
619
+ async function touch() {
620
+ // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this.
621
+ // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp,
622
+ // or reduce PING_INTERVAL here.
623
+ return container.probes.touch();
629
624
  }