@@ -55,7 +55,7 @@ export interface RedisServerDocker {
55
55
abstract class DockerBase {
56
56
async spawnRedisServerDocker ( { image, version } : RedisServerDockerConfig , serverArguments : Array < string > , environment ?: string ) : Promise < RedisServerDocker > {
57
57
const port = ( await portIterator . next ( ) ) . value ;
58
- let cmdLine = `docker run --init -d --network host -e PORT= ${ port . toString ( ) } ` ;
58
+ let cmdLine = `docker run --init -d --network host ` ;
59
59
if ( environment !== undefined ) {
60
60
cmdLine += `-e ${ environment } ` ;
61
61
}
@@ -80,7 +80,7 @@ abstract class DockerBase {
80
80
81
81
async dockerRemove ( dockerId : string ) : Promise < void > {
82
82
try {
83
- await this . dockerStop ( dockerId ) ;
83
+ await this . dockerStop ( dockerId ) ; ``
84
84
} catch ( err ) {
85
85
// its ok if stop failed, as we are just going to remove, will just be slower
86
86
console . log ( `dockerStop failed in remove: ${ err } ` ) ;
@@ -180,14 +180,9 @@ export class SentinelFramework extends DockerBase {
180
180
RedisScripts ,
181
181
RespVersions ,
182
182
TypeMapping > > , errors = true ) {
183
- // remove this safeguard
184
- // in order to test the case when
185
- // connecting to sentinel fails
186
-
187
- // if (opts?.sentinelRootNodes !== undefined) {
188
- // throw new Error("cannot specify sentinelRootNodes here");
189
- // }
190
-
183
+ if ( opts ?. sentinelRootNodes !== undefined ) {
184
+ throw new Error ( "cannot specify sentinelRootNodes here" ) ;
185
+ }
191
186
if ( opts ?. name !== undefined ) {
192
187
throw new Error ( "cannot specify sentinel db name here" ) ;
193
188
}
@@ -250,13 +245,13 @@ export class SentinelFramework extends DockerBase {
250
245
}
251
246
252
247
protected async spawnRedisSentinelNodeDocker ( ) {
253
- const imageInfo : RedisServerDockerConfig = this . config . nodeDockerConfig ?? { image : "redislabs/client-libs-test " , version : "8.0-M05-pre " } ;
248
+ const imageInfo : RedisServerDockerConfig = this . config . nodeDockerConfig ?? { image : "redis/redis-stack-server " , version : "latest " } ;
254
249
const serverArguments : Array < string > = this . config . nodeServerArguments ?? [ ] ;
255
250
let environment ;
256
251
if ( this . config . password !== undefined ) {
257
- environment = `REDIS_PASSWORD= ${ this . config . password } ` ;
252
+ environment = `REDIS_ARGS="{port} --requirepass ${ this . config . password } " ` ;
258
253
} else {
259
- environment = undefined ;
254
+ environment = 'REDIS_ARGS="{port}"' ;
260
255
}
261
256
262
257
const docker = await this . spawnRedisServerDocker ( imageInfo , serverArguments , environment ) ;
@@ -281,6 +276,9 @@ export class SentinelFramework extends DockerBase {
281
276
for ( let i = 0 ; i < ( this . config . numberOfNodes ?? 0 ) - 1 ; i ++ ) {
282
277
promises . push (
283
278
this . spawnRedisSentinelNodeDocker ( ) . then ( async node => {
279
+ if ( this . config . password !== undefined ) {
280
+ await node . client . configSet ( { 'masterauth' : this . config . password } )
281
+ }
284
282
await node . client . replicaOf ( '127.0.0.1' , master . docker . port ) ;
285
283
return node ;
286
284
} )
@@ -349,156 +347,34 @@ export class SentinelFramework extends DockerBase {
349
347
) ;
350
348
}
351
349
352
- const sentinels = await Promise . all ( promises ) ;
353
-
354
- await this . validateSentinelConfiguration ( sentinels ) ;
355
-
356
- return sentinels ;
357
- }
358
-
359
- // Add this new method to validate sentinel configuration
360
- private async validateSentinelConfiguration (
361
- sentinels : Awaited < ReturnType < SentinelFramework [ 'spawnRedisSentinelSentinelDocker' ] > > [ ]
362
- ) : Promise < void > {
363
- const maxRetries = 10 ;
364
- const retryDelay = 2000 ;
365
-
366
- // Wait for all sentinels to recognize each other
367
- for ( let retry = 0 ; retry < maxRetries ; retry ++ ) {
368
- try {
369
- let allConfigured = true ;
370
-
371
- // Check that each sentinel recognizes all other sentinels
372
- for ( const sentinel of sentinels ) {
373
- if ( ! sentinel . client . isReady ) {
374
- allConfigured = false ;
375
- break ;
376
- }
377
-
378
- // Check if this sentinel can see the master
379
- const masterInfo = await sentinel . client . sentinel . sentinelMaster ( this . config . sentinelName )
380
- . catch ( ( ) => null ) ;
381
-
382
- if ( ! masterInfo ) {
383
- allConfigured = false ;
384
- break ;
385
- }
386
-
387
- // Check if this sentinel can see other sentinels
388
- const knownSentinels = await sentinel . client . sentinel . sentinelSentinels ( this . config . sentinelName )
389
- . catch ( ( ) => [ ] ) ;
390
-
391
- // Ensure this sentinel knows about all other sentinels (minus itself)
392
- if ( knownSentinels . length < sentinels . length - 1 ) {
393
- allConfigured = false ;
394
- break ;
395
- }
396
- }
397
-
398
- if ( allConfigured ) {
399
- // All sentinels are properly configured
400
- return ;
401
- }
402
-
403
- // Wait before retrying
404
- await setTimeout ( retryDelay ) ;
405
- } catch ( err ) {
406
- // Wait before retrying after an error
407
- await setTimeout ( retryDelay ) ;
408
- }
350
+ return [
351
+ ...await Promise . all ( promises )
352
+ ]
409
353
}
410
-
411
- throw new Error ( 'Sentinel configuration did not propagate correctly within the timeout period' ) ;
412
- }
413
354
414
- async getAllRunning ( ) : Promise < void > {
415
- const MAX_RETRIES = 5 ;
416
- const RETRY_DELAY = 500 ;
417
-
418
- // Fix for Redis nodes
355
+ async getAllRunning ( ) {
419
356
for ( const port of this . getAllNodesPort ( ) ) {
420
- let retries = 0 ;
421
-
422
- while ( await isPortAvailable ( port ) ) {
423
- if ( retries >= MAX_RETRIES ) {
424
- throw new Error ( `Failed to restart Redis node at port ${ port } after ${ MAX_RETRIES } attempts` ) ;
425
- }
426
-
427
- try {
428
- await this . restartNode ( port . toString ( ) ) ;
429
- await setTimeout ( RETRY_DELAY ) ; // Give the node time to start
430
- } catch ( err ) {
431
- console . error ( `Error restarting Redis node at port ${ port } :` , err ) ;
357
+ let first = true ;
358
+ while ( await isPortAvailable ( port ) ) {
359
+ if ( ! first ) {
360
+ console . log ( `problematic restart ${ port } ` ) ;
361
+ await setTimeout ( 500 ) ;
362
+ } else {
363
+ first = false ;
432
364
}
433
-
434
- retries ++ ;
365
+ await this . restartNode ( port . toString ( ) ) ;
435
366
}
436
367
}
437
368
438
- // Fix for Sentinel nodes
439
369
for ( const port of this . getAllSentinelsPort ( ) ) {
440
- let retries = 0 ;
441
-
442
- while ( await isPortAvailable ( port ) ) {
443
- if ( retries >= MAX_RETRIES ) {
444
- throw new Error ( `Failed to restart Sentinel node at port ${ port } after ${ MAX_RETRIES } attempts` ) ;
445
- }
446
-
447
- try {
448
- await this . restartSentinel ( port . toString ( ) ) ;
449
- await setTimeout ( RETRY_DELAY ) ; // Give the sentinel time to start
450
- } catch ( err ) {
451
- console . error ( `Error restarting Sentinel at port ${ port } :` , err ) ;
452
- }
453
-
454
- retries ++ ;
455
- }
456
- }
457
-
458
- // Verify all nodes are actually responsive
459
- await this . verifyNodesResponsive ( ) ;
460
- }
461
-
462
- // Add a method to verify nodes are responsive
463
- private async verifyNodesResponsive ( ) : Promise < void > {
464
- const MAX_ATTEMPTS = 10 ;
465
- const ATTEMPT_DELAY = 2000 ;
466
-
467
- // Check Redis nodes
468
- for ( const nodeInfo of this . #nodeMap. values ( ) ) {
469
- if ( ! nodeInfo . client . isReady ) {
470
- // Try to reconnect client if not ready
471
- for ( let attempt = 0 ; attempt < MAX_ATTEMPTS ; attempt ++ ) {
472
- try {
473
- await nodeInfo . client . connect ( ) ;
474
- await nodeInfo . client . ping ( ) ;
475
- break ;
476
- } catch ( err ) {
477
- if ( attempt === MAX_ATTEMPTS - 1 ) {
478
- throw new Error ( `Node at port ${ nodeInfo . docker . port } is not responsive after ${ MAX_ATTEMPTS } attempts` ) ;
479
- }
480
- await setTimeout ( ATTEMPT_DELAY ) ;
481
- }
482
- }
483
- }
484
- }
485
-
486
- // Check Sentinel nodes
487
- for ( const sentinelInfo of this . #sentinelMap. values ( ) ) {
488
- if ( ! sentinelInfo . client . isReady ) {
489
- // Try to reconnect client if not ready
490
- for ( let attempt = 0 ; attempt < MAX_ATTEMPTS ; attempt ++ ) {
491
- try {
492
- await sentinelInfo . client . connect ( ) ;
493
- await sentinelInfo . client . ping ( ) ;
494
- break ;
495
- } catch ( err ) {
496
- if ( attempt === MAX_ATTEMPTS - 1 ) {
497
- throw new Error ( `Sentinel at port ${ sentinelInfo . docker . port } is not responsive after ${ MAX_ATTEMPTS } attempts` ) ;
498
- }
499
- await setTimeout ( ATTEMPT_DELAY ) ;
500
- }
370
+ let first = true ;
371
+ while ( await isPortAvailable ( port ) ) {
372
+ if ( ! first ) {
373
+ await setTimeout ( 500 ) ;
374
+ } else {
375
+ first = false ;
501
376
}
377
+ await this . restartSentinel ( port . toString ( ) ) ;
502
378
}
503
379
}
504
380
}
@@ -525,6 +401,9 @@ private async validateSentinelConfiguration(
525
401
const masterPort = await this . getMasterPort ( ) ;
526
402
const newNode = await this . spawnRedisSentinelNodeDocker ( ) ;
527
403
404
+ if ( this . config . password !== undefined ) {
405
+ await newNode . client . configSet ( { 'masterauth' : this . config . password } )
406
+ }
528
407
await newNode . client . replicaOf ( '127.0.0.1' , masterPort ) ;
529
408
530
409
this . #nodeList. push ( newNode ) ;
@@ -608,16 +487,8 @@ private async validateSentinelConfiguration(
608
487
if ( node === undefined ) {
609
488
throw new Error ( "unknown node: " + id ) ;
610
489
}
611
-
612
- let masterPort : number | null = null ;
613
- try {
614
- masterPort = await this . getMasterPort ( ) ;
615
- } catch ( err ) {
616
- console . log ( `Could not determine master before restarting node ${ id } : ${ err } ` ) ;
617
- }
618
-
490
+
619
491
await this . dockerStart ( node . docker . dockerId ) ;
620
-
621
492
if ( ! node . client . isOpen ) {
622
493
node . client = await RedisClient . create ( {
623
494
password : this . config . password ,
@@ -626,17 +497,6 @@ private async validateSentinelConfiguration(
626
497
}
627
498
} ) . on ( "error" , ( ) => { } ) . connect ( ) ;
628
499
}
629
-
630
- // Wait for node to be ready
631
- await setTimeout ( 500 ) ;
632
-
633
- if ( masterPort && node . docker . port !== masterPort ) {
634
- try {
635
- await node . client . replicaOf ( '127.0.0.1' , masterPort ) ;
636
- } catch ( err ) {
637
- console . error ( `Failed to reconfigure node ${ id } as replica: ${ err } ` ) ;
638
- }
639
- }
640
500
}
641
501
642
502
async stopSentinel ( id : string ) {
@@ -742,4 +602,4 @@ private async validateSentinelConfiguration(
742
602
}
743
603
}
744
604
}
745
- }
605
+ }
0 commit comments