uninterruptible 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ffdc9d6870a2bf9ae5573fbf9cc5b0e8a4d3f86
4
- data.tar.gz: 81019ac427f4af4e182c8cf367ce9c807cef97a6
3
+ metadata.gz: 1947e00fb25907205555f9a1cbef1d5875008513
4
+ data.tar.gz: a6865bb7d65675c31aad797c4cd88634285419ce
5
5
  SHA512:
6
- metadata.gz: 163f49c6706ade360df9626e817c92af5e0c6276d6867a2dab5a6b5ab4582e5ff7e25209316a778add47b8bf45629e0abae3c02f170c47c6b98db2e5a7f55370
7
- data.tar.gz: 7dd7aa37124bde1196b48f6884d62b1db6214c695ac1f8e4b7c1ff1832d0f015c03829c6b502705857be79b29f8f156bdf4d4b05c1f280fdd1c6513765839097
6
+ metadata.gz: 33f6c792fa320d873e3d47d90f2500f7ec2fc589192ae18081df04a88bcda5518a198bffb0933005bb4af0ffe2636ee1c3542619ebb9604aa1fc2be2a913ae37
7
+ data.tar.gz: dd6b8c1f399b544a4977043176734aa50ea3857a1aa55bc1f8aeb0e9b3201e712785a510c8c23a0eb8613b89696aa0d1495ede000c19080e8e44eed07d416bd0
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Uninterruptible
2
2
 
3
- Uninterruptible gives you zero downtime restarts for your TCP servers with nearly zero effort. Sounds good? Read on...
3
+ Uninterruptible gives you zero downtime restarts for your socket servers with nearly zero effort. Sounds good? Read on.
4
4
 
5
5
  Small socket servers are great, sometimes you need a quick and efficient way of moving data between servers (or even
6
6
  processes on the same machine). Restarting these processes can be a bit hairy though, you either need to build your
@@ -60,14 +60,22 @@ finished processing all of it's existing connections. To kill the server (allowi
60
60
  ```ruby
61
61
  echo_server.configure do |config|
62
62
  config.start_command = 'ruby echo_server.rb' # *Required* Command to execute to start a new server process
63
- config.bind_port = 6789 # *Required* Port to listen on, falls back to ENV['PORT']
64
- config.bind_address = '::' # Address to listen on
63
+ config.bind = "tcp://0.0.0.0:12345" # *Required* Interface to listen on, falls back to 0.0.0.0 on ENV['PORT']
65
64
  config.pidfile_path = 'tmp/pids/echoserver.pid' # Location to write a pidfile, falls back to ENV['PID_FILE']
66
65
  config.log_path = 'log/echoserver.log' # Location to write logfile, defaults to STDOUT
67
66
  config.log_level = Logger::INFO # Log writing severity, defaults to Logger::INFO
68
67
  end
69
68
  ```
70
69
 
70
+ Uninterruptible supports both TCP and UNIX sockets. To connect to a unix socket simply pass the path in the bind
71
+ configuration parameter:
72
+
73
+ ```ruby
74
+ echo_server.configure do |config|
75
+ config.bind = "unix:///tmp/echo_server.sock"
76
+ end
77
+ ```
78
+
71
79
  ## The Magic
72
80
 
73
81
  Upon receiving `USR1`, your server will spawn a new copy of itself and pass the file descriptor of the open socket to
@@ -102,7 +110,7 @@ class EchoServer
102
110
 
103
111
  def worker_loop
104
112
  loop do
105
- client_socket = tcp_server.accept
113
+ client_socket = socket_server.accept
106
114
  process_request(client_socket)
107
115
  end
108
116
  end
@@ -1,8 +1,11 @@
1
1
  require "uninterruptible/version"
2
2
  require 'uninterruptible/configuration'
3
+ require 'uninterruptible/binder'
3
4
  require 'uninterruptible/server'
4
5
 
5
6
  # All of the interesting stuff is in Uninterruptible::Server
6
7
  module Uninterruptible
7
8
  class ConfigurationError < StandardError; end
9
+
10
+ SERVER_FD_VAR = 'UNINTERRUPTIBLE_SERVER_FD'.freeze
8
11
  end
