1
- import type { ResourceRelease } from '@matrixai/resources' ;
2
- import type {
3
- LockBox ,
4
- MultiLockRequest as AsyncLocksMultiLockRequest ,
5
- } from '@matrixai/async-locks' ;
1
+ import type { LockBox } from '@matrixai/async-locks' ;
6
2
import type DB from './DB' ;
7
3
import type {
8
- ToString ,
9
4
KeyPath ,
10
5
LevelPath ,
11
6
DBIteratorOptions ,
12
7
DBClearOptions ,
13
8
DBCountOptions ,
14
- MultiLockRequest ,
15
9
} from './types' ;
16
10
import type {
17
11
RocksDBTransaction ,
@@ -20,7 +14,12 @@ import type {
20
14
} from './native/types' ;
21
15
import Logger from '@matrixai/logger' ;
22
16
import { CreateDestroy , ready } from '@matrixai/async-init/dist/CreateDestroy' ;
23
- import { Lock , RWLockWriter } from '@matrixai/async-locks' ;
17
+ import {
18
+ Monitor ,
19
+ Lock ,
20
+ RWLockWriter ,
21
+ errors as asyncLocksErrors ,
22
+ } from '@matrixai/async-locks' ;
24
23
import DBIterator from './DBIterator' ;
25
24
import { rocksdbP } from './native' ;
26
25
import * as utils from './utils' ;
@@ -33,15 +32,7 @@ class DBTransaction {
33
32
34
33
protected _db : DB ;
35
34
protected logger : Logger ;
36
- protected lockBox : LockBox < RWLockWriter > ;
37
- protected _locks : Map <
38
- string ,
39
- {
40
- lock : RWLockWriter ;
41
- type : 'read' | 'write' ;
42
- release : ResourceRelease ;
43
- }
44
- > = new Map ( ) ;
35
+ protected monitor : Monitor < RWLockWriter > ;
45
36
protected _options : RocksDBTransactionOptions ;
46
37
protected _transaction : RocksDBTransaction ;
47
38
protected _snapshot : RocksDBTransactionSnapshot ;
@@ -58,18 +49,20 @@ class DBTransaction {
58
49
public constructor ( {
59
50
db,
60
51
lockBox,
52
+ locksPending,
61
53
logger,
62
54
...options
63
55
} : {
64
56
db : DB ;
65
57
lockBox : LockBox < RWLockWriter > ;
58
+ locksPending ?: Map < string , { count : number } > ;
66
59
logger ?: Logger ;
67
60
} & RocksDBTransactionOptions ) {
68
61
logger = logger ?? new Logger ( this . constructor . name ) ;
69
62
logger . debug ( `Constructing ${ this . constructor . name } ` ) ;
70
63
this . logger = logger ;
71
64
this . _db = db ;
72
- this . lockBox = lockBox ;
65
+ this . monitor = new Monitor ( lockBox , RWLockWriter , locksPending ) ;
73
66
const options_ = {
74
67
...options ,
75
68
// Transactions should be synchronous
@@ -96,9 +89,7 @@ class DBTransaction {
96
89
// this then allows the destruction to proceed
97
90
await this . commitOrRollbackLock . waitForUnlock ( ) ;
98
91
this . _db . transactionRefs . delete ( this ) ;
99
- // Unlock all locked keys in reverse
100
- const lockedKeys = [ ...this . _locks . keys ( ) ] . reverse ( ) ;
101
- await this . unlock ( ...lockedKeys ) ;
92
+ await this . monitor . unlockAll ( ) ;
102
93
this . logger . debug ( `Destroyed ${ this . constructor . name } ${ this . id } ` ) ;
103
94
}
104
95
@@ -150,15 +141,8 @@ class DBTransaction {
150
141
return this . _rollbacked ;
151
142
}
152
143
153
- get locks ( ) : ReadonlyMap <
154
- string ,
155
- {
156
- lock : RWLockWriter ;
157
- type : 'read' | 'write' ;
158
- release : ResourceRelease ;
159
- }
160
- > {
161
- return this . _locks ;
144
+ get locks ( ) : Monitor < RWLockWriter > [ 'locks' ] {
145
+ return this . monitor . locks ;
162
146
}
163
147
164
148
/**
@@ -168,78 +152,26 @@ class DBTransaction {
168
152
return this . _iteratorRefs ;
169
153
}
170
154
171
- /**
172
- * Lock a sequence of lock requests
173
- * If the lock request doesn't specify, it
174
- * defaults to using `RWLockWriter` with `write` type
175
- * Keys are locked in string sorted order
176
- * Even though keys can be arbitrary strings, by convention, you should use
177
- * keys that correspond to keys in the database
178
- * Locking with the same key is idempotent therefore lock re-entrancy is enabled
179
- * Keys are automatically unlocked in reverse sorted order
180
- * when the transaction is destroyed
181
- * There is no support for lock upgrading or downgrading
182
- * There is no deadlock detection
183
- */
184
155
public async lock (
185
- ...requests : Array < MultiLockRequest | ToString >
156
+ ...params : Parameters < Monitor < RWLockWriter > [ 'lock' ] >
186
157
) : Promise < void > {
187
- const requests_ : Array < AsyncLocksMultiLockRequest < RWLockWriter > > = [ ] ;
188
- for ( const request of requests ) {
189
- if ( Array . isArray ( request ) ) {
190
- const [ key , ...lockingParams ] = request ;
191
- const key_ = key . toString ( ) ;
192
- const lock = this . _locks . get ( key_ ) ;
193
- // Default the lock type to `write`
194
- const lockType = ( lockingParams [ 0 ] = lockingParams [ 0 ] ?? 'write' ) ;
195
- if ( lock == null ) {
196
- requests_ . push ( [ key_ , RWLockWriter , ...lockingParams ] ) ;
197
- } else if ( lock . type !== lockType ) {
198
- throw new errors . ErrorDBTransactionLockType ( ) ;
199
- }
200
- } else {
201
- const key_ = request . toString ( ) ;
202
- const lock = this . _locks . get ( key_ ) ;
203
- if ( lock == null ) {
204
- // Default to using `RWLockWriter` write lock for just string keys
205
- requests_ . push ( [ key_ , RWLockWriter , 'write' ] ) ;
206
- } else if ( lock . type !== 'write' ) {
207
- throw new errors . ErrorDBTransactionLockType ( ) ;
208
- }
158
+ try {
159
+ await this . monitor . lock ( ...params ) ( ) ;
160
+ } catch ( e ) {
161
+ if ( e instanceof asyncLocksErrors . ErrorAsyncLocksMonitorLockType ) {
162
+ throw new errors . ErrorDBTransactionLockType ( undefined , { cause : e } ) ;
209
163
}
210
- }
211
- if ( requests_ . length > 0 ) {
212
- // Duplicates are eliminated, and the returned acquisitions are sorted
213
- const lockAcquires = this . lockBox . lockMulti ( ...requests_ ) ;
214
- for ( const [ key , lockAcquire , ...lockingParams ] of lockAcquires ) {
215
- const [ lockRelease , lock ] = await lockAcquire ( ) ;
216
- // The `Map` will maintain insertion order
217
- // these must be unlocked in reverse order
218
- // when the transaction is destroyed
219
- this . _locks . set ( key as string , {
220
- lock : lock ! ,
221
- type : lockingParams [ 0 ] ! , // The `type` is defaulted to `write`
222
- release : lockRelease ,
223
- } ) ;
164
+ if ( e instanceof asyncLocksErrors . ErrorAsyncLocksMonitorDeadlock ) {
165
+ throw new errors . ErrorDBTransactionDeadlock ( undefined , { cause : e } ) ;
224
166
}
167
+ throw e ;
225
168
}
226
169
}
227
170
228
- /**
229
- * Unlock a sequence of lock keys
230
- * Unlocking will be done in the order of the keys
231
- * A transaction instance is only allowed to unlock keys that it previously
232
- * locked, all keys that are not part of the `this._locks` is ignored
233
- * Unlocking the same keys is idempotent
234
- */
235
- public async unlock ( ...keys : Array < ToString > ) : Promise < void > {
236
- for ( const key of keys ) {
237
- const key_ = key . toString ( ) ;
238
- const lock = this . _locks . get ( key_ ) ;
239
- if ( lock == null ) continue ;
240
- this . _locks . delete ( key_ ) ;
241
- await lock . release ( ) ;
242
- }
171
+ public async unlock (
172
+ ...params : Parameters < Monitor < RWLockWriter > [ 'unlock' ] >
173
+ ) : Promise < void > {
174
+ await this . monitor . unlock ( ...params ) ;
243
175
}
244
176
245
177
public async get < T > (
0 commit comments