-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Open
Labels
Description
Description
Bug
I'm getting an error (line numbers may be affected because of me modifying the package while trying to investigate the source of an error)
TypeError: Cannot read properties of undefined (reading 'reject')
at Function._RedisCommandsQueue_flushQueue (/Users/mishase/Projects/wp/node_modules/@redis/client/dist/lib/client/commands-queue.js:204:11)
at RedisCommandsQueue.flushAll (/Users/mishase/Projects/wp/node_modules/@redis/client/dist/lib/client/commands-queue.js:195:93)
at RedisClient.disconnect (/Users/mishase/Projects/wp/node_modules/@redis/client/dist/lib/client/index.js:336:63)
at <anonymous> (/Users/mishase/Projects/wp/packages/redis/dist/lib/events/EventsClient.js:86:24)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at EventsClient.blockingReadMany (/Users/mishase/Projects/wp/packages/redis/dist/lib/events/EventsClient.js:55:22)
at Object.handler (/Users/mishase/Projects/wp/apps/screen-api/src/routes/screenshot/status.ts:23:15)
Error happens in flushQueue
function
// RedisCommandsQueue.#flushQueue function
while (queue.length) {
queue.shift()!.reject(err); // This line
}
Because of queue.length
equals -1
. It goes bellow zero due to bug in yallist
package, developers of which seems to be dead in mid-2019, so submitting issue there will never be resolved
Detailed description of yallist
bug
Let's look at the shift()
function
Yallist.prototype.shift = function () {
if (!this.head) {
return undefined;
}
var res = this.head.value;
this.head = this.head.next;
if (this.head) {
this.head.prev = null;
} else {
this.tail = null;
}
this.length--;
return res;
};
It removes item from the list and returns Node
of it
Now let's examine the removeNode(node)
function
Yallist.prototype.removeNode = function (node) {
if (node.list !== this) {
throw new Error("removing node which does not belong to this list");
}
var next = node.next;
var prev = node.prev;
if (next) {
next.prev = prev;
}
if (prev) {
prev.next = next;
}
if (node === this.head) {
this.head = next;
}
if (node === this.tail) {
this.tail = prev;
}
node.list.length--;
node.next = null;
node.prev = null;
node.list = null;
return next;
};
As you can see, there is a node.list !== this
check in the beginning of the function and node.list = null
in the end of this function which prevents it from being called twice, but mentioned earlier function shift()
does not have list = null
line. So we can call shift
and then removeNode
which will break node.list.length
value. In our case, we're calling shift()
while there is one item and then removeNode
call results list.length
to be -1
Code to reproduce this bug
import LinkedList from "yallist";
const ll = new LinkedList();
ll.push({ name: "a" });
const node = ll.head;
ll.shift();
console.log(ll.length); // 0
ll.removeNode(node);
console.log(ll.length); // -1
What happens at the event of AbortSignal triggered in redis
command when passing it to the command options
In mentioned earlier flushQueue
function it calls queue.shift
function which removes node
from the waitingToBeSent
list
In addCommand
function there is a call this.#waitingToBeSent.removeNode
to removeNode
function which I described earlier. This causes waitingToBeSent.length
to be -1
return new Promise((resolve, reject) => {
const node = new LinkedList.Node<CommandWaitingToBeSent>({
args,
chainId: options?.chainId,
returnBuffers: options?.returnBuffers,
resolve,
reject,
});
if (options?.signal) {
const listener = () => {
// Remove already removed from list node and cause length decrement
this.#waitingToBeSent.removeNode(node);
node.value.reject(new AbortError());
};
node.value.abort = {
signal: options.signal,
listener,
};
// AbortSignal type is incorrent
(options.signal as any).addEventListener("abort", listener, {
once: true,
});
}
if (options?.asap) {
this.#waitingToBeSent.unshiftNode(node);
} else {
this.#waitingToBeSent.pushNode(node);
}
});
Possible fixes
Check if node exists before calling removeNode
function in Redis library
Fork yallist
and add node.list = null
in all necessary functions or implement different presence checks in the removeNode
function
Node.js Version
v18.14.0
Redis Server Version
all versions affected
Node Redis Version
4.6.6
Platform
all platforms affected
Logs
No response