{"id":25085575,"url":"https://github.com/erleans/pgo","last_synced_at":"2025-05-16T13:03:26.906Z","repository":{"id":14361401,"uuid":"59958463","full_name":"erleans/pgo","owner":"erleans","description":"Erlang Postgres client and connection pool","archived":false,"fork":false,"pushed_at":"2025-01-21T21:36:16.000Z","size":321,"stargazers_count":85,"open_issues_count":31,"forks_count":17,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-05-13T18:13:39.076Z","etag":null,"topics":["erlang","postgres-client"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/erleans.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-05-29T18:43:23.000Z","updated_at":"2025-04-26T07:17:25.000Z","dependencies_parsed_at":"2024-11-13T12:18:12.364Z","dependency_job_id":"bebcafff-b5ed-40f2-9305-f1fcafc57e5a","html_url":"https://github.com/erleans/pgo","commit_stats":{"total_commits":120,"total_committers":9,"mean_commits":"13.333333333333334","dds":0.09166666666666667,"last_synced_commit":"d1989c44f41ccf1b4e670ccafe46d43f71cb6dfc"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erleans%2Fpgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erleans%2Fpgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erleans%2Fpgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erleans%2Fpgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erleans","download_url":"https://codeload.github.com/erleans/pgo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254535826,"owners_count":22087398,"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":["erlang","postgres-client"],"created_at":"2025-02-07T08:20:26.226Z","updated_at":"2025-05-16T13:03:26.875Z","avatar_url":"https://github.com/erleans.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PGO\n\n[![Tests](https://github.com/erleans/pgo/actions/workflows/ct.yml/badge.svg)](https://github.com/erleans/pgo/actions/workflows/ct.yml)\n[![codecov](https://codecov.io/gh/erleans/pgo/branch/main/graph/badge.svg)](https://codecov.io/gh/erleans/pgo)\n[![Hex.pm](https://img.shields.io/hexpm/v/pgo.svg?style=flat)](https://hex.pm/packages/pgo)\n\nPG...Oh god not nother Postgres client in Erlang...\n\n## Why\n\n* No message passing. Clients checkout the socket and use it directly.\n* Binary protocol with input oids cached.\n* Simple and direct. Tries to limit runtime options as much as possible.\n* Instrumented with [Telemetry](https://github.com/beam-telemetry/telemetry) and [OpenCensus](https://github.com/census-instrumentation/opencensus-erlang)\n* Mix apps currently too hard to use in a Rebar3 project. \n\n## Requirements\n\nErlang/OTP 21.3 and above.\n\n## Use\n\nPools defined in the `pgo` application's environment will be started on boot. You can also add pools dynamically with `pgo:start_pool/3`.\n\nTo try `pgo` simply modify `config/example.config` by replacing the `host`, `database`, `user` and `password` values for the database you wish to connect to:\n\n```erlang\n[\n  {pgo, [{pools, [{default, #{pool_size =\u003e 10,\n                              host =\u003e \"127.0.0.1\",\n                              database =\u003e \"test\",\n                              user =\u003e \"test\"}}]}]}\n].\n```\n\n`default` is the name of the pool, `size` is the number of connections to create for the pool. Or you can start the pool through `pgo:start_pool/2` which creates it as a child of `pgo`'s simple one for one:\n\n``` erlang\n\u003e application:ensure_all_started(pgo).\n{ok,[backoff,opentelemetry_api,pg_types,pgo]}\n\u003e pgo:start_pool(default, #{pool_size =\u003e 5, host =\u003e \"127.0.0.1\", database =\u003e \"test\", user =\u003e \"test\"}). \n```\n\nOr start a pool as a child of your application's supervisor:\n\n``` erlang\nChildSpec = #{id =\u003e pgo_pool,\n              start =\u003e {pgo_pool, start_link, [Name, PoolConfig]},\n              shutdown =\u003e 1000},\n```\n\nThen start a shell with `rebar3 shell`, it will boot the applications which will start the pool automatically if it is configured through `sys.config`.\n\n```erlang\n\u003e pgo:query(\"select 1\").\n#{command =\u003e select, num_rows =\u003e 1, rows =\u003e [{1}]}\n\u003e pgo:transaction(fun() -\u003e\n\u003e     pgo:query(\"INSERT INTO my_table(name) VALUES('Name 1')\"),\n\u003e     pgo:query(\"INSERT INTO my_table(name) VALUES('Name 2')\")\n\u003e end).\n#{command =\u003e insert,num_rows =\u003e 1,rows =\u003e []}\n```\n\n## Options\n\nPool configuration includes the Postgres connection information, pool configuration like size and defaults for options used at query time. \n\n``` erlang\n#{host =\u003e string(),\n  port =\u003e integer(),\n  user =\u003e string(),\n  password =\u003e string(),\n  database =\u003e string(),\n\n  %% pool specific settings\n  pool_size =\u003e integer(),\n  queue_target =\u003e integer(),\n  queue_interval =\u003e integer(),\n  idle_interval =\u003e integer(),\n\n  %% gen_tcp socket options\n  socket_options =\u003e [gen_tcp:socket_option()],\n\n  %% defaults for options used at query time\n  queue =\u003e boolean(),\n  trace =\u003e boolean(),\n  decode_opts =\u003e [decode_option()]}\n```\n\nThe query time options can also be set through options passed to `pgo:query/3`:\n\n``` erlang\ndecode_fun() :: fun((row(), fields()) -\u003e row()) | undefined.\n\ndecode_option() :: return_rows_as_maps | {return_rows_as_maps, boolean()} |\n                   column_name_as_atom | {column_name_as_atom, boolean()} |\n                   {decode_fun, decode_fun()}.\n                         \n#{pool =\u003e atom(),\n  trace =\u003e boolean(),\n  queue =\u003e boolean(),\n  decode_opts =\u003e [decode_option()]}\n```\n\n### Query Options\n\n* `pool` (default: `default`): Name of the pool to use for checking out a connection to the database.\n* `return_rows_as_maps` (default: `false`): When `true` each row is returned as a map of column name to value instead of a list of values.\n* `column_name_as_atom` (default: `false`): If `true` converts each column name in the result to an atom.\n* `decode_fun` (default: `undefined`): Optional function for performing transformations on each row in a result. It must be a 2-arity function returning a list or map for the row and takes the row (as a list or map) and a list of `#row_description_field{}` records.\n* `queue` (default: `true`): Whether to wait for a connection from the pool if none are available.\n* `trace` (default: `false`): `pgo` is instrumented with [OpenCensus](https://opencensus.io/) and when this option is `true` a span will be created (if sampled).\n\n### Database Settings\n\n* `host` (default: `127.0.0.1`): Database server hostname.\n* `port` (default: 5432): Port the server is listening on.\n* `user`: Username to connect to database as.\n* `password`: Password for the user.\n* `database`: Name of database to use.\n* `ssl` (default: `false`): Whether to use SSL or not.\n* `ssl_options`: List of SSL options to use if `ssl` is `true`. See the [Erlang SSL connect](http://erlang.org/doc/man/ssl.html#connect-2) options.\n* `connection_parameters` (default: `[]`): List of 2-tuples, where key and value must be binary strings. You can include any Postgres connection parameter here, such as `{\u003c\u003c\"application_name\"\u003e\u003e, \u003c\u003c\"myappname\"\u003e\u003e}` and `{\u003c\u003c\"timezone\"\u003e\u003e, \u003c\u003c\"GMT\"\u003e\u003e}`.\n\n### Pool Settings\n\n* `pool_size` (default: 1): Number of connections to keep open with the database\n* `queue_target` (default: 50) and `queue_interval` (default: 1000): Checking out connections is handled through a queue. If it takes longer than `queue_target` to get out of the queue for longer than `queue_interval` then the `queue_target` will be doubled and checkouts will start to be dropped if that target is surpassed.\n* `idle_interval` (default: 1000): The database is pinged every `idle_interval` when the connection is idle.\n\n### Erlang TCP Socket Settings\n\n* `socket_options` (default `[]`): Addition options to pass to `gen_tcp:connect` such as `inet6` for IPv6 support.\n\n## Telemetry and Tracing\n\nA [Telemetry](https://github.com/beam-telemetry/telemetry) event `[pgo, query]` can be attached to for receiving the time a query takes as well as other metadata for each query.\n\n[OpenCensus](https://opencensus.io/) spans can be enabled for queries and transactions by either setting the `trace_default` to `true` for the pool:\n\n``` erlang\n\u003e pgo:start_pool(default, #{host =\u003e \"127.0.0.1\", \n                            database =\u003e \"test\", \n                            user =\u003e \"test\",\n                            pool_size =\u003e 5,\n                            trace_default =\u003e true}]). \n```\n\nOr by passing `#{trace =\u003e true}` in the options for a query or transaction:\n\n```erlang\n\u003e pgo:query(\"select 1\", [], #{trace =\u003e true}).\n#{command =\u003e select, num_rows =\u003e 1, rows =\u003e [{1}]}\n\u003e pgo:transaction(fun() -\u003e\n\u003e     pgo:query(\"INSERT INTO my_table(name) VALUES('Name 1')\"),\n\u003e     pgo:query(\"INSERT INTO my_table(name) VALUES('Name 2')\")\n\u003e end, #{trace =\u003e true}).\n#{command =\u003e insert,num_rows =\u003e 1,rows =\u003e []}\n```\n\nNote that since this is optional the `opencensus` application is not included as a dependency of `pgo`. So it must be included as a `rebar3` dependency and runtime dependency (listed in your application's `.app.src` `applications` or the list of applications for `relx` to include in a release).\n\n## Running Tests\n\nPool functionality is tested with common test suites:\n\n```\n$ rebar3 ct\n```\n\nPostgres query functionality is tested with eunit, create user `test` and database `test`:\n\n```\n$ rebar3 eunit\n```\n\n## Acknowledgements\n\nMuch is owed to https://github.com/semiocast/pgsql (especially for protocol step logic) and https://github.com/epgsql/epgsql/ (especially for some decoding logic).\n\nThe pool implementation is owed to James Fish's found in `db_connection` [PR 108](https://github.com/elixir-ecto/db_connection/pull/108). While [db_connection](https://github.com/elixir-ecto/db_connection) and [postgrex](https://github.com/elixir-ecto/postgrex) as a whole were both used as inspiration as well.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferleans%2Fpgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferleans%2Fpgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferleans%2Fpgo/lists"}