@@ -0,0 +1,69 @@
1
+ require 'uri'
2
+
3
+ module Uninterruptible
4
+ class Binder
5
+ attr_reader :bind_uri
6
+
7
+ # @param [String] bind_address The config for a server we're returning the socket for
8
+ # @example
9
+ # "unix:///tmp/server.sock"
10
+ # "tcp://127.0.0.1:8080"
11
+ def initialize(bind_address)
12
+ @bind_uri = parse_bind_address(bind_address)
13
+ end
14
+
15
+ # Bind to the TCP or UNIX socket defined in the #bind_uri
16
+ #
17
+ # @return [TCPServer, UNIXServer] Successfully bound server
18
+ #
19
+ # @raise [Uninterruptible::ConfigurationError] Raised when the URI indicates a non-tcp or unix scheme
20
+ def bind_to_socket
21
+ case bind_uri.scheme
22
+ when 'tcp'
23
+ bind_to_tcp_socket
24
+ when 'unix'
25
+ bind_to_unix_socket
26
+ else
27
+ raise Uninterruptible::ConfigurationError, "Can only bind to TCP and UNIX sockets"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # Connect (or reconnect if the FD is set) to a TCP server
34
+ #
35
+ # @return [TCPServer] Socket server for the configured address and port
36
+ def bind_to_tcp_socket
37
+ if ENV[SERVER_FD_VAR]
38
+ TCPServer.for_fd(ENV[SERVER_FD_VAR].to_i)
39
+ else
40
+ TCPServer.new(bind_uri.host, bind_uri.port)
41
+ end
42
+ end
43
+
44
+ # Connect (or reconnect if FD is set) to a UNIX socket. Will delete existing socket at path if required.
45
+ #
46
+ # @return [UNIXServer] Socket server for the configured path
47
+ def bind_to_unix_socket
48
+ if ENV[SERVER_FD_VAR]
49
+ UNIXServer.for_fd(ENV[SERVER_FD_VAR].to_i)
50
+ else
51
+ File.delete(bind_uri.path) if File.exist?(bind_uri.path)
52
+ UNIXServer.new(bind_uri.path)
53
+ end
54
+ end
55
+
56
+ # Parse the bind address in the configuration
57
+ #
58
+ # @param [String] bind_address The config for a server we're returning the socket for
59
+ #
60
+ # @return [URI::Generic] Parsed version of the bind_address
61
+ #
62
+ # @raise [Uninterruptible::ConfigurationError] Raised if the bind_address could not be parsed
63
+ def parse_bind_address(bind_address)
64
+ URI.parse(bind_address)
65
+ rescue URI::Error
66
+ raise Uninterruptible::ConfigurationError, "Couldn't parse the bind address: \"#{bind_address}\""
67
+ end
68
+ end
69
+ end
@@ -3,7 +3,7 @@ module Uninterruptible
3
3
  #
4
4
  # See {Server#configure} for usage instructions.
5
5
  class Configuration
6
- attr_writer :bind_port, :bind_address, :pidfile_path, :start_command, :log_path, :log_level
6
+ attr_writer :bind, :bind_port, :bind_address, :pidfile_path, :start_command, :log_path, :log_level
7
7
 
8
8
  # Available TCP Port for the server to bind to (required). Falls back to environment variable PORT if set.
9
9
  #
@@ -14,9 +14,14 @@ module Uninterruptible
14
14
  port.to_i
15
15
  end
16
16
 
17
- # Address to bind the server to (defaults to +::+).
17
+ # Address to bind the server to (defaults to +0.0.0.0+).
18
18
  def bind_address
19
- @bind_address || "::"
19
+ @bind_address || "0.0.0.0"
20
+ end
21
+
22
+ # URI to bind to, falls back to tcp://bind_address:bind_port if unset
23
+ def bind
24
+ @bind || "tcp://#{bind_address}:#{bind_port}"
20
25
  end
21
26
 
22
27
  # Location to write the pid of the current server to. If blank pidfile will not be written. Falls back to
@@ -30,7 +30,7 @@ module Uninterruptible
30
30
  module Server
31
31
  def self.included(base)
32
32
  base.class_eval do
33
- attr_reader :active_connections, :tcp_server, :mutex
33
+ attr_reader :active_connections, :socket_server, :mutex
34
34
  end
35
35
  end
36
36
 
@@ -52,7 +52,7 @@ module Uninterruptible
52
52
 
53
53
  logger.info "Starting server on #{server_configuration.bind_address}:#{server_configuration.bind_port}"
54
54
 
55
- establish_tcp_server
55
+ establish_socket_server
56
56
  write_pidfile
57
57
  setup_signal_traps
58
58
  accept_connections
