@@ -10,16 +10,46 @@ import RepositoryInterface from '../interface.js';
1010import * as DataMapper from './dataMapper.js' ;
1111
1212export default class MongoRepository extends RepositoryInterface {
13+ /**
14+ * Shared MongoClient management with reference counting.
15+ *
16+ * Multiple repositories connecting to the same MongoDB server/database share a single MongoClient instance
17+ * to avoid race conditions during cleanup. Without sharing, each repository would create its own client,
18+ * and closing them sequentially could still cause "Operation interrupted because client was closed" errors.
19+ * This issue has been observed on Windows but not on Linux or macOS, suggesting platform-specific differences
20+ * in how connection pool cleanup is handled.
21+ *
22+ * The reference counting ensures that the shared client is only closed when the last repository using it
23+ * calls finalize(), preventing premature connection closure while operations are still in progress.
24+ */
25+ static #clients = new Map ( ) ; // connectionURI -> MongoClient
26+ static #refCounts = new Map ( ) ; // connectionURI -> number of repositories using this client
27+
1328 constructor ( { database : databaseName , collection : collectionName , connectionURI } ) {
1429 super ( ) ;
1530
16- this . client = new MongoClient ( connectionURI ) ;
31+ this . connectionURI = connectionURI ;
1732 this . databaseName = databaseName ;
1833 this . collectionName = collectionName ;
34+ this . client = null ;
1935 }
2036
2137 async initialize ( ) {
22- await this . client . connect ( ) ;
38+ console . log ( 'MongoRepository.#clients' , MongoRepository . #clients) ;
39+ if ( MongoRepository . #clients. has ( this . connectionURI ) ) {
40+ console . log ( 'reuse client' ) ;
41+ // Reuse existing client
42+ this . client = MongoRepository . #clients. get ( this . connectionURI ) ;
43+ MongoRepository . #refCounts. set ( this . connectionURI , MongoRepository . #refCounts. get ( this . connectionURI ) + 1 ) ;
44+ } else {
45+ // Create new client
46+ console . log ( 'create new client' ) ;
47+ this . client = new MongoClient ( this . connectionURI ) ;
48+ await this . client . connect ( ) ;
49+ MongoRepository . #clients. set ( this . connectionURI , this . client ) ;
50+ MongoRepository . #refCounts. set ( this . connectionURI , 1 ) ;
51+ }
52+
2353 const db = this . client . db ( this . databaseName ) ;
2454
2555 this . collection = db . collection ( this . collectionName ) ;
@@ -29,8 +59,18 @@ export default class MongoRepository extends RepositoryInterface {
2959 return this ;
3060 }
3161
32- finalize ( ) {
33- return this . client . close ( ) ;
62+ async finalize ( ) {
63+ const refCount = MongoRepository . #refCounts. get ( this . connectionURI ) ;
64+
65+ if ( refCount === 1 ) {
66+ // Last repository using this client - close it
67+ await this . client . close ( ) ;
68+ MongoRepository . #clients. delete ( this . connectionURI ) ;
69+ MongoRepository . #refCounts. delete ( this . connectionURI ) ;
70+ } else {
71+ // Other repositories still using this client - just decrement
72+ MongoRepository . #refCounts. set ( this . connectionURI , refCount - 1 ) ;
73+ }
3474 }
3575
3676 async save ( record ) {
@@ -108,7 +148,7 @@ export default class MongoRepository extends RepositoryInterface {
108148 }
109149
110150 removeAll ( ) {
111- return this . collection . deleteMany ( ) ;
151+ return this . collection . deleteMany ( { } ) ;
112152 }
113153
114154 async loadRecordContent ( record ) {
0 commit comments