@@ -11,36 +11,11 @@ import { Locker } from './local-lock';
1111export const MUTEX_RETRY = 5 ;
1212export const MUTEX_WAIT = 100 ;
1313
14- @Injectable ( { scope : Scope . REQUEST } )
15- export class MutexService {
16- protected logger = new Logger ( MutexService . name ) ;
17- private readonly locker : Locker ;
18-
19- constructor (
20- @Inject ( REQUEST ) private readonly request : Request | GraphqlContext ,
21- private readonly ref : ModuleRef
22- ) {
23- // nestjs will always find and injecting the locker from local module
24- // so the RedisLocker implemented by the plugin mechanism will not be able to overwrite the internal locker
25- // we need to use find and get the locker from the `ModuleRef` manually
26- //
27- // NOTE: when a `constructor` execute in normal service, the Locker module we expect may not have been initialized
28- // but in the Service with `Scope.REQUEST`, we will create a separate Service instance for each request
29- // at this time, all modules have been initialized, so we able to get the correct Locker instance in `constructor`
30- this . locker = this . ref . get ( Locker , { strict : false } ) ;
31- }
14+ @Injectable ( )
15+ export class Mutex {
16+ protected logger = new Logger ( Mutex . name ) ;
3217
33- protected getId ( ) {
34- const req = 'req' in this . request ? this . request . req : this . request ;
35- let id = req . headers [ 'x-transaction-id' ] as string ;
36-
37- if ( ! id ) {
38- id = randomUUID ( ) ;
39- req . headers [ 'x-transaction-id' ] = id ;
40- }
41-
42- return id ;
43- }
18+ constructor ( protected readonly locker : Locker ) { }
4419
4520 /**
4621 * lock an resource and return a lock guard, which will release the lock when disposed
@@ -63,10 +38,10 @@ export class MutexService {
6338 * @param key resource key
6439 * @returns LockGuard
6540 */
66- async lock ( key : string ) {
41+ async lock ( key : string , owner : string = 'global' ) {
6742 try {
6843 return await retryable (
69- ( ) => this . locker . lock ( this . getId ( ) , key ) ,
44+ ( ) => this . locker . lock ( owner , key ) ,
7045 MUTEX_RETRY ,
7146 MUTEX_WAIT
7247 ) ;
@@ -79,3 +54,36 @@ export class MutexService {
7954 }
8055 }
8156}
57+
58+ @Injectable ( { scope : Scope . REQUEST } )
59+ export class RequestMutex extends Mutex {
60+ constructor (
61+ @Inject ( REQUEST ) private readonly request : Request | GraphqlContext ,
62+ ref : ModuleRef
63+ ) {
64+ // nestjs will always find and injecting the locker from local module
65+ // so the RedisLocker implemented by the plugin mechanism will not be able to overwrite the internal locker
66+ // we need to use find and get the locker from the `ModuleRef` manually
67+ //
68+ // NOTE: when a `constructor` execute in normal service, the Locker module we expect may not have been initialized
69+ // but in the Service with `Scope.REQUEST`, we will create a separate Service instance for each request
70+ // at this time, all modules have been initialized, so we able to get the correct Locker instance in `constructor`
71+ super ( ref . get ( Locker ) ) ;
72+ }
73+
74+ protected getId ( ) {
75+ const req = 'req' in this . request ? this . request . req : this . request ;
76+ let id = req . headers [ 'x-transaction-id' ] as string ;
77+
78+ if ( ! id ) {
79+ id = randomUUID ( ) ;
80+ req . headers [ 'x-transaction-id' ] = id ;
81+ }
82+
83+ return id ;
84+ }
85+
86+ override lock ( key : string ) {
87+ return super . lock ( key , this . getId ( ) ) ;
88+ }
89+ }
0 commit comments