{"action":"created","issue":{"url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/issues/32","repository_url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir","labels_url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/issues/32/labels{/name}","comments_url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/issues/32/comments","events_url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/issues/32/events","html_url":"https://github.com/hamiltop/rethinkdb-elixir/issues/32","id":73832237,"number":32,"title":"Connection Pooling","user":{"login":"hamiltop","id":354848,"avatar_url":"https://avatars.githubusercontent.com/u/354848?v=3","gravatar_id":"","url":"https://api.github.com/users/hamiltop","html_url":"https://github.com/hamiltop","followers_url":"https://api.github.com/users/hamiltop/followers","following_url":"https://api.github.com/users/hamiltop/following{/other_user}","gists_url":"https://api.github.com/users/hamiltop/gists{/gist_id}","starred_url":"https://api.github.com/users/hamiltop/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hamiltop/subscriptions","organizations_url":"https://api.github.com/users/hamiltop/orgs","repos_url":"https://api.github.com/users/hamiltop/repos","events_url":"https://api.github.com/users/hamiltop/events{/privacy}","received_events_url":"https://api.github.com/users/hamiltop/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/labels/Connection","name":"Connection","color":"fad8c7"},{"url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/labels/feedback%20wanted","name":"feedback wanted","color":"bfe5bf"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":32,"created_at":"2015-05-07T04:15:14Z","updated_at":"2016-07-19T17:52:39Z","closed_at":null,"body":"### Poolboy\r\n\r\nPoolboy works by providing exclusive access to a given resource in a \"transaction\".\r\n\r\nPros:\r\n* Dynamic connection allocation is taken care of for you.\r\n\r\nCons:\r\n* Connection affinity is required for cursors, which means a transaction may be required for the full length of the cursor. For long running change feeds, this will require one connection per changefeed.\r\n* Pipelining is not utilized as each connection is checked out until a response is provided.\r\n\r\nWhile my assumption was to use Poolboy, it seems that there are better approaches available. There are too many tradeoffs to be made for not a whole lot of benefit.\r\n\r\n### Hand rolled\r\n\r\nAn ideal connection pool will do the following:\r\n\r\n1. Connect to N clients in a cluster.\r\n2. Load balance queries between connections.\r\n3. Route query to master replica for the target table.\r\n4. If a connection dies, remove that connection from the rotation.\r\n5. If a query fails because of a server connectivity issue, retry query on another host.\r\n\r\nInitial design:\r\n\r\nA supervisor tree will be over N connections and a coordinator.\r\n\r\nClient processes send queries directly to the connection, just like we currently do. In order to know which connection to contact, the client can request a connection from the coordinator. When the client is finished with the query it informs the coordinator (in order for the coordinator to properly balance connections).\r\n\r\nThis would require zero changes to `Exrethinkdb.Connection`. The connection would be dumb to pooling. We could provide a `Exrethinkdb.ConnectionPool` module with `run`/`next`/`close` that wrap the logic for interacting with the coordinator. The end API for the user would be the same. We will also provide a `use Exrethinkdb.ConnectionPool` macro. Ideally you would replace `use Exrethinkdb.Connection` with `use Exrethinkdb.ConnectionPool` and your application would still work perfectly.\r\n\r\nRouting queries to proper replicas can be done fairly well in the coordinator by looking at `table(\"foo_table\") |> config |> run`. The coordinator can do this on a timer (once every minute is probably sufficient). It can then use this information to route the connection properly. In the event of stale_reads, the coordinator can load balance between non-master connections. We'll have to add `table` to the `Query` struct, but that's pretty straightforward.\r\n\r\n`Exrethinkdb.ConnectionPool.run` will report back to the coordinator the success or failure of a query.  The coordinator will also be monitoring the Connection processes. There will also be logic to retry on a different server if a failure occurs (in the event of stale_reads. otherwise, retrying won't be useful until a new master is selected).\r\n\r\nI think this custom approach will be fairly simple. It will be designed so that failure leads to a worst case scenario of routing requests to the less optimal replica."},"comment":{"url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/issues/comments/233712978","html_url":"https://github.com/hamiltop/rethinkdb-elixir/issues/32#issuecomment-233712978","issue_url":"https://api.github.com/repos/hamiltop/rethinkdb-elixir/issues/32","id":233712978,"user":{"login":"almightycouch","id":7694587,"avatar_url":"https://avatars.githubusercontent.com/u/7694587?v=3","gravatar_id":"","url":"https://api.github.com/users/almightycouch","html_url":"https://github.com/almightycouch","followers_url":"https://api.github.com/users/almightycouch/followers","following_url":"https://api.github.com/users/almightycouch/following{/other_user}","gists_url":"https://api.github.com/users/almightycouch/gists{/gist_id}","starred_url":"https://api.github.com/users/almightycouch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/almightycouch/subscriptions","organizations_url":"https://api.github.com/users/almightycouch/orgs","repos_url":"https://api.github.com/users/almightycouch/repos","events_url":"https://api.github.com/users/almightycouch/events{/privacy}","received_events_url":"https://api.github.com/users/almightycouch/received_events","type":"User","site_admin":false},"created_at":"2016-07-19T17:52:39Z","updated_at":"2016-07-19T17:52:39Z","body":"> Is your fork ready for a discussion of merging into the mainline?\r\n\r\nHi @hamiltop, The fork is ready to be merged [without conflics](https://github.com/hamiltop/rethinkdb-elixir/compare/master...almightycouch:master).\r\n\r\nAll the tests are passing successfully with the exception of 5 tests in `test/connection_test.exs` which must be rewritten to work with the `DBConnection` behaviour. Some new test should also be written to test the connection pools.\r\n\r\nThere are a few implementation details that should be discussed thought:\r\n\r\n* The connection logic is implemented in the `Multiplexer` module.\r\n  * Each connection has two processes, for sending and receiving (see [`recv_loop/2`](https://github.com/almightycouch/rethinkdb-elixir/blob/master/lib/rethinkdb/connection/multiplexer.ex#L109)).\r\n  * Calling [`send_recv/3`](https://github.com/almightycouch/rethinkdb-elixir/blob/master/lib/rethinkdb/connection/multiplexer.ex#L42) function blocks until the response is received (or connection timeout) without blocking the connection. This is done using `GenServer.call/3` and `GenServer.reply/3`.\r\n  * Only the latest [authentication method](https://rethinkdb.com/blog/2.3-release/) (server >= 2.3) has been implemented. Older authentication methods must be implemented if required.\r\n* To use a specific pool backend, pass the module to the `:pool` option:\r\n  * Within a supervision tree: `supervisor(MyApp.Repo, [[pool: DBConnection.Poolboy]])`\r\n  * For available options, check: [`DBConnection.start_link/2`](https://hexdocs.pm/db_connection/DBConnection.html#start_link/2).\r\n  * Default is `DBConnection.Connection`, `:poolboy` and `:sbroker` are optional dependencies."}}
{"actor":{"display_login":"almightycouch"}}