@storyous/mongodb 1.7.0 → 1.7.2
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/README.md +134 -134
- package/lib/appData/index.d.ts +12 -12
- package/lib/appData/index.js +39 -39
- package/lib/appData/index.ts +48 -48
- package/lib/concurrentTask.d.ts +9 -9
- package/lib/concurrentTask.js +19 -19
- package/lib/concurrentTask.ts +30 -30
- package/lib/fixtures.d.ts +5 -5
- package/lib/fixtures.js +27 -27
- package/lib/fixtures.ts +36 -36
- package/lib/getCollection.d.ts +3 -3
- package/lib/getCollection.js +8 -8
- package/lib/getCollection.ts +5 -5
- package/lib/getMongoConfig.d.ts +6 -6
- package/lib/getMongoConfig.js +85 -85
- package/lib/getMongoConfig.ts +78 -78
- package/lib/getMongoLocker.d.ts +10 -10
- package/lib/getMongoLocker.js +52 -52
- package/lib/getMongoLocker.ts +71 -71
- package/lib/global-bundle.pem +2876 -2876
- package/lib/index.d.ts +15 -13
- package/lib/index.js +36 -32
- package/lib/index.ts +34 -30
- package/lib/migrations/Migration.d.ts +6 -6
- package/lib/migrations/Migration.js +21 -21
- package/lib/migrations/Migration.ts +29 -29
- package/lib/migrations/MigrationsStore.d.ts +13 -13
- package/lib/migrations/MigrationsStore.js +64 -64
- package/lib/migrations/MigrationsStore.ts +81 -81
- package/lib/migrations/runMigrations.d.ts +12 -12
- package/lib/migrations/runMigrations.js +49 -49
- package/lib/migrations/runMigrations.ts +57 -57
- package/lib/migrations/template.d.ts +3 -3
- package/lib/migrations/template.js +10 -10
- package/lib/migrations/template.ts +7 -7
- package/lib/mongoClient.d.ts +8 -8
- package/lib/mongoClient.js +17 -17
- package/lib/mongoClient.ts +25 -25
- package/lib/mongoErrorCodes.d.ts +6 -6
- package/lib/mongoErrorCodes.js +10 -10
- package/lib/mongoErrorCodes.ts +10 -10
- package/lib/mongoLocker.d.ts +3 -3
- package/lib/mongoLocker.js +16 -16
- package/lib/mongoLocker.ts +15 -15
- package/lib/withTransaction.d.ts +3 -3
- package/lib/withTransaction.js +20 -20
- package/lib/withTransaction.ts +24 -24
- package/package.json +2 -2
package/lib/getMongoLocker.ts
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import { AppError } from '@storyous/errors';
|
|
2
|
-
import { Collection, ObjectId } from 'mongodb';
|
|
3
|
-
import concurrentTask from './concurrentTask';
|
|
4
|
-
import { Key } from './mongoClient';
|
|
5
|
-
import mongoErrorCodes from './mongoErrorCodes';
|
|
6
|
-
|
|
7
|
-
export type LockCallback<T> = () => Promise<T>;
|
|
8
|
-
|
|
9
|
-
export type LockerOptions = {
|
|
10
|
-
noLaterThan?: number,
|
|
11
|
-
startAttemptsDelay?: number
|
|
12
|
-
expireIn?: number
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const _removeDeprecatedIndex = async (collection: Collection) => {
|
|
16
|
-
const collectionStats = await collection.stats();
|
|
17
|
-
if (collectionStats.indexDetails?.acquiredAt_1) {
|
|
18
|
-
await collection.dropIndex('acquiredAt_1');
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export default async (collection: Collection) => {
|
|
23
|
-
|
|
24
|
-
await _removeDeprecatedIndex(collection);
|
|
25
|
-
await collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
26
|
-
|
|
27
|
-
return async <T>(
|
|
28
|
-
key: Key,
|
|
29
|
-
callback: LockCallback<T>,
|
|
30
|
-
{ noLaterThan = 1000, startAttemptsDelay = 50, expireIn = 120000 }: LockerOptions = {},
|
|
31
|
-
): Promise<T> => {
|
|
32
|
-
|
|
33
|
-
const lockId = new ObjectId();
|
|
34
|
-
|
|
35
|
-
const releaseLock = () => collection.deleteOne({ _id: key as any, lockId });
|
|
36
|
-
|
|
37
|
-
const acquireLock = async () => {
|
|
38
|
-
try {
|
|
39
|
-
const now = new Date();
|
|
40
|
-
/**
|
|
41
|
-
* 3 possible situations
|
|
42
|
-
* a) There is no lock -> upsert will create the document
|
|
43
|
-
* b) There is an expired lock -> expiresAt: { $lte: now } will match and the document will be replaced
|
|
44
|
-
* c) There is an unexpired lock -> expiresAt: { $lte: now } will not match,
|
|
45
|
-
* database will try to insert the new one, but it will fail on duplicate key (_id) error
|
|
46
|
-
*/
|
|
47
|
-
await collection.replaceOne(
|
|
48
|
-
{ _id: key as any, expiresAt: { $lte: now } },
|
|
49
|
-
// replaceOne will use the _id from filter parameter when inserting new document
|
|
50
|
-
{ lockId, expiresAt: new Date(now.getTime() + expireIn) },
|
|
51
|
-
{ upsert: true },
|
|
52
|
-
);
|
|
53
|
-
} catch (err:any) {
|
|
54
|
-
if (mongoErrorCodes.DUPLICATE_KEY.includes(err.code)) {
|
|
55
|
-
throw AppError.concurrentRequest('The lock is already acquired.');
|
|
56
|
-
}
|
|
57
|
-
await releaseLock(); // the lock could be possible acquired
|
|
58
|
-
throw err;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
await concurrentTask(acquireLock, { noLaterThan, startAttemptsDelay });
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
return await callback();
|
|
66
|
-
|
|
67
|
-
} finally {
|
|
68
|
-
await releaseLock();
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
};
|
|
1
|
+
import { AppError } from '@storyous/errors';
|
|
2
|
+
import { Collection, ObjectId } from 'mongodb';
|
|
3
|
+
import concurrentTask from './concurrentTask';
|
|
4
|
+
import { Key } from './mongoClient';
|
|
5
|
+
import mongoErrorCodes from './mongoErrorCodes';
|
|
6
|
+
|
|
7
|
+
export type LockCallback<T> = () => Promise<T>;
|
|
8
|
+
|
|
9
|
+
export type LockerOptions = {
|
|
10
|
+
noLaterThan?: number,
|
|
11
|
+
startAttemptsDelay?: number
|
|
12
|
+
expireIn?: number
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const _removeDeprecatedIndex = async (collection: Collection) => {
|
|
16
|
+
const collectionStats = await collection.stats();
|
|
17
|
+
if (collectionStats.indexDetails?.acquiredAt_1) {
|
|
18
|
+
await collection.dropIndex('acquiredAt_1');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default async (collection: Collection) => {
|
|
23
|
+
|
|
24
|
+
await _removeDeprecatedIndex(collection);
|
|
25
|
+
await collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
26
|
+
|
|
27
|
+
return async <T>(
|
|
28
|
+
key: Key,
|
|
29
|
+
callback: LockCallback<T>,
|
|
30
|
+
{ noLaterThan = 1000, startAttemptsDelay = 50, expireIn = 120000 }: LockerOptions = {},
|
|
31
|
+
): Promise<T> => {
|
|
32
|
+
|
|
33
|
+
const lockId = new ObjectId();
|
|
34
|
+
|
|
35
|
+
const releaseLock = () => collection.deleteOne({ _id: key as any, lockId });
|
|
36
|
+
|
|
37
|
+
const acquireLock = async () => {
|
|
38
|
+
try {
|
|
39
|
+
const now = new Date();
|
|
40
|
+
/**
|
|
41
|
+
* 3 possible situations
|
|
42
|
+
* a) There is no lock -> upsert will create the document
|
|
43
|
+
* b) There is an expired lock -> expiresAt: { $lte: now } will match and the document will be replaced
|
|
44
|
+
* c) There is an unexpired lock -> expiresAt: { $lte: now } will not match,
|
|
45
|
+
* database will try to insert the new one, but it will fail on duplicate key (_id) error
|
|
46
|
+
*/
|
|
47
|
+
await collection.replaceOne(
|
|
48
|
+
{ _id: key as any, expiresAt: { $lte: now } },
|
|
49
|
+
// replaceOne will use the _id from filter parameter when inserting new document
|
|
50
|
+
{ lockId, expiresAt: new Date(now.getTime() + expireIn) },
|
|
51
|
+
{ upsert: true },
|
|
52
|
+
);
|
|
53
|
+
} catch (err:any) {
|
|
54
|
+
if (mongoErrorCodes.DUPLICATE_KEY.includes(err.code)) {
|
|
55
|
+
throw AppError.concurrentRequest('The lock is already acquired.');
|
|
56
|
+
}
|
|
57
|
+
await releaseLock(); // the lock could be possible acquired
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
await concurrentTask(acquireLock, { noLaterThan, startAttemptsDelay });
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
return await callback();
|
|
66
|
+
|
|
67
|
+
} finally {
|
|
68
|
+
await releaseLock();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
};
|