Skip to content

Added request body buffer regeneration #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from

Conversation

dylandreimerink
Copy link

I made a fix for issue #41 and #42.
Optional support for body buffer regeneration.

end

local function finish_body(self)
if self.resore_body then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo here

return nil, err
end

return setmetatable({
sock = sock,
size = chunk_size or CHUNK_SIZE,
line_size = max_line_size or MAX_LINE_SIZE,
resore_body = restore_body_buffer or false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resore -> restore? Also or false not needed, though, doesn't hurt much either.

@@ -145,6 +181,8 @@ local function read_body_part(self)

local data = sock:receive(2)
if data == "--" then
append_body(self, "\r\n--" .. self.boundary .. "--\r\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always concatenate strings here. Even when restore_body is false. Same thing happens in other append_body places. Maybe just checking the boolean variable doesn't warrant its own function and that can be inlined. Then we also solve this issue.


local function discard_line(self)
local read_line = self.read_line

local line, err = read_line(self.line_size)
if not line then
finish_body(self);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

semicon ; not needed.

@dylandreimerink
Copy link
Author

I fixed the typo and implemented you suggestion. I am not sure though if it is faster to append multiple times like I have done now or to create a new string and append once.

@bungle
Copy link
Member

bungle commented Apr 2, 2018

Yes, previously you created strings even when not appending anything.

@@ -93,21 +111,39 @@ function _M.set_timeout(self, timeout)
return sock:settimeout(timeout)
end

local function append_body(self, ...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to use varargs here, as you only ever have at most 3 args. Also you don't need to create a table with varargs, you can select("#", ...) and select(1, ...).

@bungle
Copy link
Member

bungle commented Apr 2, 2018

@dylandreimerink thanks for working on this. I think that one thing to add is test(s).

@dylandreimerink
Copy link
Author

I took a look at the source code for ngx.req.append_body which is quite big and does a memcpy. So I think it is best to ditch the whole idea of a separate function ( like u suggested before ) and concatenate a single string in the main functions. Then calling ngx.req.append_body once right before ngx.req.finish_body to minimize overhead.

I will work on that as soon as I have time

@dylandreimerink
Copy link
Author

dylandreimerink commented Apr 4, 2018

This should do it. I replaced the appending by a string buffer which should make it more performant. If this passes the only thing left to do is to make tests. I have to admit however that I do not yet understand how this unit test framework works, so i may need a little bit of guidance.

@bungle
Copy link
Member

bungle commented Apr 4, 2018

I am not sure what does it add to buffer to Lua table? Just more complexity. For real string buffer we need something like this: LuaJIT/LuaJIT#14

I think that just ngx.req.append_body inline without separate function is the best still. Even append that \r\n without ... Lets just not be to clever here. The function is called append, and that should be fast already. If that turns to be issue, we need to prove it first, before adding table buffers etc.

@dylandreimerink
Copy link
Author

dylandreimerink commented Apr 4, 2018

Working from the assumption that append_body is relatively speaking slow since it has to copy data from the lua vm to nginx the idea is to keep the data in lua for as long as possible. Since lua strings are immutable they have to be recreated and copied every time you want to concatenate to an existing string. The solution would be to have a native implementation of a mutable string or string buffer. But since I have little hope for that in the near future I think the use of a table to store a list of strings and concatenating them once is the best option.

The reason I am going to these lengths is because I will be using this feature in a proxy with a high throughput, so for me personally speed and optimization is quite important. So in light of that I will benchmark the different solutions so we can make an informed decision instead of having to work based on assumptions.

@dylandreimerink
Copy link
Author

I made the benchmark. The results are to me a bit unexpected. I tested 4 variations and the original, the summarized results are as follows:

Name total time time per req (avg) reqests per second(avg)
Base test 87.77 8.777 11393.38
complex string buffer test 90.987 9.099 10990.54
simple string buffer test 90.971 9.097 10992.56
string concat test 90.843 9.084 11008.04
No buffer test 90.233 9.023 11082.48

The variation without the string buffer is the fastest but the difference is almost negligible.
So I went with that solution.

@dylandreimerink
Copy link
Author

@bungle Have you had time to look at the last changes?

Copy link
Member

@bungle bungle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some more comments. Mostly style related changes. Biggest pressing issue right now is that this doesn't yet have tests. I think we should have good test suite that also tests with invalid input. Should we actually have some kind of internal error handler here that runs in case of error happens, and then just writes rest of the buffer back, e.g. leaving the body intact.

return nil, err
end

local read_line, err = sock:receiveuntil("\r\n")
if not read_line then

if restore_body_buffer then
ngx_finish_body()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous read, on line 72, was successful, but we didn't write the request body back, is this intended? Maybe there are many similar cases in code, but no easy way to solve? E.g. on errors we don't write the request body back.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sock:receiveuntil returns an iterator Lua function and doesn't actually read data from the buffer so there is noting to restore.

return nil, err
end

if self.restore_body_buffer then
ngx_append_body(line .. "\r\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine, but you could also have:

ngx_append_body(line)
ngx_append_body("\r\n")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any point in not concatenating the string here, the same goes for the rest of the remarks regarding concatenation.

@@ -145,6 +181,10 @@ local function read_body_part(self)

local data = sock:receive(2)
if data == "--" then
if self.restore_body_buffer then
ngx_append_body("\r\n--" .. self.boundary .. "--\r\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

ngx_append_body("\r\n--")
ngx_append_body(self.boundary)
ngx_append_body(ngx_append_body("--\r\n"))

Not a big deal.

@@ -161,10 +201,18 @@ local function read_body_part(self)
end
end

if self.restore_body_buffer then
ngx_append_body("\r\n--" .. self.boundary .. "\r\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This coud be cached before the while loop:

local boundary_delimiter = "\r\n--" .. self.boundary .. "\r\n"

And then here:

ngx_append_body(boundary_delimiter)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no while loop surrounding this line

@@ -232,7 +296,14 @@ local function read_preamble(self)
while true do
local preamble = read2boundary(size)
if not preamble then
if self.restore_body_buffer then
ngx_append_body("--" .. self.boundary)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here:

ngx_append_body("--")
ngx_append_body(self.boundary)

Yes, bikeshedding :-).

local dummy, err = read_line(1)
if dummy then
if self.restore_body_buffer then
ngx_finish_body()
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after this.

return nil, "line too long: " .. line .. dummy .. "..."
end

if err then
if self.restore_body_buffer then
ngx_finish_body()
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after this.

@@ -122,6 +152,9 @@ local function discard_rest(self)
while true do
local dummy, err = sock:receive(size)
if err and err ~= 'closed' then
if self.restore_body_buffer then
ngx_finish_body()
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after this.

@@ -137,6 +170,9 @@ local function read_body_part(self)

local chunk, err = read2boundary(self.size)
if err then
if self.restore_body_buffer then
ngx_finish_body()
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after this.

@@ -174,15 +222,28 @@ local function read_header(self)

local line, err = read_line(self.line_size)
if err then
if self.restore_body_buffer then
ngx_finish_body()
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line after this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants