Closed prettydiff closed 1 year ago
The prior solution was super simple, but it has a fatal flaw evident only in perf testing. Incoming messages are about 10x slower to process than outgoing messages. The prior solution just concatenated overflow into a single buffer that is sliced from the front as dictated by lengths specified in the frame headers. That results in a buffer that eventually generates a stack overflow because it grows to fast to process.
The new solution instead uses a buffer array, which is extremely complex. This new approach works for perf testing and in my local testing, but it might be error prone in ways I am not aware of.
Experimental solution:
payload:Buffer = (function terminal_server_transmission_transmitWs_listener_processor_payload():Buffer {
if (frame === null) {
return null;
}
const dataLength:number = data.length;
let frameSize:number = frame.extended + frame.startByte,
index:number = 0,
size:number = 0,
frameLength:number = socket.frame.length,
complete:Buffer = null;
// when the data exactly matches the size of payload and frame header
if (dataLength === frameSize) {
if (buf === null || buf === undefined) {
socket.frame[0] = socket.frame[0].slice(frameSize);
}
return unmask(data.slice(frame.startByte));
}
if (buf !== null && buf !== undefined) {
socket.frame.push(buf);
frameLength = frameLength + 1;
}
if (frameLength < 1) {
return null;
}
do {
size = size + socket.frame[index].length;
if (size > frameSize) {
break;
}
index = index + 1;
} while (index < frameLength);
if (size < frameSize) {
return null;
}
if (size > frameSize) {
const bulk:Buffer = (index === 0)
? socket.frame[0]
: Buffer.concat(socket.frame.slice(0, index + 1)),
meta:websocket_meta = extended(bulk);
frame.extended = meta.lengthExtended;
frame.len = meta.lengthShort;
frame.mask = meta.mask;
frame.startByte = meta.startByte;
frameSize = frame.extended + frame.startByte;
if (size < frameSize) {
return null;
}
complete = unmask(bulk.slice(frame.startByte, frameSize));
if (index > 0) {
socket.frame.splice(1, index);
}
socket.frame[0] = bulk.slice(frameSize);
return complete;
}
complete = unmask(Buffer.concat(socket.frame.slice(0, index)).slice(frame.startByte));
socket.frame.splice(0, index);
return complete;
}());
if (socket.frame.length > 0) {
terminal_server_transmission_transmitWs_listener_processor(null);
}
Prior solution:
if (len > packageSize) {
// necessary if two frames from unrelated messages are combined into a single packet
excess = data.slice(packageSize);
data = data.slice(0, packageSize);
}
if (excess === null) {
socket.frame = [];
} else {
socket.frame = [excess];
terminal_server_transmission_transmitWs_listener_processor(null);
}
So far the learnings from performance testing my approach to websockets.
bun claims that it can send about 740,000 messages per second and the NPM package ws
can send about 107,000 messages per second.
ws
but only a third as fast as bun.
WebSocket messages are binary messages with a RFC6455 frame header over messages passed on a TCP stream.
Constraints
What is the most ideal way of solving for those three constraints. Don't worry about the technical requirements of RFC6455 as those are easily solved at this point. I am just looking for guidance on handling the listed items.