Skip to content

Commit 0da5727

Browse files
committed
writeToClient() properly handles chunk splitting
In case there are larger amounts of data to be sent back to client they are normally split into up to 64K chunks (unless server exceeds "maxmemory" which causes it to send as much as possible). When writeToClient() returns and has pending replies to be sent - it is scheduled in event loop and when IOCP signals that the socket is writeable again - sendReplyToClient() handler is called and sends additional chunk via writeToClient(). This however behaves differently under Windows and in order to send the following pedning replies - we need to schedule sendReplyToClient() again. Furthermore when we are sending data too fast and receive WSAEWOULDBLOCK error/warning - we back off again and re-schedule sending. Fixes #11
1 parent 2f87db0 commit 0da5727

File tree

1 file changed

+29
-3
lines changed

1 file changed

+29
-3
lines changed

src/networking.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,13 @@ int writeToClient(int fd, client *c, int handler_installed) {
970970
server.el, c, o, NULL);
971971
if (result == SOCKET_ERROR && errno != WSA_IO_PENDING) {
972972
nwritten = -1;
973+
974+
//[tporadowski/#11] we might be bursting data too fast, so turn it into another try that will put back the client
975+
// in the sending queue
976+
if (errno == WSAEWOULDBLOCK) {
977+
serverLog(LL_DEBUG, "writeToClient: will try again (EAGAIN) due to WSAEWOULDBLOCK");
978+
errno = EAGAIN;
979+
}
973980
break;
974981
}
975982
c->sentlen += objlen;
@@ -995,7 +1002,7 @@ int writeToClient(int fd, client *c, int handler_installed) {
9951002
if (listLength(c->reply) == 0)
9961003
serverAssert(c->reply_bytes == 0);
9971004
}
998-
}
1005+
}
9991006
/* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT
10001007
* bytes, in a single threaded server it's a good idea to serve
10011008
* other clients as well, even if a very large request comes from
@@ -1007,7 +1014,7 @@ int writeToClient(int fd, client *c, int handler_installed) {
10071014
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
10081015
(server.maxmemory == 0 ||
10091016
zmalloc_used_memory() < server.maxmemory)) break;
1010-
}
1017+
}
10111018
server.stat_net_output_bytes += totwritten;
10121019
if (nwritten == -1) {
10131020
if (errno == EAGAIN) {
@@ -1043,8 +1050,27 @@ int writeToClient(int fd, client *c, int handler_installed) {
10431050
return C_ERR;
10441051
}
10451052
}
1053+
#if _WIN32
1054+
/* [tporadowski/#11] too much data was sent in one run, schedule for next available sending slot again.
1055+
* We are re-scheduling only when writeToClient was called from sendReplyToClient handler as normally
1056+
* handleClientsWithPendingWrites() does this automatically after writeToClient() returns with pending
1057+
* replies.
1058+
*/
1059+
else if (handler_installed) {
1060+
if (aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
1061+
sendReplyToClient, c) == AE_ERR)
1062+
{
1063+
serverLog(LL_WARNING, "writeToClient: aeCreateFileEvent failed");
1064+
freeClientAsync(c);
1065+
return C_ERR;
1066+
}
1067+
else {
1068+
serverLog(LL_DEBUG, "writeToClient: re-scheduling sendReplyToClient() for pending client replies");
1069+
}
1070+
}
1071+
#endif
10461072
return C_OK;
1047-
}
1073+
}
10481074

10491075
/* Write event handler. Just send data to the client. */
10501076
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {

0 commit comments

Comments
 (0)