{"id":13491278,"url":"https://github.com/mtrudel/thousand_island","last_synced_at":"2025-05-13T20:22:32.353Z","repository":{"id":36458958,"uuid":"220247931","full_name":"mtrudel/thousand_island","owner":"mtrudel","description":"Thousand Island is a pure Elixir socket server","archived":false,"fork":false,"pushed_at":"2025-04-29T17:51:41.000Z","size":27156,"stargazers_count":885,"open_issues_count":0,"forks_count":54,"subscribers_count":18,"default_branch":"main","last_synced_at":"2025-05-10T03:14:41.973Z","etag":null,"topics":["elixir","high-performance","socket-server","tcp-server","tls"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/mtrudel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-11-07T13:50:35.000Z","updated_at":"2025-05-09T20:13:38.000Z","dependencies_parsed_at":"2024-02-23T21:30:42.378Z","dependency_job_id":"3c247a5d-7bc9-4313-b1e6-1359f3fa568c","html_url":"https://github.com/mtrudel/thousand_island","commit_stats":{"total_commits":344,"total_committers":22,"mean_commits":"15.636363636363637","dds":"0.26453488372093026","last_synced_commit":"8e66e81436211aa8a529c9c71afa9e9863f586a4"},"previous_names":[],"tags_count":66,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fthousand_island","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fthousand_island/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fthousand_island/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtrudel%2Fthousand_island/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtrudel","download_url":"https://codeload.github.com/mtrudel/thousand_island/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253356281,"owners_count":21895674,"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":["elixir","high-performance","socket-server","tcp-server","tls"],"created_at":"2024-07-31T19:00:55.182Z","updated_at":"2025-05-13T20:22:27.344Z","avatar_url":"https://github.com/mtrudel.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"![Thousand Island](https://github.com/mtrudel/thousand_island/raw/main/assets/readme_logo.png#gh-light-mode-only)\n![Thousand Island](https://github.com/mtrudel/thousand_island/raw/main/assets/readme_logo-white.png#gh-dark-mode-only)\n\n[![Build Status](https://github.com/mtrudel/thousand_island/workflows/Elixir%20CI/badge.svg)](https://github.com/mtrudel/thousand_island/actions)\n[![Docs](https://img.shields.io/badge/api-docs-green.svg?style=flat)](https://hexdocs.pm/thousand_island)\n[![Hex.pm](https://img.shields.io/hexpm/v/thousand_island.svg?style=flat\u0026color=blue)](https://hex.pm/packages/thousand_island)\n\nThousand Island is a modern, pure Elixir socket server, inspired heavily by\n[ranch](https://github.com/ninenines/ranch). It aims to be easy to understand\nand reason about, while also being at least as stable and performant as alternatives.\nInformal tests place ranch and Thousand Island at roughly the same level of\nperformance and overhead; short of synthetic scenarios on the busiest of servers,\nthey perform equally for all intents and purposes.\n\nThousand Island is written entirely in Elixir, and is nearly dependency-free (the\nonly library used is [telemetry](https://github.com/beam-telemetry/telemetry)).\nThe application strongly embraces OTP design principles, and emphasizes readable,\nsimple code. The hope is that as much as Thousand Island is capable of backing\nthe most demanding of services, it is also useful as a simple and approachable\nreference for idiomatic OTP design patterns.\n\n## Usage\n\nThousand Island is implemented as a supervision tree which is intended to be hosted\ninside a host application, often as a dependency embedded within a higher-level\nprotocol library such as [Bandit](https://github.com/mtrudel/bandit). Aside from\nsupervising the Thousand Island process tree, applications interact with Thousand\nIsland primarily via the\n[`ThousandIsland.Handler`](https://hexdocs.pm/thousand_island/ThousandIsland.Handler.html) behaviour.\n\n### Handlers\n\nThe [`ThousandIsland.Handler`](https://hexdocs.pm/thousand_island/ThousandIsland.Handler.html) behaviour defines the interface that Thousand Island\nuses to pass [`ThousandIsland.Socket`](https://hexdocs.pm/thousand_island/ThousandIsland.Socket.html)s up to the application level; together they\nform the primary interface that most applications will have with Thousand Island.\nThousand Island comes with a few simple protocol handlers to serve as examples;\nthese can be found in the [examples](https://github.com/mtrudel/thousand_island/tree/main/examples)\nfolder of this project. A simple implementation would look like this:\n\n```elixir\ndefmodule Echo do\n  use ThousandIsland.Handler\n\n  @impl ThousandIsland.Handler\n  def handle_data(data, socket, state) do\n    ThousandIsland.Socket.send(socket, data)\n    {:continue, state}\n  end\nend\n\n{:ok, pid} = ThousandIsland.start_link(port: 1234, handler_module: Echo)\n```\n\nFor more information, please consult the [`ThousandIsland.Handler`](https://hexdocs.pm/thousand_island/ThousandIsland.Handler.html) documentation.\n\n### Starting a Thousand Island Server\n\nThousand Island servers exist as a supervision tree, and are started by a call\nto\n[`ThousandIsland.start_link/1`](https://hexdocs.pm/thousand_island/ThousandIsland.html#start_link/1).\nThere are a number of options supported; for a complete description, consult the\n[Thousand Island\ndocs](https://hexdocs.pm/thousand_island/ThousandIsland.html#t:options/0).\n\n### Connection Draining \u0026 Shutdown\n\nThe `ThousandIsland.Server` process is just a standard `Supervisor`, so all the\nusual rules regarding shutdown and shutdown timeouts apply. Immediately upon\nbeginning the shutdown sequence the `ThousandIsland.ShutdownListener` will cause\nthe listening socket to shut down, which in turn will cause all of the `Acceptor`\nprocesses to shut down as well. At this point all that is left in the supervision\ntree are several layers of Supervisors and whatever `Handler` processes were\nin progress when shutdown was initiated. At this point, standard Supervisor shutdown\ntimeout semantics give existing connections a chance to finish things up. `Handler`\nprocesses trap exit, so they continue running beyond shutdown until they either\ncomplete or are `:brutal_kill`ed after their shutdown timeout expires.\n\nThe `shutdown_timeout` configuration option allows for fine grained control of\nthe shutdown timeout value. It defaults to 15000 ms.\n\n### Logging \u0026 Telemetry\n\nAs a low-level library, Thousand Island purposely does not do any inline\nlogging of any kind. The [`ThousandIsland.Logger`](https://hexdocs.pm/thousand_island/ThousandIsland.Logger.html) module defines a number of\nfunctions to aid in tracing connections at various log levels, and such logging\ncan be dynamically enabled and disabled against an already running server. This\nlogging is backed by telemetry events internally.\n\nThousand Island emits a rich set of telemetry events including spans for each\nserver, acceptor process, and individual client connection. These telemetry\nevents are documented in the [`ThousandIsland.Telemetry`](https://hexdocs.pm/thousand_island/ThousandIsland.Telemetry.html) module.\n\n## Implementation Notes\n\nAt a top-level, a `Server` coordinates the processes involved in responding to\nconnections on a socket. A `Server` manages two top-level processes: a `Listener`\nwhich is responsible for actually binding to the port and managing the resultant\nlistener socket, and an `AcceptorPoolSupervisor` which is responsible for managing\na pool of `AcceptorSupervisor` processes.\n\nEach `AcceptorSupervisor` process (there are 100 by default) manages two processes:\nan `Acceptor` which accepts connections made to the server's listener socket,\nand a `DynamicSupervisor` which supervises the processes backing individual\nclient connections. Every time a client connects to the server's port, one of\nthe `Acceptor`s receives the connection in the form of a socket. It then creates\na new process based on the configured handler to manage this connection, and\nimmediately waits for another connection. It is worth noting that `Acceptor`\nprocesses are long-lived, and normally live for the entire period that the\n`Server` is running.\n\nA handler process is tied to the lifecycle of a client connection, and is\nonly started when a client connects. The length of its lifetime beyond that of the\nunderlying connection is dependent on the behaviour of the configured Handler module.\nIn typical cases its lifetime is directly related to that of the underlying connection.\n\nThis hierarchical approach reduces the time connections spend waiting to be accepted,\nand also reduces contention for `DynamicSupervisor` access when creating new\n`Handler` processes. Each `AcceptorSupervisor` subtree functions nearly\nautonomously, improving scalability and crash resiliency.\n\nGraphically, this shakes out like so:\n\n```mermaid\ngraph TD;\n  Server(Server: supervisor, rest_for_one)--\u003eListener;\n  Server--\u003eAcceptorPoolSupervisor(AcceptorPoolSupervisor: dynamic supervisor);\n  AcceptorPoolSupervisor--1...n--\u003eAcceptorSupervisor(AcceptorSupervisor: supervisor, rest_for_one)\n  AcceptorSupervisor--\u003eDynamicSupervisor\n  AcceptorSupervisor--\u003eAcceptor(Acceptor: task)\n  DynamicSupervisor--1...n--\u003eHandler(Handler: gen_server)\n  Server--\u003eShutdownListener;\n```\n\nThousand Island does not use named processes or other 'global' state internally\n(other than telemetry event names). It is completely supported for a single node\nto host any number of `Server` processes each listening on a different port.\n\n## Contributing\n\nContributions to Thousand Island are very much welcome! Before undertaking any substantial work, please\nopen an issue on the project to discuss ideas and planned approaches so we can ensure we keep\nprogress moving in the same direction.\n\nAll contributors must agree and adhere to the project's [Code of\nConduct](https://github.com/mtrudel/thousand_island/blob/main/CODE_OF_CONDUCT.md).\n\nSecurity disclosures should be handled per Thousand Island's published [security policy](https://github.com/mtrudel/thousand_island/blob/main/SECURITY.md).\n\n## Installation\n\nThousand Island is [available in Hex](https://hex.pm/packages/thousand_island). The package\ncan be installed by adding `thousand_island` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:thousand_island, \"~\u003e 1.0\"}\n  ]\nend\n```\n\nDocumentation can be found at [https://hexdocs.pm/thousand_island](https://hexdocs.pm/thousand_island).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtrudel%2Fthousand_island","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtrudel%2Fthousand_island","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtrudel%2Fthousand_island/lists"}