@medusajs/locking 2.22.0-snapshot-20261020204851 → 2.22.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/AGENTS.md +71 -0
- package/README.md +98 -0
- package/package.json +8 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @medusajs/locking
|
|
2
|
+
|
|
3
|
+
Locking Module (`Modules.LOCKING`): infrastructure module that serializes access to keys across concurrent jobs, delegating to a locking provider (in-memory built in; Redis and Postgres providers live in `packages/modules/providers/locking-redis` and `locking-postgres`).
|
|
4
|
+
|
|
5
|
+
## Using this package
|
|
6
|
+
|
|
7
|
+
- Registered by default as `@medusajs/medusa/locking` with only the in-memory provider (cloud config registers `@medusajs/medusa/locking-redis` as default when `REDIS_URL` is set; see `packages/core/utils/src/common/define-config.ts`).
|
|
8
|
+
- Options (`LockingModuleOptions` in `src/types/index.ts`): `providers[]` of `{ resolve, id, is_default?, options? }`.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
module.exports = defineConfig({
|
|
12
|
+
modules: [
|
|
13
|
+
{
|
|
14
|
+
resolve: "@medusajs/medusa/locking",
|
|
15
|
+
options: {
|
|
16
|
+
providers: [
|
|
17
|
+
{
|
|
18
|
+
resolve: "@medusajs/medusa/locking-redis",
|
|
19
|
+
id: "locking-redis",
|
|
20
|
+
is_default: true,
|
|
21
|
+
options: { redisUrl: process.env.LOCKING_REDIS_URL },
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- Resolve: `container.resolve(Modules.LOCKING)` (`ILockingModule`). In workflows prefer `acquireLockStep` / `releaseLockStep` from `@medusajs/medusa/core-flows`.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const locking = container.resolve(Modules.LOCKING)
|
|
34
|
+
|
|
35
|
+
await locking.execute("prod_123", async () => {
|
|
36
|
+
// critical section
|
|
37
|
+
}, { timeout: 2 })
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- Methods (`src/services/locking-module.ts`), all accept `provider` to override the default:
|
|
41
|
+
- `execute(keys, job, { timeout?, expire?, provider? })`: acquires, runs `job`, always releases.
|
|
42
|
+
- `acquire(keys, { ownerId?, expire?, provider? })`, `release(keys, { ownerId?, provider? })`, `releaseAll({ ownerId?, provider? })`.
|
|
43
|
+
- No data models, no events, no module links.
|
|
44
|
+
|
|
45
|
+
Docs: https://docs.medusajs.com/resources/infrastructure-modules/locking.md, https://docs.medusajs.com/resources/infrastructure-modules/locking/redis.md, https://docs.medusajs.com/resources/infrastructure-modules/locking/postgres.md, https://docs.medusajs.com/learn/fundamentals/workflows/locks.md
|
|
46
|
+
|
|
47
|
+
## Architecture
|
|
48
|
+
|
|
49
|
+
- `src/loaders/providers.ts`: always registers `InMemoryLockingProvider` (`identifier = "in-memory"`) under `lp_in-memory` and as the default; then registers each configured provider as `lp_<id>` (alias of its provider registration key). The default becomes the provider with `is_default: true`, or the only configured provider when exactly one is configured.
|
|
50
|
+
- `src/services/locking-provider.ts` resolves `lp_<providerId>`; providers must have a static `identifier` and a non-empty `id`, or registration throws.
|
|
51
|
+
- `src/providers/in-memory.ts`: process-local `Map` of locks with owner, optional expiration, and a promise queue.
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
cd packages/modules/locking
|
|
57
|
+
yarn build
|
|
58
|
+
yarn test
|
|
59
|
+
yarn test:integration
|
|
60
|
+
../../../node_modules/.bin/jest integration-tests/__tests__/index.spec.ts -t "should release lock in case of failure"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Integration tests: `integration-tests/__tests__/index.spec.ts`.
|
|
64
|
+
- The `migration:*` scripts in `package.json` are unused: there is no `mikro-orm.config.dev.ts` and no `src/models`.
|
|
65
|
+
|
|
66
|
+
## Gotchas
|
|
67
|
+
|
|
68
|
+
- In-memory locks only protect a single process; multi-instance deployments need the Redis or Postgres provider.
|
|
69
|
+
- `timeout` and `expire` are in seconds. In-memory `execute` uses `timeout` default 5 and minimum 1, waits in the key's queue, and throws on timeout; the job is never started then.
|
|
70
|
+
- `acquire` without `ownerId` on an already locked, non-expired key throws `Failed to acquire lock for key "<key>"`. Re-acquiring with the same `ownerId` succeeds and refreshes `expire`.
|
|
71
|
+
- Locks taken by `acquire` without `expire` never expire in memory; release them explicitly.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.medusajs.com">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/59018053/229103275-b5e482bb-4601-46e6-8142-244f531cebdb.svg">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
|
|
6
|
+
<img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
|
|
7
|
+
</picture>
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
<h1 align="center">
|
|
11
|
+
@medusajs/locking
|
|
12
|
+
</h1>
|
|
13
|
+
|
|
14
|
+
<h4 align="center">
|
|
15
|
+
<a href="https://docs.medusajs.com">Documentation</a> |
|
|
16
|
+
<a href="https://www.medusajs.com">Website</a>
|
|
17
|
+
</h4>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
Building blocks for digital commerce
|
|
21
|
+
</p>
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://github.com/medusajs/medusa/blob/develop/LICENSE">
|
|
24
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Medusa is released under the MIT license." />
|
|
25
|
+
</a>
|
|
26
|
+
<a href="https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md">
|
|
27
|
+
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
|
|
28
|
+
</a>
|
|
29
|
+
<a href="https://discord.gg/xpCwq3Kfn8">
|
|
30
|
+
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
|
|
31
|
+
</a>
|
|
32
|
+
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
|
|
33
|
+
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
|
|
34
|
+
</a>
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
Locking Module for Medusa: acquires and releases locks on shared resources so that only one process runs a critical section at a time, preventing race conditions such as overselling inventory. Locks are handled by Locking Module Providers: an in-memory provider is built in, and Postgres or Redis providers coordinate locks across multiple instances. It ships with `@medusajs/medusa` and is registered by default; use it with the `acquireLockStep` and `releaseLockStep` workflow steps, or resolve it with `container.resolve(Modules.LOCKING)`.
|
|
38
|
+
|
|
39
|
+
## At a glance
|
|
40
|
+
|
|
41
|
+
| | |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| Package | `@medusajs/locking` |
|
|
44
|
+
| Type | Infrastructure Module |
|
|
45
|
+
| Module key | `Modules.LOCKING` (`"locking"`) |
|
|
46
|
+
| Registered by default | Yes (in-memory provider; Redis provider on Medusa Cloud when `REDIS_URL` is set) |
|
|
47
|
+
|
|
48
|
+
## Providers
|
|
49
|
+
|
|
50
|
+
The module accepts multiple providers. The built-in in-memory provider (`in-memory`) is always registered and is the default when you don't register others. Official providers:
|
|
51
|
+
|
|
52
|
+
| Package | Register with |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| [`@medusajs/locking-postgres`](../providers/locking-postgres) | `@medusajs/medusa/locking-postgres` |
|
|
55
|
+
| [`@medusajs/locking-redis`](../providers/locking-redis) | `@medusajs/medusa/locking-redis` |
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
No installation needed. The module ships with `@medusajs/medusa`.
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { defineConfig } from "@medusajs/framework/utils"
|
|
65
|
+
|
|
66
|
+
module.exports = defineConfig({
|
|
67
|
+
// ...
|
|
68
|
+
modules: [
|
|
69
|
+
{
|
|
70
|
+
resolve: "@medusajs/medusa/locking",
|
|
71
|
+
options: {
|
|
72
|
+
providers: [
|
|
73
|
+
{
|
|
74
|
+
resolve: "@medusajs/medusa/locking-redis",
|
|
75
|
+
id: "locking-redis",
|
|
76
|
+
is_default: true,
|
|
77
|
+
options: {
|
|
78
|
+
redisUrl: process.env.LOCKING_REDIS_URL,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
| Option | Type | Description | Default |
|
|
91
|
+
| --- | --- | --- | --- |
|
|
92
|
+
| `providers` | `{ resolve: string, id: string, is_default?: boolean, options?: Record<string, unknown> }[]` | Locking providers to register. When only one provider is registered, or one sets `is_default: true`, it becomes the default. | `[]` (in-memory provider is used) |
|
|
93
|
+
|
|
94
|
+
## Resources
|
|
95
|
+
|
|
96
|
+
- [Locking Module](https://docs.medusajs.com/resources/infrastructure-modules/locking)
|
|
97
|
+
- [Postgres Locking Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/locking/postgres)
|
|
98
|
+
- [Redis Locking Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/locking/redis)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@medusajs/locking",
|
|
3
|
-
"version": "2.22.0
|
|
3
|
+
"version": "2.22.0",
|
|
4
4
|
"description": "Locking Module for Medusa",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -13,10 +13,14 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
|
+
"AGENTS.md",
|
|
16
17
|
"!dist/**/__tests__",
|
|
17
18
|
"!dist/**/__mocks__",
|
|
18
19
|
"!dist/**/__fixtures__"
|
|
19
20
|
],
|
|
21
|
+
"llms": "https://docs.medusajs.com/llms.txt",
|
|
22
|
+
"llmsFull": "https://docs.medusajs.com/llms-full.txt",
|
|
23
|
+
"mcpServer": "https://docs.medusajs.com/mcp",
|
|
20
24
|
"publishConfig": {
|
|
21
25
|
"access": "public"
|
|
22
26
|
},
|
|
@@ -35,10 +39,10 @@
|
|
|
35
39
|
"orm:cache:clear": "MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLI=true medusa-mikro-orm cache:clear"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
38
|
-
"@medusajs/framework": "2.22.0
|
|
39
|
-
"@medusajs/test-utils": "2.22.0
|
|
42
|
+
"@medusajs/framework": "2.22.0",
|
|
43
|
+
"@medusajs/test-utils": "2.22.0"
|
|
40
44
|
},
|
|
41
45
|
"peerDependencies": {
|
|
42
|
-
"@medusajs/framework": "2.22.0
|
|
46
|
+
"@medusajs/framework": "2.22.0"
|
|
43
47
|
}
|
|
44
48
|
}
|