LangGraph Memory Implementation: PostgreSQL vs MongoDB for State Persistence
LangGraph Memory Implementation: PostgreSQL vs MongoDB for State Persistence
Building smart applications with LangGraph is really exciting. LangGraph helps you create complex AI agents that can do many steps. These agents need to remember things as they work through different tasks.
This “remembering” part is called memory or state persistence. It means saving your agent’s thoughts and actions so it doesn’t forget them. If your application crashes or you want to continue later, this saved memory is super important.
Today, we’re going to look at two popular ways to save this LangGraph memory. We’ll compare PostgreSQL and MongoDB for keeping your agent’s state safe. We’ll explore which one might be best for your project.
Understanding LangGraph Memory
LangGraph helps you build agents that have different steps. Imagine a cooking recipe; each step is a node in your graph. Your agent moves from one step to the next.
For your agent to be smart, it needs to remember what happened in previous steps. This remembered information is its “memory.” For example, it might remember a user’s name or a piece of information it looked up.
Why is saving this memory important? Well, if your computer turns off, all that memory would be gone. State persistence means saving this memory to a database. This way, your agent can pick up exactly where it left off, even after a break.
PostgreSQL for LangGraph Memory Persistence
PostgreSQL is like a very organized filing cabinet. It’s a type of database called a relational database. This means it stores data in tables, much like spreadsheets with rows and columns.
Each piece of information has a specific place and type. This structure makes PostgreSQL very reliable for certain kinds of data. It ensures your data is always correct and follows strict rules.
Relational vs Document Storage (PostgreSQL Perspective)
When we talk about relational vs document storage, PostgreSQL is firmly in the relational camp. It requires you to define how your data looks upfront. You create tables with specific columns for each piece of information.
For LangGraph memory, this means you decide exactly what pieces of information your agent needs to save. You design tables to hold things like the agent’s ID, the current step, and the actual memory content. This structured approach helps keep your data very consistent.
This strong structure can be great for ensuring data quality. It also makes it easy to search for specific items using clear rules. You can trust that the data you retrieve will always fit the pattern you expect.
Schema Design Considerations (PostgreSQL)
Designing how to store your LangGraph state in PostgreSQL needs some thought. You’ll likely need a table to hold the main LangGraph state. This table would link to other tables if your state is very complex.
A common approach is to have a threads table. This table could store basic information about each LangGraph conversation or “thread.” It would also store a unique ID for each thread.
Then, you might have another table, like messages or states. This table would hold the actual detailed memory for each step or message within a thread. Each entry would point back to its thread_id to keep things organized.
Let’s look at a simple example of what a table might look like:
1
2
3
4
5
CREATE TABLE IF NOT EXISTS langgraph_states (
thread_id VARCHAR(255) PRIMARY KEY,
current_state JSONB,
last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
In this example, thread_id is a unique name for each agent conversation. current_state uses JSONB, which lets you store flexible data inside a rigid table. JSONB means “JSON Binary” and is a powerful PostgreSQL feature.
This allows you to keep the flexibility of LangGraph’s dynamic state. At the same time, you still benefit from PostgreSQL’s strong reliability. You get the best of both worlds for your langgraph memory postgresql mongodb comparison.
PostgreSQL State Implementation
Implementing PostgreSQL state implementation for LangGraph often involves using a Python library. You would typically use psycopg2 or asyncpg to connect to your PostgreSQL database. LangGraph provides interfaces to make this easier.
You define a class that knows how to save and load your agent’s state. This class interacts with your database using SQL commands. It will insert new states or update existing ones.
Here’s a simplified idea of how you might save state in Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import json
import psycopg2
class PostgreSQLSaver:
def __init__(self, db_url):
self.conn = psycopg2.connect(db_url)
self.create_table_if_not_exists()
def create_table_if_not_exists(self):
with self.conn.cursor() as cur:
cur.execute("""
CREATE TABLE IF NOT EXISTS langgraph_states (
thread_id VARCHAR(255) PRIMARY KEY,
current_state JSONB,
last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")
self.conn.commit()
def get_state(self, thread_id):
with self.conn.cursor() as cur:
cur.execute("SELECT current_state FROM langgraph_states WHERE thread_id = %s", (thread_id,))
result = cur.fetchone()
if result:
return json.loads(result[0]) # JSONB is returned as string
return None
def update_state(self, thread_id, state):
with self.conn.cursor() as cur:
cur.execute(
"INSERT INTO langgraph_states (thread_id, current_state) VALUES (%s, %s) "
"ON CONFLICT (thread_id) DO UPDATE SET current_state = EXCLUDED.current_state, last_update = CURRENT_TIMESTAMP;",
(thread_id, json.dumps(state))
)
self.conn.commit()
# Example of use with LangGraph (conceptual)
# from langgraph.checkpoint import BaseCheckpointSaver
# class LangGraphPostgresCheckpoint(BaseCheckpointSaver):
# def __init__(self, db_url):
# self.saver = PostgreSQLSaver(db_url)
#
# def get(self, thread_id: str):
# return self.saver.get_state(thread_id)
#
# def put(self, thread_id: str, state: dict):
# self.saver.update_state(thread_id, state)
#
# # checkpoint = LangGraphPostgresCheckpoint("postgresql://user:password@host:port/dbname")
# # app = Graph().compile(checkpointer=checkpoint)
This snippet shows you how you might design a simple saver. It connects to PostgreSQL, creates a table if needed, and has methods to get and update the LangGraph state. The ON CONFLICT part is important; it either adds a new state or updates an old one if the thread_id already exists.
Transaction Handling (PostgreSQL)
Transaction handling is one of PostgreSQL’s superpowers. It uses something called ACID properties. ACID stands for Atomicity, Consistency, Isolation, and Durability.
Imagine you are doing several steps to save your LangGraph state. Atomicity means all those steps either succeed completely or fail completely; no half-done work. Consistency ensures your data always follows the rules you set.
Isolation means that if multiple LangGraph agents are saving state at the same time, they don’t interfere with each other. Durability means that once your state is saved, it stays saved, even if the power goes out. These properties make PostgreSQL very reliable for critical applications.
Scalability Comparison (PostgreSQL)
When we talk about scalability comparison, PostgreSQL traditionally shines with vertical scaling. This means you make your existing server more powerful. You add more CPU, more memory, or faster storage.
For many applications, a single powerful PostgreSQL server can handle a huge amount of traffic. When that’s not enough, you can use techniques like read replicas. Read replicas are copies of your main database that handle reading requests.
This offloads work from your main server, improving performance for your LangGraph agents. While not as easy for horizontal scaling (spreading data across many machines) as some NoSQL databases, PostgreSQL offers robust options. For internal linking, you might check out our guide on optimizing PostgreSQL for high traffic.
Backup Strategies (PostgreSQL)
Having good backup strategies is crucial for any database. PostgreSQL offers excellent tools for this. pg_dump is a popular command-line tool that lets you create a full backup of your database.
You can also set up point-in-time recovery. This lets you restore your database to a specific moment in the past. This is like having a time machine for your data, protecting you from data loss or accidental changes.
Regular backups ensure your LangGraph memory is safe. You can sleep easy knowing your agent’s hard-earned knowledge won’t disappear. It’s a key factor in your langgraph memory postgresql mongodb comparison.
PostgreSQL Checkpointer Setup
The PostgreSQL checkpointer setup is an important internal process. Think of it like a diligent librarian. It makes sure that changes made in memory are regularly and safely written to the actual disk.
This process helps maintain data integrity and performance. It prevents the database from losing data if there’s a sudden crash. It also helps speed up recovery after an unexpected shutdown.
Properly configuring the checkpointer involves setting parameters like checkpoint_timeout and max_wal_size. These settings balance recovery speed with disk write performance. For specific configuration details, you should always refer to the official PostgreSQL documentation.
MongoDB for LangGraph Memory Persistence
MongoDB is a different kind of database, often called a NoSQL or document database. Instead of tables, it stores data in flexible documents. These documents are similar to JSON objects.
Imagine a folder where you can put any kind of paper. Some papers might have names, addresses, and phone numbers. Others might have drawings and notes; MongoDB is like that flexible folder.
This flexibility makes MongoDB great for data that doesn’t always fit into neat rows and columns. LangGraph’s state can be complex and change over time. MongoDB handles this kind of dynamic data very well.
Relational vs Document Storage (MongoDB Perspective)
Regarding relational vs document storage, MongoDB represents the document side. You don’t need to define a strict schema beforehand. Each LangGraph state can be saved as a single document.
This means you can store all the information related to an agent’s current memory in one place. If your agent’s memory structure changes, you don’t need to change your database tables. You simply save a new document with the updated structure.
This schema-less nature is a major advantage for rapidly evolving applications. It offers great agility. It means less time spent on database migrations when your LangGraph state evolves.
Schema Design Considerations (MongoDB)
Designing for schema design considerations in MongoDB for LangGraph is much simpler. You typically store each LangGraph thread’s entire state as one document. This document lives in a collection.
A “collection” in MongoDB is like a folder that holds many documents. All documents in a collection are generally related. You might have a langgraph_states collection.
Each document in this collection would have a unique ID for the LangGraph thread. It would also contain the full state of that thread. Let’s look at an example document:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"_id": "thread_12345",
"current_state": {
"user_input": "What is the capital of France?",
"tool_results": {
"search_tool": "Paris"
},
"agent_history": [
{"role": "user", "content": "What is the capital of France?"},
{"role": "assistant", "content": "The capital of France is Paris."}
],
"step_count": 2
},
"last_updated": ISODate("2023-10-27T10:00:00Z")
}
Here, _id is the unique identifier for the thread. current_state holds all the detailed memory of your LangGraph agent. This makes it easy to save and retrieve the entire state in one go.
This approach is very natural for LangGraph’s often nested and dynamic state. It maps very well to how Python dictionaries work. You simply convert your LangGraph state dictionary directly into a MongoDB document.
MongoDB State Implementation
The MongoDB state implementation for LangGraph is also straightforward. You use a library like pymongo to connect to your MongoDB database. You then interact with collections to store and retrieve documents.
You’d create a class similar to the PostgreSQL example. This class would handle connecting to MongoDB and performing insert or update operations. It also knows how to fetch the state for a given thread_id.
Here’s a simplified Python example for saving LangGraph state with MongoDB:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from pymongo import MongoClient
from datetime import datetime
class MongoDBSaver:
def __init__(self, db_url, db_name="langgraph_db", collection_name="langgraph_states"):
self.client = MongoClient(db_url)
self.db = self.client[db_name]
self.collection = self.db[collection_name]
def get_state(self, thread_id):
document = self.collection.find_one({"_id": thread_id})
if document:
return document.get("current_state")
return None
def update_state(self, thread_id, state):
self.collection.update_one(
{"_id": thread_id},
{"$set": {"current_state": state, "last_updated": datetime.utcnow()}},
upsert=True # Creates a new document if _id doesn't exist
)
# Example of use with LangGraph (conceptual)
# from langgraph.checkpoint import BaseCheckpointSaver
# class LangGraphMongoCheckpoint(BaseCheckpointSaver):
# def __init__(self, db_url):
# self.saver = MongoDBSaver(db_url)
#
# def get(self, thread_id: str):
# return self.saver.get_state(thread_id)
#
# def put(self, thread_id: str, state: dict):
# self.saver.update_state(thread_id, state)
#
# # checkpoint = LangGraphMongoCheckpoint("mongodb://user:password@host:port/")
# # app = Graph().compile(checkpointer=checkpoint)
This code connects to MongoDB and defines methods to get and update state. The upsert=True part is like PostgreSQL’s ON CONFLICT. It either adds a new document or updates an existing one for the thread_id.
MongoDB’s document model fits very naturally with the nested dictionary structure often used in Python. This can make development faster. You don’t have to break down complex objects into many separate tables.
Transaction Handling (MongoDB)
Historically, transaction handling was a major difference between MongoDB and relational databases. MongoDB focused more on availability and scalability. With MongoDB 4.0 and later, it introduced multi-document ACID transactions.
This means you can now perform multiple operations across different documents as a single, all-or-nothing transaction. This ensures data consistency, similar to PostgreSQL. However, these transactions often work best within a single replica set.
While not as fundamental to its design as in PostgreSQL, MongoDB’s transactions are robust. They offer the necessary consistency for many use cases. For detailed information, you should consult the official MongoDB documentation on transactions.
Scalability Comparison (MongoDB)
In terms of scalability comparison, MongoDB truly excels with horizontal scaling. This means you can spread your data across many different servers. This process is called sharding.
Sharding allows your database to handle enormous amounts of data and traffic. If your LangGraph application grows very large, you can simply add more servers. MongoDB automatically distributes your data and queries across these machines.
This makes MongoDB a strong choice for applications expecting massive growth. It’s designed from the ground up to scale out. For more on this, you can look at our blog post on scalable microservices with NoSQL.
Backup Strategies (MongoDB)
Backup strategies for MongoDB are also well-developed. mongodump is a command-line utility for creating backups. It exports data into BSON (Binary JSON) files.
You can also use cloud provider snapshots if running MongoDB on a service like AWS or Google Cloud. For larger, production-grade deployments, MongoDB Atlas (MongoDB’s cloud service) offers continuous backups and point-in-time recovery.
These methods ensure your LangGraph state is safe. They provide different levels of recovery. You can choose the strategy that best fits your recovery needs and budget.
Direct Comparison: LangGraph Memory PostgreSQL MongoDB
Now let’s put langgraph memory postgresql mongodb comparison side-by-side. Both are powerful databases, but they have different strengths. Choosing depends on what your LangGraph project needs most.
Query Patterns Comparison
When considering query patterns comparison, think about how you’ll look up your LangGraph state.
- PostgreSQL: You’ll use SQL queries. You can filter based on any column, join tables, and do complex aggregations. If your LangGraph state is stored across multiple tables, joins are essential.
- Example:
SELECT current_state FROM langgraph_states WHERE thread_id = 'abc' AND last_update > '2023-01-01';
- Example:
- MongoDB: You’ll use a query language based on JSON-like documents. You can query deeply nested fields within a document. It’s very efficient for fetching an entire document by its
_id.- Example:
db.langgraph_states.findOne({"_id": "thread_abc"})ordb.langgraph_states.find({"current_state.user_input": "hello"}).
- Example:
If you primarily fetch the entire LangGraph state by its ID, both are efficient. If you need to search within parts of the state very often, or combine state with other structured data, PostgreSQL might offer more powerful options. MongoDB’s strength is often in retrieving entire documents or filtering by fields within documents.
Performance Benchmarks (Conceptual)
It’s hard to give exact performance benchmarks without knowing your specific use case. However, we can discuss factors.
- PostgreSQL:
- Writes: Can be slower with many small updates due to ACID overhead, especially on disk.
- Reads: Very fast for structured queries, especially with proper indexing. Reading large JSONB blobs can add overhead.
- Data Size: Good for medium to large datasets on a single server.
- Latency/Throughput: Highly dependent on hardware, indexing, and query complexity.
- MongoDB:
- Writes: Generally faster for single document writes, especially with its document model.
- Reads: Very fast for retrieving entire documents by
_id. Queries on deeply nested fields can be slower without proper indexing. - Data Size: Excellent for very large datasets distributed across many servers.
- Latency/Throughput: Can achieve very high throughput with horizontal scaling.
For langgraph memory postgresql mongodb comparison, if your state changes frequently with many small updates, MongoDB might show better write performance initially. If your state is read often with complex filtering, PostgreSQL with good indexing can be excellent. The type of query patterns comparison you have will heavily influence actual performance benchmarks.
Schema Design Trade-offs
The schema design trade-offs are a major differentiator.
- PostgreSQL: Requires a defined schema. This means you decide the shape of your data columns before you save it.
- Pros: Data consistency, strong data types, relational integrity (ensuring links between tables are valid).
- Cons: Less flexible for changing data structures, requires migrations when your LangGraph state changes significantly.
- MongoDB: Schema-less (or flexible schema). Documents in a collection don’t have to look exactly the same.
- Pros: Very flexible, easy to evolve your LangGraph state structure, quick development.
- Cons: Can lead to “schema drift” if not managed, harder to ensure consistency across all documents without application-level checks.
If your LangGraph memory structure is stable and unlikely to change, PostgreSQL offers robust consistency. If your LangGraph state is experimental, rapidly evolving, or varies greatly between different agent types, MongoDB’s flexibility is a huge advantage. This directly impacts langgraph memory postgresql mongodb comparison for development speed and future maintenance.
Transaction Handling Differences
The transaction handling aspect is also quite different.
- PostgreSQL: ACID transactions are a core part of its design. They guarantee consistency and reliability across multiple operations, even involving different tables. This is fundamental for mission-critical data.
- MongoDB: Supports multi-document ACID transactions since version 4.0. These offer consistency guarantees comparable to relational databases. However, they can have performance implications and are typically within a replica set.
For applications where absolute consistency across multiple pieces of information is paramount, PostgreSQL’s long-standing ACID guarantees are a strong point. MongoDB’s newer transaction capabilities are excellent for many modern applications, but understanding their scope and potential performance impacts is important.
Scalability Approaches
The scalability approaches are fundamentally different.
- PostgreSQL: Primarily scales vertically (bigger server). It can also scale horizontally with read replicas for read-heavy workloads. Sharding for writes is possible but more complex to set up and manage compared to MongoDB.
- MongoDB: Designed for horizontal scaling (sharding). It can distribute data across many servers to handle massive growth in data and traffic. This makes it ideal for very large, high-throughput applications.
If you anticipate your LangGraph application growing to handle millions of threads or store petabytes of data, MongoDB’s native sharding is a powerful feature. For smaller to medium-sized applications, or those where data fits well on a single powerful server, PostgreSQL can be highly cost-effective and performant.
Backup Strategies
Both databases offer robust backup strategies.
- PostgreSQL:
pg_dumpfor full backups, point-in-time recovery with WAL (Write-Ahead Log) archiving. Very mature and reliable. - MongoDB:
mongodumpfor backups, cloud provider snapshots, MongoDB Atlas for continuous backups and point-in-time recovery. Also very reliable, especially with cloud-managed services.
The choice here often comes down to your operational preferences and existing infrastructure. Both provide strong guarantees against data loss.
Cost Analysis
The cost analysis for langgraph memory postgresql mongodb comparison involves several factors.
- PostgreSQL:
- Licensing: Open-source, so no direct license costs.
- Operational Costs: Can be significant for high-availability setups or highly optimized performance. Requires skilled DBAs. Cloud services like AWS RDS or Google Cloud SQL offer managed PostgreSQL, reducing operational burden but incurring service fees.
- MongoDB:
- Licensing: MongoDB Community Edition is open-source. MongoDB Enterprise Edition offers additional features and support (paid license).
- Operational Costs: Also requires skilled personnel for self-managed deployments. MongoDB Atlas is a fully managed cloud service that greatly simplifies operations but comes with service fees.
For many small to medium projects, both can be very cost-effective, especially using open-source versions on commodity hardware or basic cloud instances. As you scale, the operational complexity and managed service fees become a bigger part of the cost analysis. MongoDB Atlas can be more expensive than managed PostgreSQL for similar performance tiers, but it simplifies horizontal scaling significantly.
Practical Examples and Use Cases
Let’s imagine a simple LangGraph agent. This agent greets a user, asks their name, and then remembers it.
Simple LangGraph Agent with Memory
First, we define our basic LangGraph structure. This part is the same regardless of your memory choice.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from langgraph.graph import StateGraph, END
from typing import Dict, TypedDict, List
class AgentState(TypedDict):
chat_history: List[str]
user_name: str
greeted: bool
def greet_user(state: AgentState):
if not state.get("greeted"):
print("Agent: Hello there! What's your name?")
return {"chat_history": state.get("chat_history", []) + ["Agent: Hello there! What's your name?"], "greeted": True}
return state
def get_name(state: AgentState):
user_input = input("You: ")
if "my name is" in user_input.lower():
name = user_input.split("my name is")[-1].strip().capitalize()
print(f"Agent: Nice to meet you, {name}!")
return {"chat_history": state.get("chat_history", []) + [f"You: {user_input}", f"Agent: Nice to meet you, {name}!"], "user_name": name}
print("Agent: I didn't catch your name. Can you tell me 'My name is [your name]'?")
return {"chat_history": state.get("chat_history", []) + [f"You: {user_input}", "Agent: I didn't catch your name. Can you tell me 'My name is [your name]'?"]}
def respond_with_name(state: AgentState):
if state.get("user_name"):
print(f"Agent: Is there anything else I can help you with today, {state['user_name']}?")
return {"chat_history": state.get("chat_history", []) + [f"Agent: Is there anything else I can help you with today, {state['user_name']}?"]}
return state
workflow = StateGraph(AgentState)
workflow.add_node("greet", greet_user)
workflow.add_node("get_name", get_name)
workflow.add_node("respond", respond_with_name)
workflow.set_entry_point("greet")
workflow.add_edge("greet", "get_name")
workflow.add_edge("get_name", "respond")
workflow.add_edge("respond", END) # Or loop back if more interaction
This graph defines a simple flow. Now, how do we make it remember the user_name across runs or if the program restarts? That’s where persistence comes in.
Saving State with PostgreSQL
To use PostgreSQL for this, you’d integrate the PostgreSQLSaver (or a similar official LangGraph checkpoint saver) into your LangGraph app.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# Assuming you have the PostgreSQLSaver class defined earlier
# and psycopg2 installed: pip install psycopg2-binary
import json
import psycopg2
from langgraph.checkpoint import BaseCheckpointSaver
from langgraph.graph import StateGraph, END
from typing import Dict, TypedDict, List
# Re-define AgentState and nodes for completeness in this snippet
class AgentState(TypedDict):
chat_history: List[str]
user_name: str
greeted: bool
def greet_user(state: AgentState):
if not state.get("greeted"):
print("Agent: Hello there! What's your name?")
return {"chat_history": state.get("chat_history", []) + ["Agent: Hello there! What's your name?"], "greeted": True}
return state
def get_name(state: AgentState):
user_input = input("You: ")
if "my name is" in user_input.lower():
name = user_input.split("my name is")[-1].strip().capitalize()
print(f"Agent: Nice to meet you, {name}!")
return {"chat_history": state.get("chat_history", []) + [f"You: {user_input}", f"Agent: Nice to meet you, {name}!"], "user_name": name}
print("Agent: I didn't catch your name. Can you tell me 'My name is [your name]'?")
return {"chat_history": state.get("chat_history", []) + [f"You: {user_input}", "Agent: I didn't catch your name. Can you tell me 'My name is [your name]'?"]}
def respond_with_name(state: AgentState):
if state.get("user_name"):
print(f"Agent: Is there anything else I can help you with today, {state['user_name']}?")
return {"chat_history": state.get("chat_history", []) + [f"Agent: Is there anything else I can help you with today, {state['user_name']}?"]}
return state
# --- PostgreSQL Saver (simplified from above) ---
class LangGraphPostgresCheckpoint(BaseCheckpointSaver):
def __init__(self, db_url):
self.conn_str = db_url
self._ensure_table()
def _ensure_table(self):
with psycopg2.connect(self.conn_str) as conn:
with conn.cursor() as cur:
cur.execute("""
CREATE TABLE IF NOT EXISTS langgraph_states (
thread_id VARCHAR(255) PRIMARY KEY,
current_state JSONB,
last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")
conn.commit()
def get(self, thread_id: str):
with psycopg2.connect(self.conn_str) as conn:
with conn.cursor() as cur:
cur.execute("SELECT current_state FROM langgraph_states WHERE thread_id = %s", (thread_id,))
result = cur.fetchone()
if result:
return {"config": {"configurable": {"thread_id": thread_id}}, "checkpoint": {"v": 1, "ts": "2023-01-01T00:00:00Z", "id": "1", "channel_values": {"__root__": json.loads(result[0])}}}
return None
def put(self, thread_id: str, checkpoint: Dict):
state_to_save = checkpoint["checkpoint"]["channel_values"]["__root__"]
with psycopg2.connect(self.conn_str) as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO langgraph_states (thread_id, current_state) VALUES (%s, %s) "
"ON CONFLICT (thread_id) DO UPDATE SET current_state = EXCLUDED.current_state, last_update = CURRENT_TIMESTAMP;",
(thread_id, json.dumps(state_to_save))
)
conn.commit()
# --- LangGraph Application ---
workflow = StateGraph(AgentState)
workflow.add_node("greet", greet_user)
workflow.add_node("get_name", get_name)
workflow.add_node("respond", respond_with_name)
workflow.set_entry_point("greet")
workflow.add_edge("greet", "get_name")
workflow.add_edge("get_name", "respond")
workflow.add_edge("respond", END)
# Configure the PostgreSQL checkpoint
# Replace with your actual PostgreSQL connection string
PG_DB_URL = "postgresql://user:password@host:port/dbname"
checkpoint = LangGraphPostgresCheckpoint(PG_DB_URL)
app = workflow.compile(checkpointer=checkpoint)
# To run the app and save state for a specific thread
thread_id = "my_first_agent_session"
# app.invoke({}, {"configurable": {"thread_id": thread_id}})
In this setup, LangGraph will automatically call get and put methods of LangGraphPostgresCheckpoint. When put is called, your agent’s state (which is a dictionary) gets converted to JSON and stored in the current_state JSONB column. When get is called, it retrieves this JSON and converts it back into a Python dictionary. This is a robust way for langgraph memory postgresql mongodb comparison.
Saving State with MongoDB
Similarly, for MongoDB, you’d use the MongoDBSaver with your LangGraph application.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# Assuming you have the MongoDBSaver class defined earlier
# and pymongo installed: pip install pymongo
from pymongo import MongoClient
from datetime import datetime
import json # for JSON conversion in LangGraph checkpoint format
from langgraph.checkpoint import BaseCheckpointSaver
from langgraph.graph import StateGraph, END
from typing import Dict, TypedDict, List
# Re-define AgentState and nodes for completeness in this snippet
class AgentState(TypedDict):
chat_history: List[str]
user_name: str
greeted: bool
def greet_user(state: AgentState):
if not state.get("greeted"):
print("Agent: Hello there! What's your name?")
return {"chat_history": state.get("chat_history", []) + ["Agent: Hello there! What's your name?"], "greeted": True}
return state
def get_name(state: AgentState):
user_input = input("You: ")
if "my name is" in user_input.lower():
name = user_input.split("my name is")[-1].strip().capitalize()
print(f"Agent: Nice to meet you, {name}!")
return {"chat_history": state.get("chat_history", []) + [f"You: {user_input}", f"Agent: Nice to meet you, {name}!"], "user_name": name}
print("Agent: I didn't catch your name. Can you tell me 'My name is [your name]'?")
return {"chat_history": state.get("chat_history", []) + [f"You: {user_input}", "Agent: I didn't catch your name. Can you tell me 'My name is [your name]'?"]}
def respond_with_name(state: AgentState):
if state.get("user_name"):
print(f"Agent: Is there anything else I can help you with today, {state['user_name']}?")
return {"chat_history": state.get("chat_history", []) + [f"Agent: Is there anything else I can help you with today, {state['user_name']}?"]}
return state
# --- MongoDB Saver (simplified from above) ---
class LangGraphMongoCheckpoint(BaseCheckpointSaver):
def __init__(self, db_url, db_name="langgraph_db", collection_name="langgraph_states"):
self.client = MongoClient(db_url)
self.collection = self.client[db_name][collection_name]
def get(self, thread_id: str):
document = self.collection.find_one({"_id": thread_id})
if document:
# LangGraph expects a specific checkpoint format
return {"config": {"configurable": {"thread_id": thread_id}}, "checkpoint": {"v": 1, "ts": "2023-01-01T00:00:00Z", "id": "1", "channel_values": {"__root__": document.get("current_state")}}}
return None
def put(self, thread_id: str, checkpoint: Dict):
state_to_save = checkpoint["checkpoint"]["channel_values"]["__root__"]
self.collection.update_one(
{"_id": thread_id},
{"$set": {"current_state": state_to_save, "last_updated": datetime.utcnow()}},
upsert=True
)
# --- LangGraph Application ---
workflow = StateGraph(AgentState)
workflow.add_node("greet", greet_user)
workflow.add_node("get_name", get_name)
workflow.add_node("respond", respond_with_name)
workflow.set_entry_point("greet")
workflow.add_edge("greet", "get_name")
workflow.add_edge("get_name", "respond")
workflow.add_edge("respond", END)
# Configure the MongoDB checkpoint
# Replace with your actual MongoDB connection string
MONGO_DB_URL = "mongodb://localhost:27017/"
checkpoint = LangGraphMongoCheckpoint(MONGO_DB_URL)
app = workflow.compile(checkpointer=checkpoint)
# To run the app and save state for a specific thread
thread_id = "my_first_agent_session"
# app.invoke({}, {"configurable": {"thread_id": thread_id}})
With MongoDB, the current_state dictionary from LangGraph is saved directly as a part of the MongoDB document. This makes it very intuitive, as Python dictionaries map almost perfectly to JSON documents. The upsert=True makes sure it either creates or updates the document for the given thread_id.
These practical examples highlight how each database handles the langgraph memory postgresql mongodb comparison at a code level. Both integrate well with LangGraph’s checkpointing system.
Which One Should You Choose?
Making a choice in langgraph memory postgresql mongodb comparison depends on your project’s unique needs. There isn’t a single “best” answer. Consider these factors carefully.
Here’s a quick summary to help you decide:
| Feature | PostgreSQL | MongoDB |
|---|---|---|
| Data Model | Relational (tables, rows, columns) | Document (JSON-like documents) |
| Schema | Rigid, defined upfront (JSONB adds flexibility) |
Flexible, dynamic (schema-less) |
| Transactions | Strong ACID compliance (core design) | Multi-document ACID (since 4.0, within replica set) |
| Scalability | Vertical, Read Replicas, complex Sharding | Horizontal (Sharding is native and easier) |
| Querying | SQL, complex joins across tables | JSON-like queries, deep nesting, efficient by _id |
| Complexity of State | Best for structured, stable state. JSONB for dynamic parts | Excellent for dynamic, nested, evolving state |
| Performance | Strong for complex reads, good for balanced loads | Strong for high-volume writes, simple reads, horizontal scaling |
| Use Case Fit | Financial, inventory, strict data rules | Content management, IoT, real-time analytics, rapidly changing data |
| Cost | Open-source, managed services available | Community Edition free, Enterprise/Atlas paid |
Decision Factors Based on Project Needs
- If your LangGraph state is very structured and stable:
- PostgreSQL is a great choice. Its strong schema and ACID guarantees ensure data integrity. If you need to perform complex analysis on historical states, SQL’s power can be beneficial.
- If your LangGraph state is dynamic, nested, and likely to evolve:
- MongoDB is often preferred. Its flexible document model allows you to change your state structure without downtime. This can speed up development and iteration.
- If you anticipate massive scale and high throughput (millions of users, terabytes of data):
- MongoDB’s horizontal scaling (sharding) is a powerful advantage. It’s built for distributed systems from the ground up.
- If data consistency and transactional integrity are your absolute top priority:
- PostgreSQL remains a highly reliable choice, especially if you rely on multi-table transactions. MongoDB’s transactions are robust but have different characteristics.
- If you prefer working with SQL and an established ecosystem:
- PostgreSQL has a very mature ecosystem and a vast community.
- If you prefer a JSON-like structure and have experience with NoSQL:
- MongoDB will feel more natural and efficient for development.
For a new project, especially if you’re not sure how your LangGraph agent’s state will evolve, MongoDB’s flexibility can be less restrictive. If you are building a critical system where every piece of data must adhere to strict rules, PostgreSQL provides stronger built-in guarantees.
Conclusion
Both PostgreSQL and MongoDB are excellent choices for langgraph memory postgresql mongodb comparison for state persistence. Each brings its own set of strengths to the table. PostgreSQL offers strong data integrity and powerful relational querying, while MongoDB provides schema flexibility and superior horizontal scalability.
Your decision should be guided by your specific project requirements, future scalability needs, and your team’s familiarity with each technology. Carefully consider your schema design considerations, anticipated query patterns comparison, and scalability comparison. Think about your backup strategies and cost analysis for the long run.
By understanding the key differences between relational vs document storage, and the unique features like PostgreSQL checkpointer setup and MongoDB state implementation, you can make an informed decision. This will ensure your LangGraph agents have reliable and efficient memory, no matter how complex their tasks become.
Leave a comment