Sessions
The default API uses a fresh database session for every call. Here is when you need to open a session yourself, and why.
Default behavior: a fresh session per call
await env.db.exec("select 1");That single line connects, runs the query, syncs, and disconnects. As a result, no session state survives between calls.
BEGINdoes not carry over to the next call.SETsettings do not apply to the next call.- Prepared statements and temporary tables do not survive either.
Why connections are not reused
Reusing a connection requires a guarantee that every trace of state left by the previous request has been removed. PostgreSQL's DISCARD ALL is meant to do that, but we have not yet confirmed that it safely clears all state in this engine.
Reusing connections before that is confirmed would let one request's SET settings or temporary tables leak into another user's request. So for now, we close the connection and open a new one every time. No session state passes between requests.
Performance characteristics
| Step | Cost |
|---|---|
| Baseline cost of a new session | 2.6 ms |
$1 parameter | +0.6 ms |
| Real table lookup (catalog warm-up) | +5 ms |
| Write (commit + flush) | +9 ms |
The 5 ms on a table lookup is the cost of opening a new session and warming the catalog on every call. It can be reduced once safe session reuse is confirmed. If you connect directly over the PostgreSQL protocol and keep the session open, the same query takes 0.55 ms.
Using a session directly
If you need to run several SQL statements in the same session, use a session handle.
const s = await env.db.session();
try {
await s.exec("begin");
await s.exec("insert into a values ($1)", [1]);
await s.exec("insert into b values ($1)", [2]);
await s.exec("commit");
} finally {
await s.close();
}In @runlot/pg, pool.connect() plays the same role.
Rules
close()is idempotent. You can call it more than once.close()rolls back an open transaction. To commit, you must sendCOMMITyourself.- Running SQL on a closed handle raises an
08003error. - After 30 seconds without a SQL statement, the session is reclaimed and any open transaction is rolled back.
Keep it inside the request scope
Do not store a session handle in a global variable or pass it outside the request. It can hit the 30-second limit and have its transaction rolled back, and while it holds the session other requests have to wait.
Concurrent requests are processed in order
Database requests are handled one at a time per project. If a request holds a session for a long time, other requests wait. A long transaction can increase response times across the whole project.
The default execution limit for a single SQL statement is 10 seconds. Queries that exceed it are aborted.