@powersync/service-core 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/entry/commands/compact-action.js +14 -14
- package/dist/entry/commands/compact-action.js.map +1 -1
- package/dist/entry/commands/migrate-action.js +15 -4
- package/dist/entry/commands/migrate-action.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations/PowerSyncMigrationManager.d.ts +17 -0
- package/dist/migrations/PowerSyncMigrationManager.js +21 -0
- package/dist/migrations/PowerSyncMigrationManager.js.map +1 -0
- package/dist/migrations/ensure-automatic-migrations.d.ts +4 -0
- package/dist/migrations/ensure-automatic-migrations.js +14 -0
- package/dist/migrations/ensure-automatic-migrations.js.map +1 -0
- package/dist/migrations/migrations-index.d.ts +2 -3
- package/dist/migrations/migrations-index.js +2 -3
- package/dist/migrations/migrations-index.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +12 -12
- package/dist/routes/endpoints/admin.d.ts +24 -24
- package/dist/storage/BucketStorage.d.ts +51 -3
- package/dist/storage/BucketStorage.js +26 -0
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/bson.d.ts +24 -0
- package/dist/storage/bson.js +73 -0
- package/dist/storage/bson.js.map +1 -0
- package/dist/storage/storage-index.d.ts +3 -14
- package/dist/storage/storage-index.js +3 -14
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/sync/sync.js +3 -1
- package/dist/sync/sync.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +3 -0
- package/dist/system/ServiceContext.js +11 -3
- package/dist/system/ServiceContext.js.map +1 -1
- package/dist/util/config/types.d.ts +2 -2
- package/dist/util/utils.d.ts +17 -1
- package/dist/util/utils.js +49 -1
- package/dist/util/utils.js.map +1 -1
- package/package.json +7 -8
- package/src/entry/commands/compact-action.ts +19 -14
- package/src/entry/commands/migrate-action.ts +17 -4
- package/src/index.ts +1 -4
- package/src/migrations/PowerSyncMigrationManager.ts +42 -0
- package/src/migrations/ensure-automatic-migrations.ts +15 -0
- package/src/migrations/migrations-index.ts +2 -3
- package/src/storage/BucketStorage.ts +59 -3
- package/src/storage/bson.ts +78 -0
- package/src/storage/storage-index.ts +3 -15
- package/src/sync/sync.ts +3 -1
- package/src/system/ServiceContext.ts +17 -4
- package/src/util/config/types.ts +2 -2
- package/src/util/utils.ts +47 -1
- package/test/src/env.ts +0 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/db/db-index.d.ts +0 -1
- package/dist/db/db-index.js +0 -2
- package/dist/db/db-index.js.map +0 -1
- package/dist/db/mongo.d.ts +0 -35
- package/dist/db/mongo.js +0 -73
- package/dist/db/mongo.js.map +0 -1
- package/dist/locks/LockManager.d.ts +0 -10
- package/dist/locks/LockManager.js +0 -7
- package/dist/locks/LockManager.js.map +0 -1
- package/dist/locks/MongoLocks.d.ts +0 -36
- package/dist/locks/MongoLocks.js +0 -81
- package/dist/locks/MongoLocks.js.map +0 -1
- package/dist/locks/locks-index.d.ts +0 -2
- package/dist/locks/locks-index.js +0 -3
- package/dist/locks/locks-index.js.map +0 -1
- package/dist/migrations/db/migrations/1684951997326-init.d.ts +0 -3
- package/dist/migrations/db/migrations/1684951997326-init.js +0 -33
- package/dist/migrations/db/migrations/1684951997326-init.js.map +0 -1
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.d.ts +0 -2
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +0 -5
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +0 -1
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +0 -3
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +0 -56
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +0 -1
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +0 -3
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +0 -29
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +0 -1
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.d.ts +0 -3
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js +0 -31
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js.map +0 -1
- package/dist/migrations/definitions.d.ts +0 -18
- package/dist/migrations/definitions.js +0 -6
- package/dist/migrations/definitions.js.map +0 -1
- package/dist/migrations/executor.d.ts +0 -16
- package/dist/migrations/executor.js +0 -64
- package/dist/migrations/executor.js.map +0 -1
- package/dist/migrations/migrations.d.ts +0 -18
- package/dist/migrations/migrations.js +0 -110
- package/dist/migrations/migrations.js.map +0 -1
- package/dist/migrations/store/migration-store.d.ts +0 -11
- package/dist/migrations/store/migration-store.js +0 -46
- package/dist/migrations/store/migration-store.js.map +0 -1
- package/dist/storage/MongoBucketStorage.d.ts +0 -48
- package/dist/storage/MongoBucketStorage.js +0 -427
- package/dist/storage/MongoBucketStorage.js.map +0 -1
- package/dist/storage/mongo/MongoBucketBatch.d.ts +0 -74
- package/dist/storage/mongo/MongoBucketBatch.js +0 -683
- package/dist/storage/mongo/MongoBucketBatch.js.map +0 -1
- package/dist/storage/mongo/MongoCompactor.d.ts +0 -40
- package/dist/storage/mongo/MongoCompactor.js +0 -310
- package/dist/storage/mongo/MongoCompactor.js.map +0 -1
- package/dist/storage/mongo/MongoIdSequence.d.ts +0 -12
- package/dist/storage/mongo/MongoIdSequence.js +0 -21
- package/dist/storage/mongo/MongoIdSequence.js.map +0 -1
- package/dist/storage/mongo/MongoPersistedSyncRules.d.ts +0 -9
- package/dist/storage/mongo/MongoPersistedSyncRules.js +0 -9
- package/dist/storage/mongo/MongoPersistedSyncRules.js.map +0 -1
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +0 -20
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +0 -26
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +0 -1
- package/dist/storage/mongo/MongoStorageProvider.d.ts +0 -5
- package/dist/storage/mongo/MongoStorageProvider.js +0 -26
- package/dist/storage/mongo/MongoStorageProvider.js.map +0 -1
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +0 -38
- package/dist/storage/mongo/MongoSyncBucketStorage.js +0 -534
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +0 -1
- package/dist/storage/mongo/MongoSyncRulesLock.d.ts +0 -16
- package/dist/storage/mongo/MongoSyncRulesLock.js +0 -65
- package/dist/storage/mongo/MongoSyncRulesLock.js.map +0 -1
- package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +0 -20
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js +0 -104
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +0 -1
- package/dist/storage/mongo/OperationBatch.d.ts +0 -35
- package/dist/storage/mongo/OperationBatch.js +0 -119
- package/dist/storage/mongo/OperationBatch.js.map +0 -1
- package/dist/storage/mongo/PersistedBatch.d.ts +0 -46
- package/dist/storage/mongo/PersistedBatch.js +0 -223
- package/dist/storage/mongo/PersistedBatch.js.map +0 -1
- package/dist/storage/mongo/config.d.ts +0 -19
- package/dist/storage/mongo/config.js +0 -26
- package/dist/storage/mongo/config.js.map +0 -1
- package/dist/storage/mongo/db.d.ts +0 -36
- package/dist/storage/mongo/db.js +0 -47
- package/dist/storage/mongo/db.js.map +0 -1
- package/dist/storage/mongo/models.d.ts +0 -163
- package/dist/storage/mongo/models.js +0 -27
- package/dist/storage/mongo/models.js.map +0 -1
- package/dist/storage/mongo/util.d.ts +0 -54
- package/dist/storage/mongo/util.js +0 -190
- package/dist/storage/mongo/util.js.map +0 -1
- package/src/db/db-index.ts +0 -1
- package/src/db/mongo.ts +0 -81
- package/src/locks/LockManager.ts +0 -16
- package/src/locks/MongoLocks.ts +0 -142
- package/src/locks/locks-index.ts +0 -2
- package/src/migrations/db/migrations/1684951997326-init.ts +0 -38
- package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +0 -5
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +0 -102
- package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +0 -34
- package/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts +0 -37
- package/src/migrations/definitions.ts +0 -21
- package/src/migrations/executor.ts +0 -87
- package/src/migrations/migrations.ts +0 -142
- package/src/migrations/store/migration-store.ts +0 -63
- package/src/storage/MongoBucketStorage.ts +0 -541
- package/src/storage/mongo/MongoBucketBatch.ts +0 -900
- package/src/storage/mongo/MongoCompactor.ts +0 -393
- package/src/storage/mongo/MongoIdSequence.ts +0 -24
- package/src/storage/mongo/MongoPersistedSyncRules.ts +0 -16
- package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +0 -50
- package/src/storage/mongo/MongoStorageProvider.ts +0 -31
- package/src/storage/mongo/MongoSyncBucketStorage.ts +0 -640
- package/src/storage/mongo/MongoSyncRulesLock.ts +0 -85
- package/src/storage/mongo/MongoWriteCheckpointAPI.ts +0 -154
- package/src/storage/mongo/OperationBatch.ts +0 -131
- package/src/storage/mongo/PersistedBatch.ts +0 -285
- package/src/storage/mongo/config.ts +0 -40
- package/src/storage/mongo/db.ts +0 -88
- package/src/storage/mongo/models.ts +0 -187
- package/src/storage/mongo/util.ts +0 -203
- package/test/src/__snapshots__/sync.test.ts.snap +0 -332
- package/test/src/bucket_validation.test.ts +0 -143
- package/test/src/bucket_validation.ts +0 -60
- package/test/src/compacting.test.ts +0 -295
- package/test/src/data_storage.test.ts +0 -1569
- package/test/src/stream_utils.ts +0 -42
- package/test/src/sync.test.ts +0 -511
- package/test/src/util.ts +0 -150
package/src/locks/MongoLocks.ts
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
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
|
-
};
|
package/src/locks/locks-index.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
|
|
5
|
-
export const up = async (context: utils.MigrationContext) => {
|
|
6
|
-
const { runner_config } = context;
|
|
7
|
-
|
|
8
|
-
const config = await utils.loadConfig(runner_config);
|
|
9
|
-
|
|
10
|
-
const database = storage.createPowerSyncMongo(config.storage);
|
|
11
|
-
await mongo.waitForAuth(database.db);
|
|
12
|
-
try {
|
|
13
|
-
await database.bucket_parameters.createIndex(
|
|
14
|
-
{
|
|
15
|
-
'key.g': 1,
|
|
16
|
-
lookup: 1,
|
|
17
|
-
_id: 1
|
|
18
|
-
},
|
|
19
|
-
{ name: 'lookup1' }
|
|
20
|
-
);
|
|
21
|
-
} finally {
|
|
22
|
-
await database.client.close();
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const down = async (context: utils.MigrationContext) => {
|
|
27
|
-
const { runner_config } = context;
|
|
28
|
-
const config = await utils.loadConfig(runner_config);
|
|
29
|
-
|
|
30
|
-
const database = storage.createPowerSyncMongo(config.storage);
|
|
31
|
-
try {
|
|
32
|
-
if (await database.bucket_parameters.indexExists('lookup')) {
|
|
33
|
-
await database.bucket_parameters.dropIndex('lookup1');
|
|
34
|
-
}
|
|
35
|
-
} finally {
|
|
36
|
-
await database.client.close();
|
|
37
|
-
}
|
|
38
|
-
};
|
|
@@ -1,102 +0,0 @@
|
|
|
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
|
-
|
|
5
|
-
interface LegacySyncRulesDocument extends storage.SyncRuleDocument {
|
|
6
|
-
/**
|
|
7
|
-
* True if this is the active sync rules.
|
|
8
|
-
* requires `snapshot_done == true` and `replicating == true`.
|
|
9
|
-
*/
|
|
10
|
-
active?: boolean;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* True if this sync rules should be used for replication.
|
|
14
|
-
*
|
|
15
|
-
* During reprocessing, there is one sync rules with `replicating = true, active = true`,
|
|
16
|
-
* and one with `replicating = true, active = false, auto_activate = true`.
|
|
17
|
-
*/
|
|
18
|
-
replicating?: boolean;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* True if the sync rules should set `active = true` when `snapshot_done` = true.
|
|
22
|
-
*/
|
|
23
|
-
auto_activate?: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const up = async (context: utils.MigrationContext) => {
|
|
27
|
-
const { runner_config } = context;
|
|
28
|
-
const config = await utils.loadConfig(runner_config);
|
|
29
|
-
const db = storage.createPowerSyncMongo(config.storage);
|
|
30
|
-
|
|
31
|
-
await mongo.waitForAuth(db.db);
|
|
32
|
-
try {
|
|
33
|
-
// We keep the old flags for existing deployments still shutting down.
|
|
34
|
-
|
|
35
|
-
// 1. New sync rules: `active = false, snapshot_done = false, replicating = true, auto_activate = true`
|
|
36
|
-
await db.sync_rules.updateMany(
|
|
37
|
-
{
|
|
38
|
-
active: { $ne: true },
|
|
39
|
-
replicating: true,
|
|
40
|
-
auto_activate: true
|
|
41
|
-
},
|
|
42
|
-
{ $set: { state: storage.SyncRuleState.PROCESSING } }
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// 2. Snapshot done: `active = true, snapshot_done = true, replicating = true, auto_activate = false`
|
|
46
|
-
await db.sync_rules.updateMany(
|
|
47
|
-
{
|
|
48
|
-
active: true
|
|
49
|
-
},
|
|
50
|
-
{ $set: { state: storage.SyncRuleState.ACTIVE } }
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// 3. Stopped: `active = false, snapshot_done = true, replicating = false, auto_activate = false`.
|
|
54
|
-
await db.sync_rules.updateMany(
|
|
55
|
-
{
|
|
56
|
-
active: { $ne: true },
|
|
57
|
-
replicating: { $ne: true },
|
|
58
|
-
auto_activate: { $ne: true }
|
|
59
|
-
},
|
|
60
|
-
{ $set: { state: storage.SyncRuleState.STOP } }
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
const remaining = await db.sync_rules.find({ state: null as any }).toArray();
|
|
64
|
-
if (remaining.length > 0) {
|
|
65
|
-
const slots = remaining.map((doc) => doc.slot_name).join(', ');
|
|
66
|
-
throw new Error(`Invalid state for sync rules: ${slots}`);
|
|
67
|
-
}
|
|
68
|
-
} finally {
|
|
69
|
-
await db.client.close();
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const down = async (context: utils.MigrationContext) => {
|
|
74
|
-
const { runner_config } = context;
|
|
75
|
-
const config = await utils.loadConfig(runner_config);
|
|
76
|
-
|
|
77
|
-
const db = storage.createPowerSyncMongo(config.storage);
|
|
78
|
-
try {
|
|
79
|
-
await db.sync_rules.updateMany(
|
|
80
|
-
{
|
|
81
|
-
state: storage.SyncRuleState.ACTIVE
|
|
82
|
-
},
|
|
83
|
-
{ $set: { active: true, replicating: true } }
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
await db.sync_rules.updateMany(
|
|
87
|
-
{
|
|
88
|
-
state: storage.SyncRuleState.PROCESSING
|
|
89
|
-
},
|
|
90
|
-
{ $set: { active: false, replicating: true, auto_activate: true } }
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
await db.sync_rules.updateMany(
|
|
94
|
-
{
|
|
95
|
-
$or: [{ state: storage.SyncRuleState.STOP }, { state: storage.SyncRuleState.TERMINATED }]
|
|
96
|
-
},
|
|
97
|
-
{ $set: { active: false, replicating: false, auto_activate: false } }
|
|
98
|
-
);
|
|
99
|
-
} finally {
|
|
100
|
-
await db.client.close();
|
|
101
|
-
}
|
|
102
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import * as storage from '../../../storage/storage-index.js';
|
|
2
|
-
import * as utils from '../../../util/util-index.js';
|
|
3
|
-
|
|
4
|
-
export const up = async (context: utils.MigrationContext) => {
|
|
5
|
-
const { runner_config } = context;
|
|
6
|
-
const config = await utils.loadConfig(runner_config);
|
|
7
|
-
const db = storage.createPowerSyncMongo(config.storage);
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
await db.write_checkpoints.createIndex(
|
|
11
|
-
{
|
|
12
|
-
user_id: 1
|
|
13
|
-
},
|
|
14
|
-
{ name: 'user_id' }
|
|
15
|
-
);
|
|
16
|
-
} finally {
|
|
17
|
-
await db.client.close();
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const down = async (context: utils.MigrationContext) => {
|
|
22
|
-
const { runner_config } = context;
|
|
23
|
-
const config = await utils.loadConfig(runner_config);
|
|
24
|
-
|
|
25
|
-
const db = storage.createPowerSyncMongo(config.storage);
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
if (await db.write_checkpoints.indexExists('user_id')) {
|
|
29
|
-
await db.write_checkpoints.dropIndex('user_id');
|
|
30
|
-
}
|
|
31
|
-
} finally {
|
|
32
|
-
await db.client.close();
|
|
33
|
-
}
|
|
34
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as storage from '../../../storage/storage-index.js';
|
|
2
|
-
import * as utils from '../../../util/util-index.js';
|
|
3
|
-
|
|
4
|
-
const INDEX_NAME = 'user_sync_rule_unique';
|
|
5
|
-
|
|
6
|
-
export const up = async (context: utils.MigrationContext) => {
|
|
7
|
-
const { runner_config } = context;
|
|
8
|
-
const config = await utils.loadConfig(runner_config);
|
|
9
|
-
const db = storage.createPowerSyncMongo(config.storage);
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
await db.custom_write_checkpoints.createIndex(
|
|
13
|
-
{
|
|
14
|
-
user_id: 1,
|
|
15
|
-
sync_rules_id: 1
|
|
16
|
-
},
|
|
17
|
-
{ name: INDEX_NAME, unique: true }
|
|
18
|
-
);
|
|
19
|
-
} finally {
|
|
20
|
-
await db.client.close();
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const down = async (context: utils.MigrationContext) => {
|
|
25
|
-
const { runner_config } = context;
|
|
26
|
-
const config = await utils.loadConfig(runner_config);
|
|
27
|
-
|
|
28
|
-
const db = storage.createPowerSyncMongo(config.storage);
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
if (await db.custom_write_checkpoints.indexExists(INDEX_NAME)) {
|
|
32
|
-
await db.custom_write_checkpoints.dropIndex(INDEX_NAME);
|
|
33
|
-
}
|
|
34
|
-
} finally {
|
|
35
|
-
await db.client.close();
|
|
36
|
-
}
|
|
37
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,87 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs/promises';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
import { logger } from '@powersync/lib-services-framework';
|
|
6
|
-
import * as db from '../db/db-index.js';
|
|
7
|
-
import * as locks from '../locks/locks-index.js';
|
|
8
|
-
import * as util from '../util/util-index.js';
|
|
9
|
-
import { Direction } from './definitions.js';
|
|
10
|
-
import { execute, writeLogsToStore } from './executor.js';
|
|
11
|
-
import { createMongoMigrationStore } from './store/migration-store.js';
|
|
12
|
-
|
|
13
|
-
const DEFAULT_MONGO_LOCK_COLLECTION = 'locks';
|
|
14
|
-
const MONGO_LOCK_PROCESS = 'migrations';
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = path.dirname(__filename);
|
|
18
|
-
|
|
19
|
-
const MIGRATIONS_DIR = path.join(__dirname, '/db/migrations');
|
|
20
|
-
|
|
21
|
-
export type MigrationOptions = {
|
|
22
|
-
direction: Direction;
|
|
23
|
-
runner_config: util.RunnerConfig;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export type AutomaticMigrationParams = {
|
|
27
|
-
config: util.ResolvedPowerSyncConfig;
|
|
28
|
-
runner_config: util.RunnerConfig;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Loads migrations and injects a custom context for loading the specified
|
|
33
|
-
* runner configuration.
|
|
34
|
-
*/
|
|
35
|
-
const loadMigrations = async (dir: string, runnerConfig: util.RunnerConfig) => {
|
|
36
|
-
const files = await fs.readdir(dir);
|
|
37
|
-
const migrations = files.filter((file) => {
|
|
38
|
-
return path.extname(file) === '.js';
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const context: util.MigrationContext = {
|
|
42
|
-
runner_config: runnerConfig
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return await Promise.all(
|
|
46
|
-
migrations.map(async (migration) => {
|
|
47
|
-
const module = await import(path.resolve(dir, migration));
|
|
48
|
-
return {
|
|
49
|
-
name: path.basename(migration).replace(path.extname(migration), ''),
|
|
50
|
-
up: () => module.up(context),
|
|
51
|
-
down: () => module.down(context)
|
|
52
|
-
};
|
|
53
|
-
})
|
|
54
|
-
);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Runs migration scripts exclusively using Mongo locks
|
|
59
|
-
*/
|
|
60
|
-
export const migrate = async (options: MigrationOptions) => {
|
|
61
|
-
const { direction, runner_config } = options;
|
|
62
|
-
|
|
63
|
-
const config = await util.loadConfig(runner_config);
|
|
64
|
-
const { storage } = config;
|
|
65
|
-
/**
|
|
66
|
-
* Try and get Mongo from config file.
|
|
67
|
-
* But this might not be available in Journey Micro as we use the standard Mongo.
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
|
-
const client = db.mongo.createMongoClient(storage);
|
|
71
|
-
logger.info('Connecting to MongoDB');
|
|
72
|
-
await client.connect();
|
|
73
|
-
|
|
74
|
-
const clientDB = client.db(storage.database);
|
|
75
|
-
const collection = clientDB.collection<locks.Lock>(DEFAULT_MONGO_LOCK_COLLECTION);
|
|
76
|
-
|
|
77
|
-
const manager = locks.createMongoLockManager(collection, {
|
|
78
|
-
name: MONGO_LOCK_PROCESS
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Only one process should execute this at a time.
|
|
82
|
-
logger.info('Acquiring lock');
|
|
83
|
-
const lockId = await manager.acquire();
|
|
84
|
-
|
|
85
|
-
if (!lockId) {
|
|
86
|
-
throw new Error('Could not acquire lock_id');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
let isReleased = false;
|
|
90
|
-
const releaseLock = async () => {
|
|
91
|
-
if (isReleased) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
await manager.release(lockId);
|
|
95
|
-
isReleased = true;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// For the case where the migration is terminated
|
|
99
|
-
process.addListener('beforeExit', releaseLock);
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
logger.info('Loading migrations');
|
|
103
|
-
const migrations = await loadMigrations(MIGRATIONS_DIR, runner_config);
|
|
104
|
-
|
|
105
|
-
// Use the provided config to connect to Mongo
|
|
106
|
-
const store = createMongoMigrationStore(clientDB);
|
|
107
|
-
|
|
108
|
-
const state = await store.load();
|
|
109
|
-
|
|
110
|
-
logger.info('Running migrations');
|
|
111
|
-
const logStream = execute({
|
|
112
|
-
direction: direction,
|
|
113
|
-
migrations,
|
|
114
|
-
state
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
await writeLogsToStore({
|
|
118
|
-
log_stream: logStream,
|
|
119
|
-
store,
|
|
120
|
-
state
|
|
121
|
-
});
|
|
122
|
-
} finally {
|
|
123
|
-
logger.info('Releasing lock');
|
|
124
|
-
await releaseLock();
|
|
125
|
-
logger.info('Closing database');
|
|
126
|
-
await client.close(true);
|
|
127
|
-
process.removeListener('beforeExit', releaseLock);
|
|
128
|
-
logger.info('Done with migrations');
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Ensures automatic migrations are executed
|
|
134
|
-
*/
|
|
135
|
-
export const ensureAutomaticMigrations = async (params: AutomaticMigrationParams) => {
|
|
136
|
-
if (!params.config.migrations?.disable_auto_migration) {
|
|
137
|
-
await migrate({
|
|
138
|
-
direction: Direction.Up,
|
|
139
|
-
runner_config: params.runner_config
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
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
|
-
};
|