Production & Scale/Infrastructure
Advanced14 min

Database & State Storage Patterns

LangGraph separates state into two layers: checkpointers for within-thread history and the Store for cross-thread memory. Choosing the right backend for each layer — and knowing what breaks when you get it wrong — is what this article covers.

Quick Reference

  • Checkpointers handle short-term state within a thread; the Store handles long-term memory across threads
  • AsyncPostgresSaver: durable, ACID-compliant, full checkpoint history — the safe production default
  • AsyncRedisSaver: lower latency than Postgres; requires AOF persistence or you lose checkpoints on restart
  • AsyncShallowRedisSaver: stores only the latest checkpoint, cutting Redis memory by 60–80% for simple agents
  • PostgresStore / RedisStore: LangGraph's built-in Store API for cross-thread memory with optional semantic search
  • Checkpoint tables grow at ~3 rows per invocation per node; plan a retention policy before first production deploy
  • Call checkpointer.setup() and store.setup() once at startup — both are idempotent

Two-Tier Memory: Checkpoints vs Store

LangGraph's persistence model has two distinct layers that engineers regularly conflate. Getting them confused leads to building redundant infrastructure or using the wrong tool for a job. The checkpointer handles within-thread state — what happened in this conversation. The Store handles across-thread state — what should persist across all conversations for a user.

Checkpointerthread-scoped auto-saveThread Auser-123 · session-1ckptckptckptstep 1 → 2 → 3Thread Buser-123 · session-2ckptckptstep 1 → 2no cross-thread accessPostgresSavercheckpoints tableStore (BaseStore)cross-thread, explicit put/searchnamespace: (user-123, "memories")survives new threads, sessions, restartsThread A stores prefThread B reads pref✓ shared across threadsPostgresStorestore.put / store.searchUse for: resume, HITL, time travelUse for: user prefs, long-term facts,cross-session memory

Checkpointer = thread-scoped auto-save · Store = cross-thread shared memory

CheckpointerStore
ScopeSingle thread (one conversation)Cross-thread (all conversations)
Written byLangGraph automatically after each nodeYour nodes explicitly via store.put()
Read byLangGraph automatically on resumeYour nodes explicitly via store.get() / store.search()
Typical contentFull graph state: messages, tool results, intermediate valuesUser facts, preferences, summaries, learned context
Grows withEvery node executionExplicit writes from your code
Production backendsAsyncPostgresSaver, AsyncRedisSaver, AsyncMongoDBSaverPostgresStore, RedisStore
Dev backendInMemorySaver (built-in)InMemoryStore (built-in)
Chain / AgentRunnableWithMessageHistorysession_idFactory Functionget_history(session_id)← only place you name the backendreturnsBackend InstanceBaseChatMessageHistoryInMemory / Redis /SQL / DynamoDBadd_messages() / messagesSwap backend = change factory only. Chain/agent code unchanged.Storage LayerRedis / PostgreSQL / SQLite / DynamoDB

Factory function is the only swap point — the chain never sees the backend

Don't build what LangGraph already built

If your agent needs to remember a user's preferred language or summarized facts across sessions, that's the Store API — not a custom Redis hash. Use InMemoryStore for dev and PostgresStore for production. One import line change, same interface.