Skip to main content

For the complete documentation index, see llms.txt

Async calls

grpc_call is synchronous - the SQL statement blocks until the gRPC server replies. grpc_call_async enqueues the call instead, returning an id immediately. A background worker executes the call and stores the result; you fetch it later with grpc_call_result.

Use async when you need to:

  • Fan out many calls in parallel without holding a connection per call.
  • Tolerate slow or unreliable backends without stalling your transaction.
  • Decouple the calling transaction from the network round-trip.

How it works

The worker runs as a Postgres background worker process. It wakes up the moment a committing transaction sets the latch, dequeues up to pg_grpc.batch_size rows, executes all calls concurrently on a single-threaded async runtime, then persists the results. Old results are TTL-cleaned in the same cycle.

Basic usage

-- Enqueue. Returns a bigint id immediately.
SELECT grpc_call_async(
'localhost:50051',
'auth.AuthService/GetUser',
'{"id": "42"}'::jsonb
);
-- 1

-- Poll for the result.
SELECT status, response, message
FROM grpc_call_result(1);
-- status | response | message
-- ---------+---------------------------+---------
-- SUCCESS | {"id":"42","email":"..."} |

status is PENDING while the worker hasn't finished yet, SUCCESS on completion, or ERROR if the call failed.

Blocking wait

Pass async => false to block until the result is ready instead of polling:

SELECT status, response
FROM grpc_call_result(1, async => false);

This polls at 50 ms intervals using a WaitLatch timeout. The background worker does not signal the caller's latch when it writes a result, so the wait is purely time-based. Useful in scripts or tests where you want the result inline without writing a loop.

With metadata and options

Both parameters work the same as in grpc_call. Options are validated at enqueue time - bad values raise an error before any row is inserted.

SELECT grpc_call_async(
'localhost:50051',
'auth.AuthService/GetUser',
'{"id": "42"}'::jsonb,
metadata => '{"authorization": "Bearer abc"}'::jsonb,
options => '{"timeout_ms": 5000, "use_reflection": true}'::jsonb
);

Fan-out pattern

Call grpc_call_async in a set-returning context to enqueue one call per row in a single transaction. The worker picks them all up as a batch:

SELECT grpc_call_async(
'localhost:50051',
'notify.NotifyService/Send',
jsonb_build_object('user_id', user_id)
)
FROM users
WHERE notify_at < now();

Rollback safety

If the transaction that calls grpc_call_async is rolled back, the queue row is rolled back with it and the worker is never signaled. The worker is only woken on a real commit.

Configuration

The background worker is configured via postgresql.conf (or ALTER SYSTEM). The two identity settings (database_name, username) require a server restart because they are read at startup. The operational settings (ttl, batch_size) take effect on the next SIGHUP.

GUCTypeDefaultReloadDescription
pg_grpc.database_namestringpostgresrestartDatabase the worker connects to.
pg_grpc.usernamestring(superuser)restartRole the worker runs as. NULL = bootstrap superuser.
pg_grpc.batch_sizeinteger200SIGHUPMax rows dequeued per worker cycle.
pg_grpc.ttlstring6 hoursSIGHUPHow long completed results are retained before cleanup.

Minimal postgresql.conf setup:

shared_preload_libraries = 'pg_grpc'
pg_grpc.database_name = 'mydb'
pg_grpc.username = 'grpc_worker'

Schema

Two unlogged tables are created by the extension:

grpc.call_queue - pending and in-flight calls. Rows are deleted once the worker moves them to _call_result.

ColumnTypeNotes
idbigserialPrimary key; returned by grpc_call_async.
endpointtexthost:port of the gRPC server.
methodtextpkg.Service/Method.
requestjsonbRequest body.
metadatajsonbgRPC headers. NULL if not supplied.
optionsjsonbPer-call options blob. NULL if not supplied.
timeout_msintResolved at enqueue time from options.

grpc._call_result - completed results, cleaned up after pg_grpc.ttl.

ColumnTypeNotes
idbigintSame id as the call_queue row.
statustextSUCCESS or ERROR.
responsejsonbDecoded response. NULL on error.
error_msgtextError string. NULL on success.
createdtimestamptzWhen the result was stored; TTL anchor.