How do I keep data in Postgres checkpointer database from growing unbounded? #244

Open
opened 2026-02-15 18:14:59 -05:00 by yindo · 8 comments
Owner

Originally created by @kgreen24 on GitHub (Apr 29, 2025).

Hello all,

Just getting into Langchain / Graph and I want to use the Postgres saver for conversation state and to enable human in the loop. This is for a production app so I want to use something official, so I'm trying to use the out of the box PostgresSaver.

In looking at the data, I noticed that every trip through my graph results in about 100 rows being created across the 3 database tables that hold data (the other one looks like a version lookup table). I have a node in my graph that keeps the conversation length at a maximum, but that doesn't seem to clean up rows in the database accordingly.

What is the strategy to avoid unbounded growth in these tables? I'd like to for example be able to do the following:

  • Delete all threads whose last message is older than X days
  • Delete all checkpoints older than X days for threads that are still active

Are either of these possible without any custom database migrations or other large changes? Thank you!

Originally created by @kgreen24 on GitHub (Apr 29, 2025). Hello all, Just getting into Langchain / Graph and I want to use the Postgres saver for conversation state and to enable human in the loop. This is for a production app so I want to use something official, so I'm trying to use the out of the box PostgresSaver. In looking at the data, I noticed that every trip through my graph results in about 100 rows being created across the 3 database tables that hold data (the other one looks like a version lookup table). I have a node in my graph that keeps the conversation length at a maximum, but that doesn't seem to clean up rows in the database accordingly. What is the strategy to avoid unbounded growth in these tables? I'd like to for example be able to do the following: - Delete all threads whose last message is older than X days - Delete all checkpoints older than X days for threads that are still active Are either of these possible without any custom database migrations or other large changes? Thank you!
yindo added the enhancement label 2026-02-15 18:14:59 -05:00
Author
Owner

@hinthornw commented on GitHub (Apr 29, 2025):

In LangGraph platform, we support TTLs. You can do somethign similar if you are just using OSS.

https://langchain-ai.github.io/langgraph/how-tos/ttl/configure_ttl/

tl;dr if you're tracking top-level thread information in a separate table, you can drop stale ones, or you can garbage collect non-terminal checkpoints per thread.

@hinthornw commented on GitHub (Apr 29, 2025): In LangGraph platform, we support TTLs. You can do somethign similar if you are just using OSS. https://langchain-ai.github.io/langgraph/how-tos/ttl/configure_ttl/ tl;dr if you're tracking top-level thread information in a separate table, you can drop stale ones, or you can garbage collect non-terminal checkpoints per thread.
Author
Owner

@kgreen24 commented on GitHub (Apr 29, 2025):

So you're saying look at the code for how this is being implemented and try to replicate it?

@kgreen24 commented on GitHub (Apr 29, 2025): So you're saying look at the code for how this is being implemented and try to replicate it?
Author
Owner

@hinthornw commented on GitHub (Apr 29, 2025):

It's closed source, but implementing TTLs isn't that hard to do. Would just update liveliness of threads or checkpoints and sweep periodically.

The shallow postgres saver implements something similar, where it deletes non-terminal checkpoints for a given thread.

@hinthornw commented on GitHub (Apr 29, 2025): It's closed source, but implementing TTLs isn't that hard to do. Would just update liveliness of threads or checkpoints and sweep periodically. The shallow postgres saver implements something similar, where it deletes non-terminal checkpoints for a given thread.
Author
Owner

@kgreen24 commented on GitHub (Apr 29, 2025):

Yeah I'm asking for guidance on how to do exactly that. I'm not very familiar with the schema or the impact of making changes to it, or if this could be done with out of the box functionality and schemas.

@kgreen24 commented on GitHub (Apr 29, 2025): Yeah I'm asking for guidance on how to do exactly that. I'm not very familiar with the schema or the impact of making changes to it, or if this could be done with out of the box functionality and schemas.
Author
Owner

@Marlonzao commented on GitHub (Apr 30, 2025):

I'm also interested in this, we also need help on how to implement TTL on postgres integration.

@Marlonzao commented on GitHub (Apr 30, 2025): I'm also interested in this, we also need help on how to implement TTL on postgres integration.
Author
Owner

@Marlonzao commented on GitHub (May 1, 2025):

@kgreen24 @hinthornw

Hey everyone 👋

Just wanted to share a quick explanation of the changes I made in this repo, which is essentially a custom fork of a LangGraph checkpointer for Postgres.

I needed to implement a TTL (Time-To-Live) mechanism for data stored in Postgres, but since native TTL isn't supported, I came up with a rudimentary workaround.

The approach I used applies TTL at the thread level:

  • I introduced a new threads table that stores metadata for each thread, including a valid_till column.

  • Every time a thread is written (via putWrites or put), I perform an upsert on the threads table, updating valid_till to be 6 hours in the future (hardcoded for now).

  • All other tables (e.g., checkpoint_blobs, checkpoints, etc.) reference threads.id via a foreign key.

  • I enabled ON DELETE CASCADE for these references, so when a thread is deleted, all associated data is automatically cleaned up.

The only thing left is to hook up a cronjob or scheduled task that regularly deletes any threads where valid_till < NOW().

It's a simple but effective pattern for achieving TTL-like behavior with relational constraints and no background workers.

Open to feedback or improvements!

https://github.com/Marlonzao/checkpoint-postgres
https://www.npmjs.com/package/@eusoumarlon/langgraph-checkpoint-postgres

@Marlonzao commented on GitHub (May 1, 2025): @kgreen24 @hinthornw Hey everyone 👋 Just wanted to share a quick explanation of the changes I made in this repo, which is essentially a custom fork of a LangGraph checkpointer for Postgres. I needed to implement a TTL (Time-To-Live) mechanism for data stored in Postgres, but since native TTL isn't supported, I came up with a rudimentary workaround. The approach I used applies TTL at the thread level: - I introduced a new threads table that stores metadata for each thread, including a valid_till column. - Every time a thread is written (via putWrites or put), I perform an upsert on the threads table, updating valid_till to be 6 hours in the future (hardcoded for now). - All other tables (e.g., checkpoint_blobs, checkpoints, etc.) reference threads.id via a foreign key. - I enabled ON DELETE CASCADE for these references, so when a thread is deleted, all associated data is automatically cleaned up. The only thing left is to hook up a cronjob or scheduled task that regularly deletes any threads where valid_till < NOW(). It's a simple but effective pattern for achieving TTL-like behavior with relational constraints and no background workers. Open to feedback or improvements! https://github.com/Marlonzao/checkpoint-postgres https://www.npmjs.com/package/@eusoumarlon/langgraph-checkpoint-postgres
Author
Owner

@harveyhang commented on GitHub (Nov 25, 2025):

Can anyone think use k8s job do this task is a bad practice? (write code to delete expired data)

@harveyhang commented on GitHub (Nov 25, 2025): Can anyone think use k8s job do this task is a bad practice? (write code to delete expired data)
Author
Owner

@alex-antaya-sh commented on GitHub (Dec 9, 2025):

If you don't need time travel, you can use the ShallowPostgresSaver which reduces the amount of data being written to the Postgres tables. Doesn't solve the TTL / unbounded problem though.

@alex-antaya-sh commented on GitHub (Dec 9, 2025): If you don't need time travel, you can use the `ShallowPostgresSaver` which reduces the amount of data being written to the Postgres tables. Doesn't solve the TTL / unbounded problem though.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#244