Skip to content

RESP2 parser error with large, multi-chunk responses #2419

Closed
@fast-facts

Description

@fast-facts

Description

I'm running an FT.Search command that returns 5000 items (using a limit argument). Upon debugging, I noticed the data usually comes in through 8-10 chunks. 95% of the time, the command just hangs. I have to reduce the limit argument to about 1000 for the command to succeed regularly. It seems to be there's some race condition with a larger number of chunks.

I confirmed the issue is not with the redis server or with the network as I was running it directly from the redis-cli command without any issues.

After much debugging, I boiled down the issue to the write function in the RESP2 decoder.

After adding various logging, I noticed that you can guarantee the function finishes, at least for me, if you slow down the parseBulkString function, but obviously the function takes way longer to finish. I added a console.trace on line 178 to achieve this. It is possible that I could just slow down some other parts of the file as well to achieve the same result. It might not be anything special about that function.

I'm unsure of the nuances of it all to be able to resolve this on my own, but here's the information that I've gathered, mostly through inserting various logging. I'll keep working on it and providing feedback that might be helpful.

I modified the function as such to add logging

    write(chunk) {
        console.log('start:', this.cursor, chunk.length) // <---------- added this line
        while (this.cursor < chunk.length) {
            if (!this.type) {
                this.currentStringComposer = this.options.returnStringsAsBuffers() ?
                    this.bufferComposer :
                    this.stringComposer;
                this.type = chunk[this.cursor];
                if (++this.cursor >= chunk.length) {
                    break;
                }
            }
            const reply = this.parseType(chunk, this.type);
            if (reply start: undefined) {
                break;
            }
            this.type = undefined;
            this.options.onReply(reply);
        }
        this.cursor -= chunk.length;
        console.log('end:', this.cursor, chunk.length) // <---------- added this line
    }

Here's a sample of the log it produces during a successful run

start: 0 1460
end: 0 1460
start: 0 13140
end: 2 13140
start: 2 2920
end: 0 2920
start: 0 1460
end: 0 1460
start: 0 24820
end: 0 24820
start: 0 4380
end: 1 4380
start: 1 1460
end: 0 1460
start: 0 10220
end: 0 10220
start: 0 24795
end: 0 24795

Here's a sample of a hanging run (notice the negative numbers)

start: 0 1460
end: 0 1460
start: 0 13140
end: 2 13140
start: 2 1460
end: 0 1460
start: 0 7300
end: -7298 7300
start: -7298 20440
end: -27737 20440
start: -27737 1460
end: -29196 1460
start: -29196 14600
end: -43795 14600
start: -43795 24795
end: -68589 24795

Node.js Version

18.12.1

Redis Server Version

7.0.8

Node Redis Version

4.6.4

Platform

Windows and Linux

Logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions