11import { readdirSync , rmSync } from 'fs' ;
22import { join } from 'path' ;
3+ import { Mutex } from 'async-mutex' ;
34import { open , Database , RootDatabase } from 'lmdb' ;
45import { Logger } from 'pino' ;
56import { LoggerFactory } from '../telemetry/LoggerFactory' ;
67import { ScopedTelemetry } from '../telemetry/ScopedTelemetry' ;
78import { Telemetry } from '../telemetry/TelemetryDecorator' ;
89import { TelemetryService } from '../telemetry/TelemetryService' ;
910import { pathToArtifact } from '../utils/ArtifactsDir' ;
11+ import { isWindows } from '../utils/Environment' ;
1012import { toString } from '../utils/String' ;
1113import { DataStore , DataStoreFactory , StoreName } from './DataStore' ;
1214import { encryptionStrategy } from './lmdb/Utils' ;
1315
1416export class LMDBStore implements DataStore {
15- private readonly log : Logger ;
16- private readonly telemetry : ScopedTelemetry ;
17-
1817 constructor (
1918 public readonly name : StoreName ,
20- private readonly store : Database < unknown , string > ,
19+ protected readonly store : Database < unknown , string > ,
20+ private readonly telemetry : ScopedTelemetry = TelemetryService . instance . get ( `LMDB.${ name } ` ) ,
21+ private readonly log : Logger = LoggerFactory . getLogger ( `LMDB.${ name } ` ) ,
2122 ) {
22- this . telemetry = TelemetryService . instance . get ( `LMDB.${ name } ` ) ;
23- this . log = LoggerFactory . getLogger ( `LMDB.${ name } ` ) ;
2423 this . log . info ( 'Initialized' ) ;
2524 }
2625
@@ -29,21 +28,21 @@ export class LMDBStore implements DataStore {
2928 return this . store . get ( key ) as T | undefined ;
3029 }
3130
32- put < T > ( key : string , value : T ) : Promise < boolean > {
31+ async put < T > ( key : string , value : T ) : Promise < boolean > {
3332 this . log . info ( `Put ${ key } ` ) ;
34- return this . telemetry . measureAsync ( 'put' , ( ) => {
33+ return await this . telemetry . measureAsync ( 'put' , ( ) => {
3534 return this . store . put ( key , value ) ;
3635 } ) ;
3736 }
3837
39- remove ( key : string ) : Promise < boolean > {
38+ async remove ( key : string ) : Promise < boolean > {
4039 this . log . info ( `Remove ${ key } ` ) ;
41- return this . store . remove ( key ) ;
40+ return await this . store . remove ( key ) ;
4241 }
4342
44- clear ( ) : Promise < void > {
43+ async clear ( ) : Promise < void > {
4544 this . log . info ( `Clear ${ this . name } ` ) ;
46- return this . store . clearAsync ( ) ;
45+ return await this . store . clearAsync ( ) ;
4746 }
4847
4948 keys ( limit : number ) : ReadonlyArray < string > {
@@ -56,6 +55,52 @@ export class LMDBStore implements DataStore {
5655 }
5756}
5857
58+ export class LockedLMDBStore extends LMDBStore {
59+ private readonly lock = new Mutex ( ) ;
60+
61+ constructor ( name : StoreName , store : Database < unknown , string > ) {
62+ super (
63+ name ,
64+ store ,
65+ TelemetryService . instance . get ( `LockedLMDBStore.${ name } ` ) ,
66+ LoggerFactory . getLogger ( `LockedLMDBStore.${ name } ` ) ,
67+ ) ;
68+ }
69+
70+ override get < T > ( key : string ) : T | undefined {
71+ if ( this . lock . isLocked ( ) ) {
72+ return undefined ;
73+ }
74+
75+ return super . get ( key ) ;
76+ }
77+
78+ override async put < T > ( key : string , value : T ) : Promise < boolean > {
79+ return await this . lock . runExclusive ( async ( ) => {
80+ return await super . put ( key , value ) ;
81+ } ) ;
82+ }
83+
84+ override async remove ( key : string ) : Promise < boolean > {
85+ return await this . lock . runExclusive ( async ( ) => {
86+ return await super . remove ( key ) ;
87+ } ) ;
88+ }
89+
90+ override async clear ( ) : Promise < void > {
91+ return await this . lock . runExclusive ( async ( ) => {
92+ return await super . clear ( ) ;
93+ } ) ;
94+ }
95+
96+ override keys ( limit : number ) : ReadonlyArray < string > {
97+ if ( this . lock . isLocked ( ) ) {
98+ return [ ] ;
99+ }
100+ return super . keys ( limit ) ;
101+ }
102+ }
103+
59104export class LMDBStoreFactory implements DataStoreFactory {
60105 private readonly log : Logger ;
61106 @Telemetry ( { scope : 'LMDB.Global' } ) private readonly telemetry ! : ScopedTelemetry ;
@@ -90,7 +135,14 @@ export class LMDBStoreFactory implements DataStoreFactory {
90135 name : store ,
91136 encoding : Encoding ,
92137 } ) ;
93- this . stores . set ( store , new LMDBStore ( store , database ) ) ;
138+
139+ let lmdbStore : LMDBStore ;
140+ if ( isWindows ) {
141+ lmdbStore = new LockedLMDBStore ( store , database ) ;
142+ } else {
143+ lmdbStore = new LMDBStore ( store , database ) ;
144+ }
145+ this . stores . set ( store , lmdbStore ) ;
94146 }
95147 this . registerLMDBGauges ( ) ;
96148
@@ -177,7 +229,7 @@ export class LMDBStoreFactory implements DataStoreFactory {
177229
178230const Version = 4 ;
179231const VersionFileName = `.v${ Version } .lmdb` ;
180- const Encoding : 'msgpack' | 'json' | 'string' | 'binary' | 'ordered-binary' = 'msgpack' ;
232+ const Encoding = 'msgpack' ;
181233const TotalMaxDbSize = 5 * 250 * 1024 * 1024 ; // 250MB max size
182234
183235function stats ( store : RootDatabase | Database ) : StoreStatsType {
0 commit comments