diff --git a/packages/client/lib/client/linked-list.spec.ts b/packages/client/lib/client/linked-list.spec.ts index c791d21900..8837936405 100644 --- a/packages/client/lib/client/linked-list.spec.ts +++ b/packages/client/lib/client/linked-list.spec.ts @@ -6,7 +6,7 @@ import { import { equal, deepEqual } from "assert/strict"; describe("DoublyLinkedList", () => { - const list = new DoublyLinkedList(); + const list = new DoublyLinkedList(); it("should start empty", () => { equal(list.length, 0); @@ -96,6 +96,38 @@ describe("DoublyLinkedList", () => { } equal(count, 6); }); + + it("should handle remove on empty list", () => { + list.reset(); + const node = list.push(1); + list.remove(node); + equal(list.length, 0); + deepEqual(Array.from(list), []); + list.remove(node); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); + + + it("should safely remove nodes while iterating", () => { + list.reset(); + list.push(1); + list.push(2); + list.push(3); + list.push(4); + list.push(5); + + const visited: number[] = []; + for (const node of list.nodes()) { + visited.push(node.value); + if (node.value % 2 === 0) { + list.remove(node); + } + } + deepEqual(visited, [1, 2, 3, 4, 5]); + equal(list.length, 3); + deepEqual(Array.from(list), [1, 3, 5]); + }); }); describe("SinglyLinkedList", () => { diff --git a/packages/client/lib/client/linked-list.ts b/packages/client/lib/client/linked-list.ts index 461f1d4082..910319268a 100644 --- a/packages/client/lib/client/linked-list.ts +++ b/packages/client/lib/client/linked-list.ts @@ -29,7 +29,7 @@ export class DoublyLinkedList { ++this.#length; if (this.#tail === undefined) { - return this.#tail = this.#head = { + return this.#head = this.#tail = { previous: this.#head, next: undefined, value @@ -73,7 +73,7 @@ export class DoublyLinkedList { --this.#length; const node = this.#head; if (node.next) { - node.next.previous = node.previous; + node.next.previous = undefined; this.#head = node.next; node.next = undefined; } else { @@ -83,19 +83,23 @@ export class DoublyLinkedList { } remove(node: DoublyLinkedNode) { + if (this.#length === 0) return; --this.#length; if (this.#tail === node) { this.#tail = node.previous; - } - + } if (this.#head === node) { this.#head = node.next; } else { - node.previous!.next = node.next; - node.previous = undefined; + if (node.previous) { + node.previous.next = node.next; + } + if (node.next) { + node.next.previous = node.previous; + } } - + node.previous = undefined; node.next = undefined; } @@ -115,8 +119,9 @@ export class DoublyLinkedList { *nodes() { let node = this.#head; while(node) { + const next = node.next yield node; - node = node.next; + node = next; } } } diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index 63c4586293..a9a2b9a5e5 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -928,6 +928,16 @@ class RedisSentinelInternal< } } + #handleSentinelFailure(node: RedisNode) { + const found = this.#sentinelRootNodes.findIndex( + (rootNode) => rootNode.host === node.host && rootNode.port === node.port + ); + if (found !== -1) { + this.#sentinelRootNodes.splice(found, 1); + } + this.#reset(); + } + async close() { this.#destroy = true; @@ -1197,8 +1207,9 @@ class RedisSentinelInternal< error: err }; this.emit('client-error', event); - this.#reset(); - }); + this.#handleSentinelFailure(node); + }) + .on('end', () => this.#handleSentinelFailure(node)); this.#sentinelClient = client; this.#trace(`transform: adding sentinel client connect() to promise list`);