feat(SmartsocketClient): Improve client reconnection logic with exponential backoff and jitter; update socket.io and @types/node dependencies
This commit is contained in:
		
							
								
								
									
										103
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | # Changelog | ||||||
|  |  | ||||||
|  | ## 2025-03-10 - 2.1.0 - feat(SmartsocketClient) | ||||||
|  | Improve client reconnection logic with exponential backoff and jitter; update socket.io and @types/node dependencies | ||||||
|  |  | ||||||
|  | - Bump engine.io from 6.5.4 to 6.6.4, socket.io and socket.io-client from 4.7.5 to 4.8.1 | ||||||
|  | - Bump @types/node from ^20.12.7 to ^22.13.10 | ||||||
|  | - Add new optional reconnection parameters (maxRetries, initialBackoffDelay, maxBackoffDelay) to SmartsocketClient options | ||||||
|  | - Implement exponential backoff with jitter for auto-reconnect and reset reconnection state on successful connection | ||||||
|  |  | ||||||
|  | ## 2024-05-29 - 2.0.27 - docs | ||||||
|  | update description | ||||||
|  |  | ||||||
|  | ## 2024-04-26 to 2024-03-30 - 2.0.26 … 2.0.24 - core & configuration | ||||||
|  | A series of internal fixes and configuration tweaks. | ||||||
|  | - fix(core): update | ||||||
|  | - update tsconfig | ||||||
|  | - update npmextra.json: githost | ||||||
|  |  | ||||||
|  | ## 2023-09-10 to 2023-07-21 - 2.0.23 … 2.0.20 - core | ||||||
|  | Multiple minor core fixes were applied in rapid succession. | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2023-07-21 to 2023-03-20 - 2.0.19 … 2.0.15 - core | ||||||
|  | Routine internal updates addressing core functionality. | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2023-02-07 to 2022-03-24 - 2.0.14 … 2.0.0 - core | ||||||
|  | Further minor core updates were rolled out over several versions. | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2022-03-14 - 1.2.22 - esm | ||||||
|  | A breaking change was introduced to switch the module system. | ||||||
|  | - BREAKING CHANGE(switch to esm): update | ||||||
|  |  | ||||||
|  | ## 2022-01-20 to 2021-01-23 - 1.2.21 … 1.2.0 - core | ||||||
|  | A range of minor core fixes. | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-12-26 - 1.1.71 - SmartsocketClient | ||||||
|  | New functionality in the socket client was added. | ||||||
|  | - feat(SmartsocketClient): socket client can now be stopped with .stop() in addition to .reconnect() | ||||||
|  |  | ||||||
|  | ## 2020-12-26 to 2020-09-24 - 1.1.70 … 1.1.58 - core & test | ||||||
|  | A group of updates addressing both core mechanics and tests. | ||||||
|  | - fix(core): update | ||||||
|  | - fix(test): use @pushrocks/isohash instead of @pushrocks/smarthash | ||||||
|  |  | ||||||
|  | ## 2019-11-08 to 2019-04-23 - 1.1.57 … 1.1.27 - core | ||||||
|  | Numerous versions in this period included only internal core fixes. | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-01-31 to 2019-01-30 - 1.1.26 … 1.1.19 - build, docs & configuration | ||||||
|  | Updates went beyond the core, affecting build tooling and package metadata. | ||||||
|  | - fix(build): now building with tsbuild | ||||||
|  | - fix(readme): update | ||||||
|  | - fix(npmextra): adjust access level | ||||||
|  | - fix(scope): switch to @pushrocks | ||||||
|  | - fix(package.json): private setting | ||||||
|  | - fix(snyk): add .snyk file | ||||||
|  | - fix(structure): update to latest standards | ||||||
|  |  | ||||||
|  | ## 2018-03-19 to 2018-03-15 - 1.1.18 … 1.1.12 - core & docs | ||||||
|  | Several improvements touching both functionality and documentation. | ||||||
|  | - now working as expected | ||||||
|  | - start transitioning to better SocketFunction handling | ||||||
|  | - add @types/node | ||||||
|  | - format and update README | ||||||
|  | - update to latest standards | ||||||
|  |  | ||||||
|  | ## 2017-10-09 to 2017-07-07 - 1.1.11 … 1.1.07 - core & docs | ||||||
|  | Updates in this range improved both the internal mechanics and the developer‐facing materials. | ||||||
|  | - allow setting of specific server | ||||||
|  | - fix not ending error correctly | ||||||
|  | - update to newest version | ||||||
|  | - update docs and tests | ||||||
|  | - remove taskbuffer | ||||||
|  | - update to latest standards | ||||||
|  |  | ||||||
|  | ## 2016-09-25 to 2016-09-03 - 1.1.6 … 1.1.3 - docs & core | ||||||
|  | Minor improvements in documentation and code quality. | ||||||
|  | - improve README | ||||||
|  | - added docs | ||||||
|  | - fix scoping of socket roles and perform small syntax fixes | ||||||
|  |  | ||||||
|  | ## 2016-09-02 to 2016-08-16 - 1.1.2 … 1.1.1 - dependencies & security | ||||||
|  | Several housekeeping tasks to update dependencies and improve security. | ||||||
|  | - updated dependencies and exported socketConnection | ||||||
|  | - now authenticating sockets by checking the password hash | ||||||
|  |  | ||||||
|  | ## 2016-08-15 - 1.1.0 - docs | ||||||
|  | A documentation update was published. | ||||||
|  | - update README | ||||||
|  |  | ||||||
|  | ## 2016-08-15 - 1.0.7 - networking | ||||||
|  | A key update made the socket client work bi-directionally, enabling mesh setups. | ||||||
|  | - now working in both directions so mesh setups work | ||||||
|  |  | ||||||
|  | ## 2016-08-14 to 2016-08-07 - 1.0.6 … 1.0.0 - internal changes | ||||||
|  | From the initial release onward, several internal improvements were introduced: | ||||||
|  | - updated tests and structure | ||||||
|  | - reworked reconnection logic and added a request/response abstraction for transparent function calls | ||||||
|  | - initial release features with updated documentation and structure | ||||||
| @@ -33,9 +33,9 @@ | |||||||
|     "@push.rocks/smartpromise": "^4.0.3", |     "@push.rocks/smartpromise": "^4.0.3", | ||||||
|     "@push.rocks/smartrx": "^3.0.7", |     "@push.rocks/smartrx": "^3.0.7", | ||||||
|     "@push.rocks/smarttime": "^4.0.6", |     "@push.rocks/smarttime": "^4.0.6", | ||||||
|     "engine.io": "6.5.4", |     "engine.io": "6.6.4", | ||||||
|     "socket.io": "4.7.5", |     "socket.io": "4.8.1", | ||||||
|     "socket.io-client": "4.7.5" |     "socket.io-client": "4.8.1" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@git.zone/tsbuild": "^2.1.66", |     "@git.zone/tsbuild": "^2.1.66", | ||||||
| @@ -43,7 +43,7 @@ | |||||||
|     "@git.zone/tsrun": "^1.2.44", |     "@git.zone/tsrun": "^1.2.44", | ||||||
|     "@git.zone/tstest": "^1.0.77", |     "@git.zone/tstest": "^1.0.77", | ||||||
|     "@push.rocks/tapbundle": "^5.0.23", |     "@push.rocks/tapbundle": "^5.0.23", | ||||||
|     "@types/node": "^20.12.7" |     "@types/node": "^22.13.10" | ||||||
|   }, |   }, | ||||||
|   "private": false, |   "private": false, | ||||||
|   "files": [ |   "files": [ | ||||||
|   | |||||||
							
								
								
									
										11162
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11162
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,8 +1,8 @@ | |||||||
| /** | /** | ||||||
|  * autocreated commitinfo by @pushrocks/commitinfo |  * autocreated commitinfo by @push.rocks/commitinfo | ||||||
|  */ |  */ | ||||||
| export const commitinfo = { | export const commitinfo = { | ||||||
|   name: '@push.rocks/smartsocket', |   name: '@push.rocks/smartsocket', | ||||||
|   version: '2.0.27', |   version: '2.1.0', | ||||||
|   description: 'Provides easy and secure websocket communication mechanisms, including server and client implementation, function call routing, connection management, and tagging.' |   description: 'Provides easy and secure websocket communication mechanisms, including server and client implementation, function call routing, connection management, and tagging.' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,6 +18,9 @@ export interface ISmartsocketClientOptions { | |||||||
|   url: string; |   url: string; | ||||||
|   alias: string; // an alias makes it easier to identify this client in a multo client environment |   alias: string; // an alias makes it easier to identify this client in a multo client environment | ||||||
|   autoReconnect?: boolean; |   autoReconnect?: boolean; | ||||||
|  |   maxRetries?: number; // maximum number of reconnection attempts | ||||||
|  |   initialBackoffDelay?: number; // initial backoff delay in ms | ||||||
|  |   maxBackoffDelay?: number; // maximum backoff delay in ms | ||||||
| } | } | ||||||
|  |  | ||||||
| export class SmartsocketClient { | export class SmartsocketClient { | ||||||
| @@ -32,6 +35,11 @@ export class SmartsocketClient { | |||||||
|   public serverUrl: string; |   public serverUrl: string; | ||||||
|   public serverPort: number; |   public serverPort: number; | ||||||
|   public autoReconnect: boolean; |   public autoReconnect: boolean; | ||||||
|  |   public maxRetries: number; | ||||||
|  |   public initialBackoffDelay: number; | ||||||
|  |   public maxBackoffDelay: number; | ||||||
|  |   public currentRetryCount = 0; | ||||||
|  |   public currentBackoffDelay: number; | ||||||
|  |  | ||||||
|   // status handling |   // status handling | ||||||
|   public eventSubject = new plugins.smartrx.rxjs.Subject<interfaces.TConnectionStatus>(); |   public eventSubject = new plugins.smartrx.rxjs.Subject<interfaces.TConnectionStatus>(); | ||||||
| @@ -79,6 +87,10 @@ export class SmartsocketClient { | |||||||
|     this.serverUrl = optionsArg.url; |     this.serverUrl = optionsArg.url; | ||||||
|     this.serverPort = optionsArg.port; |     this.serverPort = optionsArg.port; | ||||||
|     this.autoReconnect = optionsArg.autoReconnect; |     this.autoReconnect = optionsArg.autoReconnect; | ||||||
|  |     this.maxRetries = optionsArg.maxRetries ?? 100; // Default to 100 retries | ||||||
|  |     this.initialBackoffDelay = optionsArg.initialBackoffDelay ?? 1000; // Default to 1 second | ||||||
|  |     this.maxBackoffDelay = optionsArg.maxBackoffDelay ?? 60000; // Default to 1 minute | ||||||
|  |     this.currentBackoffDelay = this.initialBackoffDelay; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public addSocketFunction(socketFunction: SocketFunction<any>) { |   public addSocketFunction(socketFunction: SocketFunction<any>) { | ||||||
| @@ -89,6 +101,10 @@ export class SmartsocketClient { | |||||||
|    * connect the client to the server |    * connect the client to the server | ||||||
|    */ |    */ | ||||||
|   public async connect() { |   public async connect() { | ||||||
|  |     // Reset retry counters on new connection attempt | ||||||
|  |     this.currentRetryCount = 0; | ||||||
|  |     this.currentBackoffDelay = this.initialBackoffDelay; | ||||||
|  |      | ||||||
|     const done = plugins.smartpromise.defer(); |     const done = plugins.smartpromise.defer(); | ||||||
|     const smartenvInstance = new plugins.smartenv.Smartenv(); |     const smartenvInstance = new plugins.smartenv.Smartenv(); | ||||||
|     const socketIoClient: any = await smartenvInstance.getEnvAwareModule({ |     const socketIoClient: any = await smartenvInstance.getEnvAwareModule({ | ||||||
| @@ -216,8 +232,27 @@ export class SmartsocketClient { | |||||||
|  |  | ||||||
|     if (this.autoReconnect && useAutoReconnectSetting && this.eventStatus !== 'connecting') { |     if (this.autoReconnect && useAutoReconnectSetting && this.eventStatus !== 'connecting') { | ||||||
|       this.updateStatus('connecting'); |       this.updateStatus('connecting'); | ||||||
|       console.log('debounced reconnect!'); |        | ||||||
|       await plugins.smartdelay.delayForRandom(10000, 20000); |       // Check if we've exceeded the maximum number of retries | ||||||
|  |       if (this.currentRetryCount >= this.maxRetries) { | ||||||
|  |         logger.log('warn', `Maximum reconnection attempts (${this.maxRetries}) reached. Giving up.`); | ||||||
|  |         this.disconnectRunning = false; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       // Increment retry counter | ||||||
|  |       this.currentRetryCount++; | ||||||
|  |        | ||||||
|  |       // Calculate backoff with jitter (±20% randomness) | ||||||
|  |       const jitter = this.currentBackoffDelay * 0.2 * (Math.random() * 2 - 1); | ||||||
|  |       const delay = Math.min(this.currentBackoffDelay + jitter, this.maxBackoffDelay); | ||||||
|  |        | ||||||
|  |       logger.log('info', `Reconnect attempt ${this.currentRetryCount}/${this.maxRetries} in ${Math.round(delay)}ms`); | ||||||
|  |        | ||||||
|  |       // Apply exponential backoff for next time (doubling with each attempt) | ||||||
|  |       this.currentBackoffDelay = Math.min(this.currentBackoffDelay * 2, this.maxBackoffDelay); | ||||||
|  |        | ||||||
|  |       await plugins.smartdelay.delayFor(delay); | ||||||
|       this.disconnectRunning = false; |       this.disconnectRunning = false; | ||||||
|       await this.connect(); |       await this.connect(); | ||||||
|     } else { |     } else { | ||||||
| @@ -230,6 +265,8 @@ export class SmartsocketClient { | |||||||
|    */ |    */ | ||||||
|   public async stop() { |   public async stop() { | ||||||
|     this.autoReconnect = false; |     this.autoReconnect = false; | ||||||
|  |     this.currentRetryCount = 0; | ||||||
|  |     this.currentBackoffDelay = this.initialBackoffDelay; | ||||||
|     await this.disconnect(); |     await this.disconnect(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -262,5 +299,19 @@ export class SmartsocketClient { | |||||||
|       this.eventSubject.next(statusArg); |       this.eventSubject.next(statusArg); | ||||||
|     } |     } | ||||||
|     this.eventStatus = statusArg; |     this.eventStatus = statusArg; | ||||||
|  |      | ||||||
|  |     // Reset reconnection state when connection is successful | ||||||
|  |     if (statusArg === 'connected') { | ||||||
|  |       this.currentRetryCount = 0; | ||||||
|  |       this.currentBackoffDelay = this.initialBackoffDelay; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Resets the reconnection state | ||||||
|  |    */ | ||||||
|  |   public resetReconnectionState() { | ||||||
|  |     this.currentRetryCount = 0; | ||||||
|  |     this.currentBackoffDelay = this.initialBackoffDelay; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user