From e1570586116d6facccf4d42a71e960a351e2d229 Mon Sep 17 00:00:00 2001 From: tcosgrave Date: Thu, 22 Mar 2018 18:05:31 +0100 Subject: [PATCH 1/2] Add shutdown command to enable management of open sockets without relying on GC or OS to close them --- lib/vault/client.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/vault/client.rb b/lib/vault/client.rb index c66ad400..32c60413 100644 --- a/lib/vault/client.rb +++ b/lib/vault/client.rb @@ -158,6 +158,12 @@ def pool private :pool + # Shutdown any open pool connections. Pool will be recreated upon next request. + def shutdown + @nhp.shutdown() + @nhp = nil + end + # Creates and yields a new client object with the given token. This may be # used safely in a threadsafe manner because the original client remains # unchanged. The value of the block is returned. From 69df181fb3e2d0da1ca49c6ea407695612b4454b Mon Sep 17 00:00:00 2001 From: tcosgrave Date: Fri, 23 Mar 2018 15:56:35 +0100 Subject: [PATCH 2/2] Add shudown funciton to client This change exposes the persistent pool shutdown fuction to the vault client to help manage sockets kept open by long running proccesses. --- .gitignore | 1 + spec/integration/client_spec.rb | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/.gitignore b/.gitignore index 7ed4cee5..720977b0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.gem *.rbc /.config +/.vscode /coverage/ /InstalledFiles /pkg/ diff --git a/spec/integration/client_spec.rb b/spec/integration/client_spec.rb index 9452e1a6..86c6dc7d 100644 --- a/spec/integration/client_spec.rb +++ b/spec/integration/client_spec.rb @@ -52,5 +52,65 @@ def free_address }.to raise_error(MissingTokenError) end end + + describe "#shutdown" do + it "clears the pool after calling shutdown and sets nhp to nil" do + TCPServer.open('localhost', 0) do |server| + Thread.new do + loop do + client = server.accept + sleep 0.25 + client.close + end + end + + address = "http://%s:%s" % ["localhost", server.addr[1]] + + client = described_class.new(address: address, token: "foo") + + expect { client.request(:get, "/", {}, {}) }.to raise_error(HTTPConnectionError) + + pool = client.instance_variable_get(:@nhp).pool + + client.shutdown() + + expect(pool.available.instance_variable_get(:@enqueued)).to eq(0) + expect(pool.available.instance_variable_get(:@shutdown_block)).not_to be_nil + expect(client.instance_variable_get(:@nhp)).to be_nil + + server.close + end + end + + it "the pool is recreated on the following request" do + TCPServer.open('localhost', 0) do |server| + Thread.new do + loop do + client = server.accept + sleep 0.25 + client.close + end + end + + address = "http://%s:%s" % ["localhost", server.addr[1]] + + client = described_class.new(address: address, token: "foo") + + expect { client.request(:get, "/", {}, {}) }.to raise_error(HTTPConnectionError) + + client.shutdown() + + expect { client.request(:get, "/", {}, {}) }.to raise_error(HTTPConnectionError) + + pool = client.instance_variable_get(:@nhp).pool + + expect(pool.available.instance_variable_get(:@enqueued)).to eq(1) + expect(pool.available.instance_variable_get(:@shutdown_block)).to be_nil + expect(client.instance_variable_get(:@nhp)).not_to be_nil + + server.close + end + end + end end end