Configuration

View Source

Every option minato takes, what it defaults to, and why.

A connection

Passed as connection in a pool or listener, or to minato_conn:connect/1 directly.

optiondefault
userrequiredthe role to authenticate as
passwordnonea binary, or a fun(() -> binary()) called once per attempt and never stored
databasethe useras PostgreSQL itself defaults
host"localhost"
port5432
parameterssee belowadded to the StartupMessage
sslfalse
ssl_optionsverify against the OS trust storeeach option replaces one default
channel_bindingprefer under TLS, disable withoutrequire refuses to connect without it
authevery method[scram_sha_256] to accept nothing else
connect_timeout5000covers the TCP connect and the TLS handshake
timeout15000the per read timeout, and the default statement deadline
cancel_timeout5000how long a cancelled statement has to acknowledge
prepared_statements64how many statements a connection keeps parsed; 0 disables
socket_optionsbinary, {active,false}, {packet,raw}, {nodelay,true}each replaces one default
frame_opts#{max_message_length => 67108864}raise it only for a single value near PostgreSQL's 1 GB limit

Two startup parameters are sent unless parameters overrides them: client_encoding is UTF8, because the codecs decode text as UTF-8 and a server sending LATIN1 would put mojibake in a binary rather than raise; DateStyle is ISO, MDY, because the text format for dates is only unambiguous under ISO. Set application_name here - it is what pg_stat_activity shows, and it is the difference between finding the query that is hurting and guessing.

A pool

optiondefault
connectionrequiredthe map above
size10connections kept
max_ageinfinityretire a connection checked in older than this

size is not a throughput dial. A pool larger than the database can serve moves the queue from your application to the server, where you cannot see it; a pool smaller than your concurrency makes callers wait, which [minato, checkout, stop] measures exactly.

max_age is infinity because a connection to PostgreSQL is good indefinitely, and churning connections is work nobody asked for. Set it when something sits in the middle - a proxy, a load balancer, a NAT - since those have limits of their own and a connection one of them cut is otherwise found by a query failing on it.

A listener

{ok, _Pid} = minato:start_listener(events, #{connection => #{user => ~"minato"}}).

connection is the only option. A listener holds one connection and never shares it, because LISTEN is session state.

Per query

Options to minato:query/4, minato:simple/3 and the minato_query functions:

optiondefault
timeoutthe connection's timeoutdeadline for the statement; on expiry it is cancelled on the server, not abandoned
return_rows_as_mapsfalsea map per row instead of a tuple
column_name_as_atomfalseatom keys in those maps
uuid_formatstringbinary for the raw 16 bytes
datetime_formatdatetimemicroseconds for lossless integers

column_name_as_atom is off because a column name is not always something you wrote: a query built at run time can alias a column after a value, and every distinct alias would make an atom that is never collected. Turn it on for queries whose column names you chose.

The application environment

{minato, [
    {pools, #{main => #{size => 10, connection => #{user => ~"minato"}}}},
    {listeners, #{events => #{connection => #{user => ~"minato"}}}},
    {log_statements, false}
]}.

pools and listeners are started by minato's supervisor, so they come up with the application rather than after it. log_statements puts the SQL in events and logs; it is read per call, so it can be turned on while something is happening and off again afterwards. See Security for why it is off by default.