From 08996288be48c3ad333988e73737913df12a665a Mon Sep 17 00:00:00 2001 From: Russell Garner Date: Wed, 6 Apr 2016 21:23:02 +0100 Subject: [PATCH] Fix port collisions in adapter_lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to 'reserve' a free TCP Server port, we create a TCPServer, take a note of its port number, and then immediately close it. We'd then ask our adapter to use the port we just finished with via that adapter's configuration. While this isn't a particularly safe way of 'reserving' a port, it's the best we can do with heterogeneous adapters that have a one-way config method. However, this would cause intermittent test failures when our 'canary' socket did not get out of the way in time for an adapter to bind its server. Socket#close does not actually block until the socket is closed at OS level, it just frees the Ruby handle to it. Actually waiting for the socket to become free or garbage-collecting it to force the issue is counterproductive – we'd need to poll the port or run a full GC, which would take more time than a simple sleep. Additionally, the sleep needs to be tuned so that tests are not unduly slowed by it – too quick and we'll see failures, too slow and our builds will reflect it. --- lib/webmachine/spec/adapter_lint.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/webmachine/spec/adapter_lint.rb b/lib/webmachine/spec/adapter_lint.rb index fe978f5b..d50e642d 100644 --- a/lib/webmachine/spec/adapter_lint.rb +++ b/lib/webmachine/spec/adapter_lint.rb @@ -5,7 +5,19 @@ attr_accessor :client let(:address) { "127.0.0.1" } - let(:port) { s = TCPServer.new(address, 0); p = s.addr[1]; s.close; p } + let(:port) do + s = TCPServer.new(address, 0) + p = s.addr[1] + s.close # This does not close the socket at OS level, just frees from Ruby. + # The socket will be in TIME_WAIT http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html + # "The main thing to recognize about connection teardown is that a connection in + # the TIME_WAIT state cannot move to the CLOSED state until it has waited for two times + # the maximum amount of time an IP datagram might live in the Inter net." + + sleep(0.005) # This is just about the best we can do. Any more slows the tests, + # any less and we get intermittent silent port collisions + p + end let(:application) do application = Webmachine::Application.new