@@ -463,6 +463,107 @@ describe("Concurrent Migration Locking Integration", () => {
463463
464464 console . log ( "Sequential deployment results:" , results ) ;
465465 } ) ;
466+
467+ it ( "should handle 20 concurrent instances with only one acquiring lock (stress test)" , async function ( ) {
468+ this . timeout ( 60000 ) ;
469+
470+ // EXTREME STRESS TEST: Simulate massive concurrent deployment scenario
471+ // 20 instances all trying to migrate at exactly the same time
472+ const instanceCount = 20 ;
473+ const testShift = `/stress-test-${ Date . now ( ) } ` ;
474+
475+ // Create separate config for this test to avoid interference
476+ const stressConfig = new FirebaseConfig ( ) ;
477+ stressConfig . applicationCredentials = config . applicationCredentials ;
478+ stressConfig . databaseUrl = config . databaseUrl ;
479+ stressConfig . shift = testShift ;
480+ stressConfig . tableName = config . tableName ;
481+ stressConfig . folder = config . folder ;
482+ stressConfig . locking = new LockingConfig ( {
483+ enabled : true ,
484+ timeout : 10000 , // 10 seconds
485+ tableName : 'migration_locks' ,
486+ retryAttempts : 0 , // Fail fast - no retries
487+ retryDelay : 1000
488+ } ) ;
489+
490+ console . log ( `\n🚀 Creating ${ instanceCount } concurrent MSR Firebase instances...` ) ;
491+
492+ // Create all runner instances
493+ const runners : FirebaseRunner [ ] = [ ] ;
494+ for ( let i = 0 ; i < instanceCount ; i ++ ) {
495+ const runner = await FirebaseRunner . getInstance ( { config : stressConfig } ) ;
496+ runners . push ( runner ) ;
497+ // Tiny delay to avoid Firebase app name collisions
498+ if ( i < instanceCount - 1 ) {
499+ await new Promise ( resolve => setTimeout ( resolve , 5 ) ) ;
500+ }
501+ }
502+
503+ console . log ( `✅ All ${ instanceCount } instances created. Starting simultaneous migration attempts...` ) ;
504+
505+ // ALL 20 INSTANCES TRY TO MIGRATE AT THE EXACT SAME TIME
506+ const migrationPromises = runners . map ( ( runner , index ) =>
507+ runner . migrate ( )
508+ . then ( result => ( {
509+ index,
510+ status : 'success' ,
511+ executed : result . executed . length ,
512+ success : result . success
513+ } ) )
514+ . catch ( error => ( {
515+ index,
516+ status : 'failed' ,
517+ error : error . message || String ( error )
518+ } ) )
519+ ) ;
520+
521+ const results = await Promise . all ( migrationPromises ) ;
522+
523+ // Count successes and failures with type guards
524+ const successfulInstances = results . filter ( ( r ) : r is { index : number ; status : 'success' ; executed : number ; success : boolean } =>
525+ r . status === 'success' && 'success' in r && r . success
526+ ) ;
527+ const failedInstances = results . filter ( ( r ) : r is { index : number ; status : 'failed' ; error : string } =>
528+ r . status === 'failed'
529+ ) ;
530+
531+ console . log ( `\n📊 STRESS TEST RESULTS:` ) ;
532+ console . log ( ` ✅ Successful: ${ successfulInstances . length } ` ) ;
533+ console . log ( ` ❌ Failed (lock blocked): ${ failedInstances . length } ` ) ;
534+ console . log ( ` 📝 Total: ${ results . length } ` ) ;
535+
536+ // CRITICAL ASSERTIONS
537+ expect ( successfulInstances . length ) . to . equal (
538+ 1 ,
539+ `Expected exactly 1 instance to succeed, but ${ successfulInstances . length } succeeded`
540+ ) ;
541+
542+ expect ( failedInstances . length ) . to . equal (
543+ instanceCount - 1 ,
544+ `Expected ${ instanceCount - 1 } instances to fail, but ${ failedInstances . length } failed`
545+ ) ;
546+
547+ // Verify the successful instance actually executed migrations
548+ const winner = successfulInstances [ 0 ] ;
549+ console . log ( `\n🏆 Winner: Instance #${ winner . index } acquired lock and executed ${ winner . executed } migration(s)` ) ;
550+ expect ( winner . executed ) . to . be . greaterThan ( 0 , "Winner should have executed at least one migration" ) ;
551+
552+ // Verify all failures are lock-related
553+ failedInstances . forEach ( ( failure ) => {
554+ const errorMsg = failure . error . toLowerCase ( ) ;
555+ expect ( errorMsg ) . to . match (
556+ / l o c k | a l r e a d y r u n n i n g | c o n c u r r e n t | a c q u i r e / i,
557+ `Instance #${ failure . index } failed with unexpected error: ${ failure . error } `
558+ ) ;
559+ } ) ;
560+
561+ console . log ( `\n✅ Lock mechanism successfully prevented race conditions across ${ instanceCount } concurrent instances!` ) ;
562+
563+ // Cleanup
564+ const cleanupRunner = await FirebaseRunner . getInstance ( { config : stressConfig } ) ;
565+ await cleanupRunner . getHandler ( ) . db . database . ref ( testShift ) . remove ( ) ;
566+ } ) ;
466567 } ) ;
467568
468569 describe ( "Lock Path with Shift Prefix" , ( ) => {
0 commit comments