From fb5dca5c6e2cc84b3b112820f87352a6d26ae65e Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Thu, 29 Jan 2026 11:05:03 +0300 Subject: [PATCH 1/3] Update --- Makefile | 14 ++++++++++++++ README.md | 2 +- package.json | 10 +++++----- 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..5b26bd85a --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: build lint test test-all + +build: + npm run compile + +lint: + npm lint + +test: build lint + npm test + +test-all: build lint + env HAZELCAST_ENTERPRISE_KEY=1 npm test + diff --git a/README.md b/README.md index a08eec8c7..a7e4652fa 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,6 @@ This command will only run the tests matching the pattern. The pattern can be a ## Copyright -Copyright (c) 2008-2022, Hazelcast, Inc. All Rights Reserved. +Copyright (c) 2008-2026, Hazelcast, Inc. All Rights Reserved. Visit [www.hazelcast.com](http://www.hazelcast.com) for more information. diff --git a/package.json b/package.json index 337ef844e..daa7ddf43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hazelcast-client", - "version": "5.3.0", + "version": "5.6.0", "description": "Hazelcast - a real-time stream processing platform - Node.js Client", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -9,7 +9,7 @@ "lib/**/*.d.ts" ], "dependencies": { - "@types/long": "4.0.0", + "@types/long": "4.0.2", "long": "4.0.0" }, "devDependencies": { @@ -23,9 +23,9 @@ "eslint-plugin-mocha": "~9.0.0", "husky": "~6.0.0", "jsonschema": "~1.5.0", - "markdown-link-check": "~3.13.7", + "markdown-link-check": "^3.14.2", "markdownlint-cli": "~0.32.2", - "mocha": "~9.2.2", + "mocha": "^11.7.5", "mousse": "~0.3.1", "nyc": "~15.1.0", "path-exists-cli": "~2.0.0", @@ -41,7 +41,7 @@ "yargs": "~17.5.1" }, "engines": { - "node": ">=10.4.0" + "node": ">=20.20.0" }, "scripts": { "clean": "rimraf lib *.jar *.log *.xml coverage", From b537a924ea909b920b9d43b834283729cf5aa468 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Fri, 30 Jan 2026 09:32:02 +0300 Subject: [PATCH 2/3] Trivial fixes --- src/invocation/InvocationService.ts | 5 +++-- src/network/ConnectionManager.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/invocation/InvocationService.ts b/src/invocation/InvocationService.ts index 3c6c25e6e..d8f4fd432 100644 --- a/src/invocation/InvocationService.ts +++ b/src/invocation/InvocationService.ts @@ -684,6 +684,7 @@ export class InvocationService { + invocation.request.getCorrelationId() + ') reached its deadline.', error)); return true; } + return false; } private registerInvocation(invocation: Invocation): void { @@ -703,7 +704,7 @@ export class InvocationService { deregisterInvocation(correlationId: number): void { this.pending.delete(correlationId); } - + /** * Returns `true` if we need to check the urgent invocations, by * examining the local registry of the schema service. @@ -743,7 +744,7 @@ export class InvocationService { + 'data and it is not safe to invoke it when the client is not ' + 'yet initialized on the cluster'); } - + return null; } } diff --git a/src/network/ConnectionManager.ts b/src/network/ConnectionManager.ts index 922c5d050..2198907da 100644 --- a/src/network/ConnectionManager.ts +++ b/src/network/ConnectionManager.ts @@ -665,11 +665,11 @@ export class ConnectionManager extends EventEmitter implements MembershipListene }, this.connectionTimeoutMillis); socket.once('secureConnect', () => { - clearInterval(connectTimeoutTimer); + clearTimeout(connectTimeoutTimer); connectionResolver.resolve(socket); }); socket.once('error', (err) => { - clearInterval(connectTimeoutTimer); + clearTimeout(connectTimeoutTimer); connectionResolver.reject(err); }); From f093fdb8542c3bc4ca3c14fb99b5fd282ceb0eac Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Fri, 30 Jan 2026 10:37:42 +0300 Subject: [PATCH 3/3] Fixed leak --- Makefile | 2 +- src/network/Connection.ts | 43 +++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 5b26bd85a..bc27a34b3 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ build: npm run compile lint: - npm lint + npm run lint test: build lint npm test diff --git a/src/network/Connection.ts b/src/network/Connection.ts index 96109bfaa..da2940aa7 100644 --- a/src/network/Connection.ts +++ b/src/network/Connection.ts @@ -62,6 +62,7 @@ export class PipelinedWriter extends Writer { private canWrite = true; // reusable buffer for coalescing private readonly coalesceBuf: Buffer; + private readonly drainListener: () => void; constructor( private readonly socket: net.Socket, @@ -72,11 +73,13 @@ export class PipelinedWriter extends Writer { super(); this.coalesceBuf = Buffer.allocUnsafe(threshold); - // write queued items on drain event - socket.on('drain', () => { + this.drainListener = () => { this.canWrite = true; this.schedule(); - }); + } + + // write queued items on drain event + socket.on('drain', this.drainListener); } write(message: ClientMessage, resolver: DeferredPromise): void { @@ -94,6 +97,7 @@ export class PipelinedWriter extends Writer { } this.error = this.makeIOError(error); this.canWrite = false; + this.socket.off('drain', this.drainListener); // If we pass an error to destroy, an unhandled error will be thrown because we don't handle the error event // So we don't pass anything to the socket. It is internal anyway. this.socket.destroy(); @@ -214,6 +218,8 @@ export class DirectWriter extends Writer { } close(cause: Error): void { + // Remove all listeners from this emitter + this.removeAllListeners('write'); this.socket.destroy(); } } @@ -351,6 +357,8 @@ export class Connection { private readonly writer: Writer; private readonly reader: ClientMessageReader; private readonly fragmentedMessageHandler: FragmentedClientMessageHandler; + private dataListener: ((buffer: Buffer) => void) | null = null; + private writeListener: (() => void) | null = null; constructor( private readonly connectionManager: ConnectionManager, @@ -374,9 +382,10 @@ export class Connection { this.connectedServerVersion = BuildInfo.UNKNOWN_VERSION_ID; this.writer = enablePipelining ? new PipelinedWriter(this.socket, pipeliningThreshold, this.incrementBytesWrittenFn) : new DirectWriter(this.socket, this.incrementBytesWrittenFn); - this.writer.on('write', () => { + this.writeListener = () => { this.lastWriteTimeMillis = Date.now(); - }); + } + this.writer.on('write', this.writeListener); this.reader = new ClientMessageReader(); this.fragmentedMessageHandler = new FragmentedClientMessageHandler(this.logger); } @@ -437,11 +446,28 @@ export class Connection { this.logClose(); + // Remove socket listeners before closing + this.removeListeners(); + this.writer.close(this.closedCause ? this.closedCause : new Error(reason ? reason : 'Connection closed')); this.connectionManager.onConnectionClose(this); } + /** + * Removes all registered listeners from the socket and writer. + */ + private removeListeners(): void { + if (this.dataListener !== null) { + this.socket.off('data', this.dataListener); + this.dataListener = null; + } + if (this.writeListener !== null) { + this.writer.off('write', this.writeListener); + this.writeListener = null; + } + } + isAlive(): boolean { return this.closedTime === 0; } @@ -483,7 +509,7 @@ export class Connection { * @param callback */ registerResponseCallback(callback: ClientMessageHandler): void { - this.socket.on('data', (buffer: Buffer) => { + this.dataListener = (buffer: Buffer) => { this.lastReadTimeMillis = Date.now(); this.reader.append(buffer); let clientMessage = this.reader.read(); @@ -496,14 +522,15 @@ export class Connection { clientMessage = this.reader.read(); } this.incrementBytesReadFn(buffer.length); - }); + } + this.socket.on('data', this.dataListener); } setClusterUuid(uuid: UUID): void { this.clusterUuid = uuid; } - getClusterUuid(): UUID { + getClusterUuid(): UUID { return this.clusterUuid; }