Skip to content

Commit da5ff1f

Browse files
committed
Fix open two connections with changed cache key
`args.to_s` is used for cache key of connection pooling. `ssh_options` in `args` is changed while creating connections in `connection_factory.call(*args)`. Replacing cache key with changed `args` prevents cache miss.
1 parent 018a3cc commit da5ff1f

3 files changed

Lines changed: 28 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ appear at the top.
77

88
* Your contribution here!
99
* [#390](https://github.com/capistrano/sshkit/pull/390): Properly wrap Ruby StandardError w/ add'l context - [@mattbrictson](https://github.com/mattbrictson)
10+
* [#392](https://github.com/capistrano/sshkit/pull/392): Fix open two connections with changed cache key - [@shirosaki](https://github.com/shirosaki)
1011

1112
## [1.12.0][] (2017-02-10)
1213

lib/sshkit/backends/connection_pool.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,29 @@ def initialize(idle_timeout=30)
5454
# invoking the `connection_factory` proc with the given `args`. The arguments
5555
# are used to construct a key used for caching.
5656
def with(connection_factory, *args)
57+
ssh_options = args.last
58+
ssh_options_keys = ssh_options.keys if ssh_options.is_a? Hash
59+
cache_key = args.to_s
5760
cache = find_cache(args)
5861
conn = cache.pop || begin
5962
connection_factory.call(*args)
6063
end
6164
yield(conn)
6265
ensure
6366
cache.push(conn) unless conn.nil?
67+
update_cache_key(ssh_options, ssh_options_keys, cache_key, args)
68+
end
69+
70+
# Update cache key with changed args to prevent cache miss
71+
def update_cache_key(ssh_options, ssh_options_keys, cache_key, args)
72+
return unless ssh_options_keys
73+
args[args.size - 1] = ssh_options.reject { |k, _| !ssh_options_keys.include?(k) }
74+
new_key = args.to_s
75+
if cache_key != new_key
76+
caches.synchronize do
77+
caches[new_key] = caches.delete(cache_key)
78+
end
79+
end
6480
end
6581

6682
# Immediately remove all cached connections, without closing them. This only

test/unit/backends/test_connection_pool.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ def test_close_connections
134134
pool.close_connections
135135
end
136136
end
137+
138+
def test_connections_with_changed_args_is_reused
139+
connect_change_args = ->(*args) { args[2][:a] << "a"; args[2][:b] = []; Object.new }
140+
ssh_options = { :a => [] }
141+
option1 = ["conn", "user", ssh_options.dup]
142+
conn1 = pool.with(connect_change_args, *option1) { |c| c }
143+
option2 = ["conn", "user", ssh_options.dup]
144+
conn2 = pool.with(connect_change_args, *option2) { |c| c }
145+
146+
assert_equal conn1, conn2
147+
end
137148
end
138149
end
139150
end

0 commit comments

Comments
 (0)