-
-
Notifications
You must be signed in to change notification settings - Fork 83
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
Client connection pooling #121
Open
daurnimator
wants to merge
2
commits into
client.dns_resolver
Choose a base branch
from
client.pooling
base: client.dns_resolver
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
local cs = require "cqueues.socket" | ||
|
||
local function reuse_connection(candidate, connect_options) | ||
-- Assume family/host/port/path already checked | ||
|
||
if candidate.socket == nil then | ||
return false | ||
end | ||
|
||
if connect_options.v6only then | ||
-- TODO | ||
return false | ||
end | ||
|
||
local bind = connect_options.bind | ||
if bind then | ||
-- TODO: Use :localname() | ||
return false | ||
end | ||
|
||
local version = connect_options.version | ||
if version and version ~= candidate.version then | ||
return false | ||
end | ||
|
||
if candidate.version < 2 then | ||
-- Check if connection already in use (avoid pipelining) | ||
if candidate.req_locked then | ||
return false | ||
end | ||
elseif candidate.version == 2 then | ||
-- Check if http2 connection is nearing end of stream ids | ||
local highest_stream_id = math.max(candidate.highest_odd_stream, candidate.highest_even_stream) | ||
-- The stream id is a unsigned 31bit integer. we don't reuse if it's past half way | ||
if highest_stream_id > 0x3fffffff then | ||
return false | ||
end | ||
|
||
local h2_settings = connect_options.h2_settings | ||
if h2_settings then | ||
-- TODO: check (and possibly change on connection?) | ||
return false | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should check |
||
end | ||
|
||
-- Do TLS check last, as it is the most expensive | ||
if connect_options.tls then | ||
-- TODO: compare TLS parameters | ||
return false | ||
end | ||
|
||
-- Check to see if connection has been closed | ||
local ok, err = candidate.socket:fill(1, 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Factor out into a "is alive" function. Can be reused for cleaning the pool |
||
if not ok and err == nil then | ||
-- has been closed | ||
return false | ||
end | ||
|
||
return true | ||
end | ||
|
||
local pool_methods = {} | ||
local pool_mt = { | ||
__name = "http.client.pool"; | ||
__index = pool_methods; | ||
} | ||
|
||
local function new_pool() | ||
return setmetatable({}, pool_mt) | ||
end | ||
|
||
local function ipv4_pool_key(addr, port) | ||
return string.format("%d:%s:%s", cs.AF_INET, addr, port) | ||
end | ||
|
||
local function ipv6_pool_key(addr, port) | ||
return string.format("%d:[%s]:%s", cs.AF_INET6, addr, port) | ||
end | ||
|
||
local function unix_pool_key(path) | ||
return string.format("%d:%s", cs.AF_UNIX, path) | ||
end | ||
|
||
local function connection_pool_key(connection) | ||
-- XXX: if using a proxy this may not be correct | ||
local family, a, b = connection:peername() | ||
if family == cs.AF_INET then | ||
return ipv4_pool_key(a, b) | ||
elseif family == cs.AF_INET6 then | ||
return ipv6_pool_key(a, b) | ||
elseif family == cs.AF_UNIX then | ||
return unix_pool_key(a) | ||
end | ||
end | ||
|
||
function pool_methods:add(connection) | ||
local key = connection_pool_key(connection) | ||
if not key then | ||
return false | ||
end | ||
local dst_pool = self[key] | ||
if dst_pool == nil then | ||
dst_pool = {} | ||
self[key] = dst_pool | ||
end | ||
dst_pool[connection] = true | ||
return true | ||
end | ||
|
||
function pool_methods:remove(connection) | ||
local key = connection_pool_key(connection) | ||
if not key then | ||
return true | ||
end | ||
local dst_pool = self[key] | ||
if dst_pool == nil then | ||
return true | ||
end | ||
dst_pool[connection] = nil | ||
if next(dst_pool) == nil then | ||
self[key] = nil | ||
end | ||
return true | ||
end | ||
|
||
local function find_connection(dst_pool, connect_options) | ||
for connection in pairs(dst_pool) do | ||
if reuse_connection(connection, connect_options) then | ||
return connection | ||
end | ||
end | ||
return nil | ||
end | ||
|
||
return { | ||
ipv4_pool_key = ipv4_pool_key; | ||
ipv6_pool_key = ipv6_pool_key; | ||
unix_pool_key = unix_pool_key; | ||
|
||
new = new_pool; | ||
find_connection = find_connection; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: check pipeline depth instead.
Also, might want to find shortest pipeline in the pool?