resize query buffer more accurately#5013
Conversation
|
@oranagra @yoav-steinberg dd if=/dev/zero of=bigfile bs=1M count=32
./src/redis-cli -x hset a a < bigfile
|
|
regarding #9003, shouldn't this line be: - if (remaining > 0 && (size_t)remaining < readlen) readlen = remaining;
+ if (remaining > 0 && (size_t)remaining > readlen) readlen = remaining;or even just: - if (remaining > 0 && (size_t)remaining < readlen) readlen = remaining;
+ readlen = remaining;i.e. this readlen is what's triggering the (non greedy) allocation, we do want to make room for the entire big arg. regardless, i have a new realization about the shrinking. then, for the case of an idle client that did initiate sending a big arg and then became idle, the idle time (2 seconds), can shrink the query buffer despite of i suppose that fixing either of these two problems will solve the failing pendingquerybuf.tcl test, but we wanna do both. |
If I follow this logic through, then I'd say that peak based shrinking should only happen in case the query buf is empty |
|
@yoav-steinberg There is still a problem with |
You're right. How about |
|
@yoav-steinberg querybuf is also shrinked in case of inline protocol, because the default value of |
…Followup for #9003) (#9100) Due to the change in #9003, a long-standing bug was raised under `valgrind`. This bug can cause the master-slave sync to take a very long time, causing the `pendingquerybuf.tcl` test to fail. This problem does not only occur in master-slave sync, it is triggered when the big arg is greater than 32k. step: ```sh dd if=/dev/zero of=bigfile bs=1M count=32 ./src/redis-cli -x hset a a < bigfile ``` 1) Make room for querybuf in processMultibulkBuffer, now the alloc of querybuf will be more than 32k. 2) If this happens to trigger the `clientsCronResizeQueryBuffer`, querybuf will be resized to 0. 3) Finally, in readQueryFromClient, we expand the querybuf non-greedily, from 0 to 32k. Old code, make room for querybuf is greedy, so it only needs 11 times to expand to 32M(16k*(2^11)), but now we need 2048(32*1024/16) times to reach it, due to the slow allocation under valgrind that exposed the problem. The fix for the excessive shrinking of the query buf to 0, will be handled in #5013 (that other change on it's own can fix failing test too), but the fix in this PR will also fix the failing test. The fix in this PR will makes the reading in `readQueryFromClient` more aggressive when working on a big arg (so that it is in par with the same code in `processMultibulkBuffer` (i.e. the two calls to `sdsMakeRoomForNonGreedy` should both use the bulk size). In the code before this fix the one in readQueryFromClient always has `readlen = PROTO_IOBUF_LEN`
|
@sundb @yoav-steinberg @soloestoy please have a look at the last change i pushed. |
yoav-steinberg
left a comment
There was a problem hiding this comment.
Regarding my comments about doing the resize only when sdslen(c->querybuf)==0, I now see I was wrong. Even if we're not idle and we have excess qbuf size due to previous command we still might be in the middle of processing the current command with a non zero qbuf and want to resize.
See below my updated remarks.
|
@soloestoy can you take a look at my abuse of your PR? |
yoav-steinberg
left a comment
There was a problem hiding this comment.
Fix bug when growing to a larger sds type.
1. querybuf_peak has not been updated correctly in readQueryFromClient. 2. qbuf shrinking uses sdsalloc instead of sdsAllocSize see more details in issue redis#4983
dfa05f2 to
38eb358
Compare
when tracking the peak, don't reset the peak to 0, reset it to the maximum of the current used, and the planned to be used by the current arg. when shrining, split the two separate conditions. the idle time shrinking will remove all free space. but the peak based shrinking will keep room for the current arg. when we resize due to a peak (rahter than idle time), don't trim all unused space, let the qbuf keep a size that's sufficient for the currently process bulklen, and the current peak. Co-authored-by: sundb <[email protected]> Co-authored-by: yoav-steinberg <[email protected]>
38eb358 to
582e1f9
Compare
…Followup for redis#9003) (redis#9100) Due to the change in redis#9003, a long-standing bug was raised under `valgrind`. This bug can cause the master-slave sync to take a very long time, causing the `pendingquerybuf.tcl` test to fail. This problem does not only occur in master-slave sync, it is triggered when the big arg is greater than 32k. step: ```sh dd if=/dev/zero of=bigfile bs=1M count=32 ./src/redis-cli -x hset a a < bigfile ``` 1) Make room for querybuf in processMultibulkBuffer, now the alloc of querybuf will be more than 32k. 2) If this happens to trigger the `clientsCronResizeQueryBuffer`, querybuf will be resized to 0. 3) Finally, in readQueryFromClient, we expand the querybuf non-greedily, from 0 to 32k. Old code, make room for querybuf is greedy, so it only needs 11 times to expand to 32M(16k*(2^11)), but now we need 2048(32*1024/16) times to reach it, due to the slow allocation under valgrind that exposed the problem. The fix for the excessive shrinking of the query buf to 0, will be handled in redis#5013 (that other change on it's own can fix failing test too), but the fix in this PR will also fix the failing test. The fix in this PR will makes the reading in `readQueryFromClient` more aggressive when working on a big arg (so that it is in par with the same code in `processMultibulkBuffer` (i.e. the two calls to `sdsMakeRoomForNonGreedy` should both use the bulk size). In the code before this fix the one in readQueryFromClient always has `readlen = PROTO_IOBUF_LEN`
when tracking the peak, don't reset the peak to 0, reset it to the maximum of the current used, and the planned to be used by the current arg. when shrining, split the two separate conditions. the idle time shrinking will remove all free space. but the peak based shrinking will keep room for the current arg. when we resize due to a peak (rahter than idle time), don't trim all unused space, let the qbuf keep a size that's sufficient for the currently process bulklen, and the current peak. Co-authored-by: sundb <[email protected]> Co-authored-by: yoav-steinberg <[email protected]>
After commit cec404f there are still some problem we should fix I think:
c->querybuf_peakhas not been updated correctly inreadQueryFromClient.As we can see, we update
c->querybuf_peakbeforereadandsdsIncrLen, it means thatqblenhere is the lastc->querybuflength. But the length ofc->querybufafterprocessInputBufferis always 0, unless we didn't read the whole request at one time.So, we should update
c->querybuf_peakaftersdsIncrLenI think.We should use
sdsallocinstead ofsdsAllocSize.Because we wanna get
c->querybufalloc space except for header, thesdsAllocSizevalue is always 32 * 1024 +sdsHdrSize+ 1 grate than 32 * 1024.Check if query buffer is > BIG_ARG and too big for latest peak only whenc->querybuf_peakis not 0.FunctionclientsCronResizeQueryBufferresetc->querybuf_peakto be 0 no matter resize the query buffer or not.If we don't get any requests after that, next time we will trigger the condition definitely.Query buffer shrinking improvements
when tracking the peak, don't reset the peak to 0, reset it to the maximum of the current used, and the planned to be used by the current arg.
when shrining, split the two separate conditions. the idle time shrinking will remove all free space. but the peak based shrinking will keep room for the current arg.
when we resize due to a peak (rahter than idle time), don't trim all unused space, let the qbuf keep a size that's sufficient for the currently process bulklen, and the current peak.