@@ -6,6 +6,7 @@ import { RedisArgument } from "../..";
6
6
import { isIP } from "net" ;
7
7
import { lookup } from "dns/promises" ;
8
8
import assert from "node:assert" ;
9
+ import { setTimeout } from 'node:timers/promises'
9
10
10
11
export const MAINTENANCE_EVENTS = {
11
12
PAUSE_WRITING : "pause-writing" ,
@@ -26,13 +27,6 @@ export interface SocketTimeoutUpdate {
26
27
timeout ?: number ;
27
28
}
28
29
29
- const DEFAULT_OPTIONS = {
30
- maintPushNotifications : "auto" ,
31
- maintMovingEndpointType : "auto" ,
32
- maintRelaxedCommandTimeout : 1000 ,
33
- maintRelaxedSocketTimeout : 1000 ,
34
- } as const ;
35
-
36
30
export const dbgMaintenance = ( ...args : any [ ] ) => {
37
31
if ( ! process . env . DEBUG_MAINTENANCE ) return ;
38
32
return console . log ( "[MNT]" , ...args ) ;
@@ -43,6 +37,22 @@ export default class EnterpriseMaintenanceManager extends EventEmitter {
43
37
#options: RedisClientOptions ;
44
38
#isMaintenance = 0 ;
45
39
40
+ static setupDefaultMaintOptions ( options : RedisClientOptions ) {
41
+ if ( options . maintPushNotifications === undefined ) {
42
+ options . maintPushNotifications =
43
+ options ?. RESP === 3 ? "auto" : "disabled" ;
44
+ }
45
+ if ( options . maintMovingEndpointType === undefined ) {
46
+ options . maintMovingEndpointType = "auto" ;
47
+ }
48
+ if ( options . maintRelaxedSocketTimeout === undefined ) {
49
+ options . maintRelaxedSocketTimeout = 10000 ;
50
+ }
51
+ if ( options . maintRelaxedCommandTimeout === undefined ) {
52
+ options . maintRelaxedCommandTimeout = 10000 ;
53
+ }
54
+ }
55
+
46
56
static async getHandshakeCommand (
47
57
tls : boolean ,
48
58
host : string ,
@@ -63,8 +73,8 @@ export default class EnterpriseMaintenanceManager extends EventEmitter {
63
73
movingEndpointType ,
64
74
] ,
65
75
errorHandler : ( error : Error ) => {
66
- dbgMaintenance ( ' handshake failed:' , error ) ;
67
- if ( options . maintPushNotifications === ' enabled' ) {
76
+ dbgMaintenance ( " handshake failed:" , error ) ;
77
+ if ( options . maintPushNotifications === " enabled" ) {
68
78
throw error ;
69
79
}
70
80
} ,
@@ -74,22 +84,21 @@ export default class EnterpriseMaintenanceManager extends EventEmitter {
74
84
constructor ( commandsQueue : RedisCommandsQueue , options : RedisClientOptions ) {
75
85
super ( ) ;
76
86
this . #commandsQueue = commandsQueue ;
77
- this . #options = { ... DEFAULT_OPTIONS , ... options } ;
87
+ this . #options = options ;
78
88
79
89
this . #commandsQueue. addPushHandler ( this . #onPush) ;
80
90
}
81
91
82
92
#onPush = ( push : Array < any > ) : boolean => {
83
- dbgMaintenance ( push . map ( ( item ) => item . toString ( ) ) ) ;
93
+ dbgMaintenance ( push ) ;
84
94
switch ( push [ 0 ] . toString ( ) ) {
85
95
case PN . MOVING : {
86
96
// [ 'MOVING', '17', '15', '54.78.247.156:12075' ]
87
97
// ^seq ^after ^new ip
88
- const afterMs = push [ 2 ] ;
89
- const url = push [ 3 ] ;
90
- const [ host , port ] = url . toString ( ) . split ( ":" ) ;
91
- dbgMaintenance ( "Received MOVING:" , afterMs , host , Number ( port ) ) ;
92
- this . #onMoving( afterMs , host , Number ( port ) ) ;
98
+ const afterSeconds = push [ 2 ] ;
99
+ const url : string | null = push [ 3 ] ;
100
+ dbgMaintenance ( "Received MOVING:" , afterSeconds , url ) ;
101
+ this . #onMoving( afterSeconds , url ) ;
93
102
return true ;
94
103
}
95
104
case PN . MIGRATING :
@@ -121,13 +130,37 @@ export default class EnterpriseMaintenanceManager extends EventEmitter {
121
130
// 5. [ACTION] Destroy old socket
122
131
// 6. [ACTION] Resume writing -> we are going to write to the new socket from now on
123
132
#onMoving = async (
124
- _afterMs : number ,
125
- host : string ,
126
- port : number ,
133
+ afterSeconds : number ,
134
+ url : string | null
127
135
) : Promise < void > => {
128
136
// 1 [EVENT] MOVING PN received
129
137
this . #onMigrating( ) ;
130
138
139
+ let host : string
140
+ let port : number
141
+
142
+ // The special value `none` indicates that the `MOVING` message doesn’t need
143
+ // to contain an endpoint. Instead it contains the value `null` then. In
144
+ // such a corner case, the client is expected to schedule a graceful
145
+ // reconnect to its currently configured endpoint after half of the grace
146
+ // period that was communicated by the server is over.
147
+ if ( url === null ) {
148
+ assert ( this . #options. maintMovingEndpointType === 'none' ) ;
149
+ assert ( this . #options. socket !== undefined )
150
+ assert ( 'host' in this . #options. socket )
151
+ assert ( typeof this . #options. socket . host === 'string' )
152
+ host = this . #options. socket . host
153
+ assert ( typeof this . #options. socket . port === 'number' )
154
+ port = this . #options. socket . port
155
+ const waitTime = afterSeconds * 1000 / 2 ;
156
+ dbgMaintenance ( `Wait for ${ waitTime } ms` ) ;
157
+ await setTimeout ( waitTime ) ;
158
+ } else {
159
+ const split = url . split ( ':' ) ;
160
+ host = split [ 0 ] ;
161
+ port = Number ( split [ 1 ] ) ;
162
+ }
163
+
131
164
// 2 [ACTION] Pause writing
132
165
dbgMaintenance ( "Pausing writing of new commands to old socket" ) ;
133
166
this . emit ( MAINTENANCE_EVENTS . PAUSE_WRITING ) ;
@@ -225,11 +258,13 @@ function isPrivateIP(ip: string): boolean {
225
258
async function determineEndpoint (
226
259
tlsEnabled : boolean ,
227
260
host : string ,
228
- options : RedisClientOptions
261
+ options : RedisClientOptions ,
229
262
) : Promise < MovingEndpointType > {
230
-
231
263
assert ( options . maintMovingEndpointType !== undefined ) ;
232
- if ( options . maintMovingEndpointType !== 'auto' ) return options . maintMovingEndpointType ;
264
+ if ( options . maintMovingEndpointType !== "auto" ) {
265
+ dbgMaintenance ( `Determine endpoint type: ${ options . maintMovingEndpointType } ` ) ;
266
+ return options . maintMovingEndpointType ;
267
+ }
233
268
234
269
const ip = isIP ( host ) ? host : ( await lookup ( host , { family : 0 } ) ) . address ;
235
270
@@ -242,6 +277,6 @@ async function determineEndpoint(
242
277
result = isPrivate ? "internal-ip" : "external-ip" ;
243
278
}
244
279
245
- dbgMaintenance ( `Determine endpoint format : ${ result } ` ) ;
280
+ dbgMaintenance ( `Determine endpoint type : ${ result } ` ) ;
246
281
return result ;
247
282
}
0 commit comments