You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We run an application that uses pistache to provide an API server. One of the API endpoint returns relatively large amount of data (about 160KB), we observed "incomplete" responses that make the clients stuck.
When we use the curl command to inspect the response data, the symptom is, the response header is complete (with 200 response code, content length, and the "Connection: Close" indication, however, the actual data is less than specified in the "Content-Length" field. As a result, curl waits for the remaining data and doesn't finish, until we manually press "Ctrl+C" to stop it.
(The ^C at the end is the result as we pressed Ctrl+C)
Initially we thought it might be due to networking issue, since this program runs on Kubernetes and we have ipvs and complicated iptables rules on the host. But later all the suspicions are all ruled out. During the diagnosis we found the observation itself also makes impacts to the program behavior, for example, when we use strace to attach to the process, the issue has less chance to appear. But we do find the pattern and think this should be a bug in pistache.
Several things are happening when we hit this issue, they are always the case:
We use tcpdump to capture the packets on the server interface. We always see "TCP Window Full" diagnostic information. (Packet No. 1553), later all data are acknowledged by the client (Packet No.1563), but server doesn't send more data until we hit ^C, which triggers the client to close the socket (Packet No.2187).
When the client is waiting on more data, before we hit the ^C to terminate the connection, we use netstat to inspect both client and server side connection information. From both side, regarding that connection, the tcp send queue and receive queue are all empty.
Those evidences make us believe it is either the application itself or the kernel that doesn't behave correctly, so we focus more on strace. Below is the comparison of a successful response and a stuck one. First column in each line is the line number, second column is the pid of that thread, we have multiple pistache threads in the application.
When the issue doesn't occur, all data are successfully written. (The socket fd is 200). Then in L24821 it calls epool_ctl to just wait EPOLLIN events. The client, after it gets all the data, closes the socket, which makes the epoll_wait in L24822 return and we proceed to the socket closure on L24826.
Below is the related strace log when the issue happens. The socket fd is still 200. The second to last write (L27420), the write intention is 16406, but only 11612 bytes are written, then the next write got an EAGAIN (L27421), this is because the tcp send buffer is full, the kernel needs some ack packets from the client to release the buffer.
As a response, in L27422 the epoll_ctl is called to subscribe to both EPOLLIN and EPOLLOUT event. If the tcp send buffer has available space, we expect a EPOLLOUT event to resume the writing. The problem is in L27423, another epoll_ctl is immediately called to only subscribe EPOLLIN event. When the send buffer is available again, the kernel won't send any event since EPOLLOUT is unsubscribed.
Thanks for reporting @jiankyu. I'm probably not much help with this issue, but I can't help but wonder if it might be related to another issue I recently discovered.
We run an application that uses pistache to provide an API server. One of the API endpoint returns relatively large amount of data (about 160KB), we observed "incomplete" responses that make the clients stuck.
When we use the curl command to inspect the response data, the symptom is, the response header is complete (with 200 response code, content length, and the "Connection: Close" indication, however, the actual data is less than specified in the "Content-Length" field. As a result, curl waits for the remaining data and doesn't finish, until we manually press "Ctrl+C" to stop it.
(The
^C
at the end is the result as we pressed Ctrl+C)Initially we thought it might be due to networking issue, since this program runs on Kubernetes and we have ipvs and complicated iptables rules on the host. But later all the suspicions are all ruled out. During the diagnosis we found the observation itself also makes impacts to the program behavior, for example, when we use
strace
to attach to the process, the issue has less chance to appear. But we do find the pattern and think this should be a bug in pistache.Several things are happening when we hit this issue, they are always the case:
^C
, which triggers the client to close the socket (Packet No.2187).^C
to terminate the connection, we usenetstat
to inspect both client and server side connection information. From both side, regarding that connection, the tcp send queue and receive queue are all empty.Those evidences make us believe it is either the application itself or the kernel that doesn't behave correctly, so we focus more on
strace
. Below is the comparison of a successful response and a stuck one. First column in each line is the line number, second column is the pid of that thread, we have multiple pistache threads in the application.When the issue doesn't occur, all data are successfully written. (The socket fd is 200). Then in L24821 it calls
epool_ctl
to just waitEPOLLIN
events. The client, after it gets all the data, closes the socket, which makes theepoll_wait
in L24822 return and we proceed to the socket closure on L24826.Below is the related strace log when the issue happens. The socket fd is still 200. The second to last write (L27420), the write intention is 16406, but only 11612 bytes are written, then the next write got an EAGAIN (L27421), this is because the tcp send buffer is full, the kernel needs some ack packets from the client to release the buffer.
As a response, in L27422 the
epoll_ctl
is called to subscribe to bothEPOLLIN
andEPOLLOUT
event. If the tcp send buffer has available space, we expect aEPOLLOUT
event to resume the writing.The problem is in L27423, another
epoll_ctl
is immediately called to only subscribeEPOLLIN
event. When the send buffer is available again, the kernel won't send any event sinceEPOLLOUT
is unsubscribed.The next event on this socket is the close event, triggered by the
^C
from client side, several seconds later.The line 27423 seems the root cause of the issue. That call should not be there.
The text was updated successfully, but these errors were encountered: