Gzip Gunzip: always flush busy bufs when the incoming chain is NULL.
mdounin at mdounin.ru
Wed Nov 5 15:41:28 UTC 2014
On Tue, Nov 04, 2014 at 12:42:56PM -0800, Yichun Zhang (agentzh) wrote:
> On Mon, Nov 3, 2014 at 4:54 PM, Maxim Dounin wrote:
> > The commit log in question explains the reason for the change.
> > Work on the gzip stalls problem as fixed by 973fded4f461 clearly
> > showed that just passing NULL chains is wrong unless last buffer
> > was already sent or there are busy buffers.
> AFAIK, this is the output filter caller's responsibility to ensure the
> requirements "last buffer was already sent or there are busy buffers",
> not every individual output body filter.
The problem is that caller's idea about busy buffers isn't the
same as gzip filter's one. Caller may call output filter chain
with NULL because some of its buffers are busy, but since they are
buffered by gzip filter it would be wrong to call next body filter
with NULL unconditionally. So in this case it's gzip filter which
is responsible to call next body filter properly.
> Almost all the other standard
> nginx output filter modules including but not limited to ngx_addition,
> ngx_sub_filter, ngx_image_filter, ngx_chunked_filter, and
> ngx_xslt_filter. You meant to fix these modules as well?
Addition filter doesn't buffer anything. Sub filter only calls
next body filter with NULL if it doesn't buffer anything. Image
filter always releases all input buffers and so this shouldn't be
a problem as long as a caller does the right thing. Chunked filter
doesn't buffer anything. Xslt filter always releases all input
That is, all these modules are fine and there is no need to fix
> Also, the requirement "there are busy buffers" should not just be busy
> buffers of the output filter module itself, but rather busy buffers in
> all the other filters along the filter chain, include the busy bufs in
> r->out which is managed by ngx_http_write_filter_module at the bottom
> of the filter chain.
Technically yes. But main purpose of calling output filter chain
with NULL before the last buffer is sent is to free up some busy
buffers of a calling module, and checking for the module own
buffers should be enough - and this is what, e.g., unbuffered
> > If you see an issue, you may want to share the issue details,
> > to find out how to fix it properly.
> The very issue of nginx 1.7.7 caught by my ngx_lua's test suite is in
> this condition:
So this isn't a real issue, but an artificial test case which
> 1. the content handler runs out of buffers (i.e., having the maximum
> number of busy buffers), and
> 2. ngx_gzip or ngx_gunzip does not have any busy buffers for itself
> (i.e., no ctx->busy), and
> 3. ngx_http_write_filter_module has pending data for flush to the
> system send buffer, that is, having busy bufs in r->out.
> Upon new write events, I pass NULL to the output filter chain in order
> to flush the busy bufs of ngx_http_write_fiilter_module (in r->out),
> but ngx_gzip and ngx_gunzip quietly swallows it, wasting the write
> events, and leading to request hang. This use case of NULL chain in
> ngx_http_output_filter should considered valid because there are
> indeed busy bufs for ngx_http_write_filter_module (though not
> ngx_gzip/ngx_gunzip itself). Disabling the gzip or gunzip filter makes
> the problem go away immediately.
The questions are:
- How it happened that all content handler's buffers are busy,
while there are no busy buffers in gzip?
- How it happened that there are no busy buffers in gzip, but
there are buffers in r->out?
- Why calling next filter in gzip with NULL is expected to help
here? It only can free up some gzip filter buffers which are
all already free, but not content handler module buffers.
> Regarding the "output chain is empty" alert (generated by
> ngx_http_write_filter_module), it should only happen when there is
> *no* busy bufs and no data in r->out, which is apparently not the case
> explained above.
Sure. The problem is that the code change you suggest (and that
was previously done in 1.5.7 / c52a761a2029) triggers alert in
other valid cases, when there are no data in r->out.
More information about the nginx-devel