{"id":21216064,"url":"https://github.com/krystal/uninterruptible","last_synced_at":"2025-07-10T11:32:08.999Z","repository":{"id":59158638,"uuid":"85065249","full_name":"krystal/uninterruptible","owner":"krystal","description":"Zero-downtime restarts for your trivial socket servers","archived":false,"fork":false,"pushed_at":"2020-01-16T15:04:55.000Z","size":85,"stargazers_count":38,"open_issues_count":0,"forks_count":3,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-06-09T17:26:11.785Z","etag":null,"topics":["devops","ruby","ruby-gem","socket-server","tcp-server","unix-socket"],"latest_commit_sha":null,"homepage":"https://atech.blog/atech/seamless-socket-server-restarts","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/krystal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-15T11:38:02.000Z","updated_at":"2024-09-25T05:50:00.000Z","dependencies_parsed_at":"2022-09-13T20:11:15.722Z","dependency_job_id":null,"html_url":"https://github.com/krystal/uninterruptible","commit_stats":null,"previous_names":["darkphnx/uninterruptible"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/krystal/uninterruptible","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Funinterruptible","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Funinterruptible/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Funinterruptible/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Funinterruptible/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krystal","download_url":"https://codeload.github.com/krystal/uninterruptible/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Funinterruptible/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264573087,"owners_count":23630415,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devops","ruby","ruby-gem","socket-server","tcp-server","unix-socket"],"created_at":"2024-11-20T21:49:33.115Z","updated_at":"2025-07-10T11:32:08.682Z","avatar_url":"https://github.com/krystal.png","language":"Ruby","readme":"# Uninterruptible\n\nUninterruptible gives you zero downtime restarts for your socket servers with nearly zero effort. Sounds good? Read on.\n\nSmall socket servers are great, sometimes you need a quick and efficient way of moving data between servers (or even\nprocesses on the same machine). Restarting these processes can be a bit hairy though, you either need to build your\nclients smart enough to keep trying to connect, potentially backing up traffic or you just leave your server and\nhope for the best.\n\nYou _know_ that you'll need to restart it one day and cross your fingers that you can kill the old one and start the\nnew one before anyone notices. Not ideal at all.\n\n![Just a quick switch](http://i.imgur.com/aFyJJM6.jpg)\n\nUninterruptible gives your socket server magic restarting powers. Send your running Uninterruptible server USR1 and\nit will start a brand new copy of itself which will immediately start handling new requests while the old server stays\nalive until all of it's active connections are complete.\n\n## Basic Usage\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'uninterruptible'\n```\n\nTo build your server all you need to do is include `Uninterruptible::Server` and implement `handle_request`. Let's build\na simple echo server:\n\n```ruby\n# echo_server.rb\nclass EchoServer\n  include Uninterruptible::Server\n\n  def handle_request(client_socket)\n    received_data = client_socket.gets\n    client_socket.puts(received_data)\n  end\nend\n```\n\nTo turn this into a running server you only need to configure a port to listen on and the command used to start the\nserver and call `run`:\n\n```ruby\necho_server = EchoServer.new\necho_server.configure do |config|\n  config.bind_port = 6789\n  config.start_command = 'ruby echo_server.rb'\nend\necho_server.run\n```\n\nTo restart the server just send `USR1`, a new server will start listening on your port, the old one will quit once it's\nfinished processing all of it's existing connections. To kill the server (allowing for all connections to finish) call\n`TERM`.\n\n## Configuration Options\n\n```ruby\necho_server.configure do |config|\n  config.start_command = 'ruby echo_server.rb' # *Required* Command to execute to start a new server process\n  config.bind = \"tcp://0.0.0.0:12345\" # *Required* Interface to listen on, falls back to 0.0.0.0 on ENV['PORT']\n  config.pidfile_path = 'tmp/pids/echoserver.pid' # Location to write a pidfile, falls back to ENV['PID_FILE']\n  config.log_path = 'log/echoserver.log' # Location to write logfile, defaults to STDOUT\n  config.log_level = Logger::INFO # Log writing severity, defaults to Logger::INFO\n  config.tls_version = 'TLSv1_2' # TLS version to use, defaults to TLSv1_2, falls back to ENV['TLS_VERSION']\n  config.tls_key = nil # Private key to use for TLS, reads file from ENV['TLS_KEY'] if set\n  config.tls_certificate = nil # Certificate to use for TLS, reads file from ENV['TLS_CERTIFICATE'] if set\n  config.verify_client_tls_certificate = false # Should client TLS certificates be required and verifiyed? Falls back to ENV['VERIFY_CLIENT_TLS_CERTIFICATE']\n  config.client_tls_certificate_ca = nil # Path to a trusted CA for client certificates. Implies `config.verify_client_tls_certificate = true`. Falls back to ENV['CLIENT_TLS_CERTIFICATE_CA']\n  config.allowed_networks = ['127.0.0.1/8', '2001:db8::/32'] # A list of networks that clients are allowed to connect from. If blank, all networks are allowed. Falls back to a comma-separated list from ENV['ALLOWED_NETWORKS']\nend\n```\n\nUninterruptible supports both TCP and UNIX sockets. To connect to a unix socket simply pass the path in the bind\nconfiguration parameter:\n\n```ruby\necho_server.configure do |config|\n  config.bind = \"unix:///tmp/echo_server.sock\"\nend\n```\n\n## The Magic\n\nUpon receiving `USR1`, your server will spawn a new copy of itself and pass the file descriptor of the open socket to\nthe new server. The new server attaches itself to the file descriptor then sends a `TERM` signal to the original\nprocess. The original server stops listening on the socket and shuts itself down once all ongoing requests have\ncompleted.\n\n![Restart Flow](http://i.imgur.com/k8uNP55.png)\n\n## Concurrency\n\nBy default, Uninterruptible operates on a very simple one thread per connection concurrency model. If you'd like to use\nsomething more advanced such as a threadpool or an event driven pattern you can define this in your server class.\n\nBy overriding `accept_client_connection` you can change how connections are accepted and handled. It is recommended\nthat you call `process_request` from this method and implement `handle_request` to do the bulk of the work since\n`process_request` tracks the number of active connections to the server and handles network restrictions.\n\n`accept_client_connection` is called whenever a connection is waiting to be accepted on the socket server.\n\nIf you wanted to implement a threadpool to process your requests you could do the following:\n\n```ruby\nclass EchoServer\n  # ...\n\n  def accept_client_connection\n    @worker_threads ||= 4.times.map do\n      Thread.new { worker_loop }\n    end\n\n    threads.each(\u0026:join)\n  end\n\n  def worker_loop\n    loop do\n      client_socket = socket_server.accept\n      process_request(client_socket)\n    end\n  end\nend\n```\n\n## TLS Support\n\nIf you would like to encrypt your TCP socket, Uninterruptible supports TLSv1.1 and TLSv1.2. Simply set `configuration.tls_key` and `configuration.tls_certificate` (see \"Configuration\" above) and your TCP socket will automatically be wrapped with TLS.\n\nTo generate a key, run a command similar to the following:\n\n```sh\nopenssl req -newkey rsa:4096 -nodes -sha512 -x509 -days 3650 -nodes -out tls_cert.pem -keyout tls_key.pem\n```\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/darkphnx/uninterruptible.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrystal%2Funinterruptible","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrystal%2Funinterruptible","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrystal%2Funinterruptible/lists"}