@@ -72,7 +72,7 @@ module Uninterruptible
72
72
  # use a different concurrency pattern, a thread per connection is the default.
73
73
  def accept_connections
74
74
  loop do
75
- Thread.start(tcp_server.accept) do |client_socket|
75
+ Thread.start(socket_server.accept) do |client_socket|
76
76
  logger.debug "Accepted connection from #{client_socket.peeraddr.last}"
77
77
  process_request(client_socket)
78
78
  end
@@ -93,23 +93,17 @@ module Uninterruptible
93
93
  end
94
94
  end
95
95
 
96
- # Listen (or reconnect) to the bind address and port specified in the config. If TCP_SERVER_FD is set in the env,
97
- # reconnect to that file descriptor. Once @tcp_server is set, write the file descriptor ID to the env.
98
- def establish_tcp_server
99
- if ENV['TCP_SERVER_FD']
100
- # If there's a file descriptor present, take over from a previous instance of this server and kill it off
101
- logger.debug "Reconnecting to file descriptor..."
102
- @tcp_server = TCPServer.for_fd(ENV['TCP_SERVER_FD'].to_i)
103
- kill_parent
104
- else
105
- logger.debug "Opening new socket..."
106
- @tcp_server = TCPServer.open(server_configuration.bind_address, server_configuration.bind_port)
107
- end
96
+ # Listen (or reconnect) to the bind address and port specified in the config. If socket_server_FD is set in the env,
97
+ # reconnect to that file descriptor. Once @socket_server is set, write the file descriptor ID to the env.
98
+ def establish_socket_server
99
+ @socket_server = Uninterruptible::Binder.new(server_configuration.bind).bind_to_socket
100
+ # If there's a file descriptor present, take over from a previous instance of this server and kill it off
101
+ kill_parent if ENV[SERVER_FD_VAR]
108
102
 
109
- @tcp_server.autoclose = false
110
- @tcp_server.close_on_exec = false
103
+ @socket_server.autoclose = false
104
+ @socket_server.close_on_exec = false
111
105
 
112
- ENV["TCP_SERVER_FD"] = @tcp_server.to_i.to_s
106
+ ENV[SERVER_FD_VAR] = @socket_server.to_i.to_s
113
107
  end
114
108
 
115
109
  # Send a TERM signal to the parent process. This will be called by a newly spawned server if it has been started
@@ -143,9 +137,9 @@ module Uninterruptible
143
137
  end
144
138
  end
145
139
 
146
- # Stop listening on tcp_server, wait until all active connections have finished processing and exit with 0.
140
+ # Stop listening on socket_server, wait until all active connections have finished processing and exit with 0.
147
141
  def graceful_shutdown
148
- tcp_server.close unless tcp_server.closed?
142
+ socket_server.close unless socket_server.closed?
149
143
 
150
144
  until active_connections.zero?
151
145
  sleep 0.5
@@ -1,3 +1,3 @@
1
1
  module Uninterruptible
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
Binary file
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Dan Wentworth"]
10
10
  spec.email = ["dan@atechmedia.com"]
11
11
 
12
- spec.summary = "Zero-downtime restarts for your trivial TCP servers"
12
+ spec.summary = "Zero-downtime restarts for your trivial socket servers"
13
13
  spec.description = "Uninterruptible gives your socket server magic restarting powers. Send your running "\
14
14
  "Uninterruptible server USR1 and it will start a brand new copy of itself which will immediately start handling "\
15
15
  "new requests while the old server stays alive until all of it's active connections are complete."
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uninterruptible
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Wentworth
@@ -71,9 +71,11 @@ files:
71
71
  - bin/console
72
72
  - bin/setup
73
73
  - lib/uninterruptible.rb
74
+ - lib/uninterruptible/binder.rb
74
75
  - lib/uninterruptible/configuration.rb
75
76
  - lib/uninterruptible/server.rb
76
77
  - lib/uninterruptible/version.rb
78
+ - uninterruptible-1.0.0.gem
77
79
  - uninterruptible.gemspec
78
80
  homepage: https://github.com/darkphnx/uninterruptible
79
81
  licenses:
@@ -98,5 +100,5 @@ rubyforge_project:
98
100
  rubygems_version: 2.5.1
99
101
  signing_key:
100
102
  specification_version: 4
101
- summary: Zero-downtime restarts for your trivial TCP servers
103
+ summary: Zero-downtime restarts for your trivial socket servers
102
104
  test_files: []