@@ -54,15 +54,16 @@ public sealed class AtlasSearchFixture : IDisposable
5454
5555 private readonly object _initLock = new ( ) ;
5656
57- // Per-collection one-time-init guards
58- private bool _historicalInitialized ;
59- private bool _moviesInitialized ;
60- private bool _embeddedMoviesInitialized ;
61- private bool _airbnbInitialized ;
62- private bool _testClassesInitialized ;
63- private bool _binaryVectorInitialized ;
64- private bool _autoEmbedInitialized ;
65- private bool _returnScopeInitialized ;
57+ // Per-collection one-time-init guards. Marked volatile so the lock-free fast-path
58+ // read in EnsureInitialized observes the write that publishes the seeded state.
59+ private volatile bool _historicalInitialized ;
60+ private volatile bool _moviesInitialized ;
61+ private volatile bool _embeddedMoviesInitialized ;
62+ private volatile bool _airbnbInitialized ;
63+ private volatile bool _testClassesInitialized ;
64+ private volatile bool _binaryVectorInitialized ;
65+ private volatile bool _autoEmbedInitialized ;
66+ private volatile bool _returnScopeInitialized ;
6667 private bool ? _isRerankSupported ;
6768
6869 // Routes events from the shared cluster to whichever subscribers test classes
@@ -222,13 +223,9 @@ private IMongoDatabase Database
222223 }
223224 }
224225
225- private void EnsureHistoricalInitialized ( )
226- {
227- if ( _historicalInitialized ) return ;
228- lock ( _initLock )
226+ private void EnsureHistoricalInitialized ( ) =>
227+ EnsureInitialized ( ( ) => _historicalInitialized , ( ) =>
229228 {
230- if ( _historicalInitialized ) return ;
231-
232229 var collection = Database . GetCollection < BsonDocument > ( HistoricalDocumentsName ) ;
233230 if ( ! collection . AsQueryable ( ) . Any ( ) )
234231 {
@@ -262,17 +259,11 @@ private void EnsureHistoricalInitialized()
262259 } ) ;
263260
264261 _historicalInitialized = true ;
265- ClearCapturedEvents ( ) ;
266- }
267- }
262+ } ) ;
268263
269- private void EnsureMoviesInitialized ( )
270- {
271- if ( _moviesInitialized ) return ;
272- lock ( _initLock )
264+ private void EnsureMoviesInitialized ( ) =>
265+ EnsureInitialized ( ( ) => _moviesInitialized , ( ) =>
273266 {
274- if ( _moviesInitialized ) return ;
275-
276267 // Synonyms are sourced from two separate small collections; the synonyms-tests
277268 // index references them by collection name.
278269 SeedSynonymCollection ( TransportSynonymsName , new [ ]
@@ -328,17 +319,11 @@ private void EnsureMoviesInitialized()
328319 } ) ;
329320
330321 _moviesInitialized = true ;
331- ClearCapturedEvents ( ) ;
332- }
333- }
322+ } ) ;
334323
335- private void EnsureEmbeddedMoviesInitialized ( )
336- {
337- if ( _embeddedMoviesInitialized ) return ;
338- lock ( _initLock )
324+ private void EnsureEmbeddedMoviesInitialized ( ) =>
325+ EnsureInitialized ( ( ) => _embeddedMoviesInitialized , ( ) =>
339326 {
340- if ( _embeddedMoviesInitialized ) return ;
341-
342327 var collection = Database . GetCollection < BsonDocument > ( EmbeddedMoviesName ) ;
343328 if ( ! collection . AsQueryable ( ) . Any ( ) )
344329 {
@@ -384,17 +369,11 @@ private void EnsureEmbeddedMoviesInitialized()
384369 } ) ;
385370
386371 _embeddedMoviesInitialized = true ;
387- ClearCapturedEvents ( ) ;
388- }
389- }
372+ } ) ;
390373
391- private void EnsureAirbnbInitialized ( )
392- {
393- if ( _airbnbInitialized ) return ;
394- lock ( _initLock )
374+ private void EnsureAirbnbInitialized ( ) =>
375+ EnsureInitialized ( ( ) => _airbnbInitialized , ( ) =>
395376 {
396- if ( _airbnbInitialized ) return ;
397-
398377 var collection = Database . GetCollection < BsonDocument > ( AirbnbListingsName ) ;
399378 if ( ! collection . AsQueryable ( ) . Any ( ) )
400379 {
@@ -428,17 +407,11 @@ private void EnsureAirbnbInitialized()
428407 } ) ;
429408
430409 _airbnbInitialized = true ;
431- ClearCapturedEvents ( ) ;
432- }
433- }
410+ } ) ;
434411
435- private void EnsureTestClassesInitialized ( )
436- {
437- if ( _testClassesInitialized ) return ;
438- lock ( _initLock )
412+ private void EnsureTestClassesInitialized ( ) =>
413+ EnsureInitialized ( ( ) => _testClassesInitialized , ( ) =>
439414 {
440- if ( _testClassesInitialized ) return ;
441-
442415 var collection = Database . GetCollection < BsonDocument > ( TestClassesName ) ;
443416 if ( ! collection . AsQueryable ( ) . Any ( ) )
444417 {
@@ -458,17 +431,11 @@ private void EnsureTestClassesInitialized()
458431 } ) ;
459432
460433 _testClassesInitialized = true ;
461- ClearCapturedEvents ( ) ;
462- }
463- }
434+ } ) ;
464435
465- private void EnsureBinaryVectorInitialized ( )
466- {
467- if ( _binaryVectorInitialized ) return ;
468- lock ( _initLock )
436+ private void EnsureBinaryVectorInitialized ( ) =>
437+ EnsureInitialized ( ( ) => _binaryVectorInitialized , ( ) =>
469438 {
470- if ( _binaryVectorInitialized ) return ;
471-
472439 var collection = Database . GetCollection < BsonDocument > ( BinaryVectorItemsName ) ;
473440 if ( ! collection . AsQueryable ( ) . Any ( ) )
474441 {
@@ -489,9 +456,7 @@ private void EnsureBinaryVectorInitialized()
489456 } ) ;
490457
491458 _binaryVectorInitialized = true ;
492- ClearCapturedEvents ( ) ;
493- }
494- }
459+ } ) ;
495460
496461 private void EnsureAutoEmbedInitialized ( )
497462 {
@@ -509,10 +474,9 @@ private void EnsureAutoEmbedInitialized()
509474 "key provisioned on the Atlas Local container; gate the calling test " +
510475 "with RequireEnvironment.Check().EnvironmentVariable(\" AUTO_EMBEDDING_TESTS_ENABLED\" )." ) ;
511476 }
512- lock ( _initLock )
513- {
514- if ( _autoEmbedInitialized ) return ;
515477
478+ EnsureInitialized ( ( ) => _autoEmbedInitialized , ( ) =>
479+ {
516480 var collection = Database . GetCollection < BsonDocument > ( AutoEmbedMoviesName ) ;
517481 if ( ! collection . AsQueryable ( ) . Any ( ) )
518482 {
@@ -539,8 +503,7 @@ private void EnsureAutoEmbedInitialized()
539503 WaitForAutoEmbedIndexReady ( collection , AutoEmbedIndexName ) ;
540504
541505 _autoEmbedInitialized = true ;
542- ClearCapturedEvents ( ) ;
543- }
506+ } ) ;
544507 }
545508
546509 private static BsonDocument BuildAutoEmbedMovie ( string title , string plot , int runtime , int year ) =>
@@ -587,13 +550,9 @@ private static void WaitForAutoEmbedIndexReady(IMongoCollection<BsonDocument> co
587550 "Voyage AI API quota or connectivity may be exhausted; verify the Atlas Local container has VOYAGE_API_KEY set." ) ;
588551 }
589552
590- private void EnsureReturnScopeInitialized ( )
591- {
592- if ( _returnScopeInitialized ) return ;
593- lock ( _initLock )
553+ private void EnsureReturnScopeInitialized ( ) =>
554+ EnsureInitialized ( ( ) => _returnScopeInitialized , ( ) =>
594555 {
595- if ( _returnScopeInitialized ) return ;
596-
597556 var directors =
598557 """
599558 [
@@ -728,12 +687,27 @@ private void EnsureReturnScopeInitialized()
728687 } ) ;
729688
730689 _returnScopeInitialized = true ;
690+ } ) ;
691+
692+ // ---- Helpers ----
693+
694+ // Double-checked one-time init shared by every EnsureXInitialized seeder. The single
695+ // _initLock serializes all seeding (fine: the suite never runs tests in parallel) and,
696+ // unlike Lazy<T>, a throwing seeder leaves the guard false so a transient Atlas/network
697+ // failure can be retried on the next access instead of being cached and re-thrown forever.
698+ // The guard is read lock-free on the fast path, so it must be a volatile field; isInitialized
699+ // reads it and initialize sets it (as its last step, before ClearCapturedEvents runs here).
700+ private void EnsureInitialized ( Func < bool > isInitialized , Action initialize )
701+ {
702+ if ( isInitialized ( ) ) return ;
703+ lock ( _initLock )
704+ {
705+ if ( isInitialized ( ) ) return ;
706+ initialize ( ) ;
731707 ClearCapturedEvents ( ) ;
732708 }
733709 }
734710
735- // ---- Helpers ----
736-
737711 private void SeedSynonymCollection ( string name , IEnumerable < BsonDocument > docs )
738712 {
739713 var collection = Database . GetCollection < BsonDocument > ( name ) ;
0 commit comments