Hi Team,
I want to gather opinion on one of our requirements related to LangGraph. I found a similar discussion here where this is discussed at length and provides useful insights, but I have more specific questions.
Use cases:
- Loading all the previous conversations of user(s) using checkpoints
- Re-hydrating an older conversation upon selection
(You can imagine 1 & 2 as ChatGPT-style app) - HITL & Pause-Resume, and any other typical usecases for checkpoints.
The core problem:
For these use-cases, we need identifiers beyond the standard checkpoint keys (thread_id, checkpoint_ns, checkpoint_id). Specifically, to support use case #1 — list all conversations for a user — we need to query across threads, which requires a user/agent identifier since there is no cross-thread index otherwise.
Option 1 — Thread Registry:
A separate thread registry (analogous to LangGraph Platform’s Thread entity) where each thread carries metadata such as user_id, agent_id, etc. This cleanly separates concerns: the checkpointer owns state snapshots, the registry owns identity and lifecycle metadata.
Trade-off: introduces a two-step flow (create thread → then configure checkpoint with the returned thread_id), adds infrastructure complexity (especially for non-platform deployment without an agent server or deployment infra taking care of this requirement), and requires keeping thread and checkpoint lifecycles in sync.
Option 2 — Extend the checkpoint table:
Rather than managing a separate thread registry, store the required values directly in the checkpoints. Essentially enhancing the table design to have additional columns:
thread_id TEXT NOT NULL,
checkpoint_ns TEXT NOT NULL DEFAULT ‘’,
checkpoint_id TEXT NOT NULL, – ULID, lexicographically sortable newest-last
parent_checkpoint_id TEXT,
type TEXT,
checkpoint BYTEA,
metadata JSONB, – LangGraph internal use (source, step, writes)
expires_at TIMESTAMPTZ, – optional: TTL for automatic expiry
user_id TEXT,
agent_id TEXT,
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
The agent would supply a config as follows:
config = {
"configurable": {
"thread_id": "<thread_id>",
"agent_id": "my-agent",
"user_id": "my-user"
}
}
Note: the existing metadata column is for LangGraph’s internal use (source, step, writes). The proposed user_id/agent_id columns are application-level identifiers.
Context — A2A agents:
Most of our agents are behind an A2A server (Google’s A2A protocol). For each request A2A generates a context_id; agents typically use context_id as thread_id or append more details to the context_id itself. Storing user_id and agent_id alongside checkpoints is therefore beneficial for queries such as — list conversations for user, get conversation details.
Option 2 — Pros:
- Simpler from the consumption side — no need to make two calls (1. create thread first, then 2. configure checkpoint with the returned
thread_id). Get started with configuring checkpoints straight away. - The agent can generate its own
thread_idor usecontext_idfrom A2A. Storinguser_id/agent_idwith checkpoints enables queries such as — list conversations for user, get conversation details. - TTL lives with the checkpoint itself. No separate per-thread TTL or lifecycle sync required.
Option 2 — Cons:
- Multiple snapshots are taken during agent execution; all snapshots share the same
user_id/agent_id, introducing duplication (no normalization). - Limited filtering capabilities — only two fixed identifiers. This can be mitigated by introducing an
agent_metadata JSONBcolumn to support additional key-value pairs for future filtering needs (keeping the existingmetadatacolumn for LangGraph’s internal use). - Potential performance issues due to filtering on non-PK columns across large tables — can be mitigated by indexing
user_idandagent_id.
Questions:
- Has anyone implemented Option 2 or a variant in production? What are the query performance implications of filtering on non-PK columns across large checkpoint tables?
- Is there a cleaner way to support cross-thread user/agent queries without Option 1’s two-step flow ? or would you say option-1 is the best way forward
- Any thoughts on
agent_metadata JSONBvs fixed columns — which approach has held up best in practice?
Thanks!