DOC: Intended object lifetime is not obvious #766

Closed
opened 2026-02-20 17:41:39 -05:00 by yindo · 5 comments
Owner

Originally created by @stt-mapa on GitHub (Jul 4, 2025).

Originally assigned to: @eyurtsev on GitHub.

Issue with current documentation:

Most of the code snippets you can find in the documentation either show components or concepts in isolation or in a short, module level script. While this is OK to show the functionality, I could not find any information about how these components are intended to be used in an actual application.

For example, I am building a fastapi based application to serve a react agent via websockets. Since the central element is a compiled state graph, the main question is about its lifetime. Am I meant to build a graph once and reuse it (with a different RunnableConfig for each request), or create one for each request and have it collected afterwards?

I figured I could draw further inspiration from the checkpointer concept to answer this question. Starting out with the InMemorySaver, its implementation made it obvious that it is supposed to be held in memory, since the storage is held on the InMemorySaver instance. Since you have to provide the checkpointer when compiling a graph, I concluded that I am supposed to also hold the compiled graph and reuse it.

However, looking at the sqlite- and postgres based checkpointers, those require an open connection and have no resilience in regards to connection losses. Even their creation utility function from_conn_string return a context manager, implying that the checkpointer only has a limited usage window. Does this mean that I have to create a new checkpointer and recompile the graph for each request?

Am I missing something obvious here?

Idea or request for content:

Provide a how-to guide on application integration best practices or recommendations.

Originally created by @stt-mapa on GitHub (Jul 4, 2025). Originally assigned to: @eyurtsev on GitHub. ### Issue with current documentation: Most of the code snippets you can find in the documentation either show components or concepts in isolation or in a short, module level script. While this is OK to show the functionality, I could not find any information about how these components are intended to be used in an actual application. For example, I am building a fastapi based application to serve a react agent via websockets. Since the central element is a compiled state graph, the main question is about its lifetime. Am I meant to build a graph once and reuse it (with a different `RunnableConfig` for each request), or create one for each request and have it collected afterwards? I figured I could draw further inspiration from the checkpointer concept to answer this question. Starting out with the `InMemorySaver`, its implementation made it obvious that it is supposed to be held in memory, since the storage is held on the `InMemorySaver` instance. Since you have to provide the checkpointer when compiling a graph, I concluded that I am supposed to also hold the compiled graph and reuse it. However, looking at the sqlite- and postgres based checkpointers, those require an open connection and have no resilience in regards to connection losses. Even their creation utility function `from_conn_string` return a context manager, implying that the checkpointer only has a limited usage window. Does this mean that I have to create a new checkpointer and recompile the graph for each request? Am I missing something obvious here? ### Idea or request for content: Provide a how-to guide on application integration best practices or recommendations.
yindo added the documentation label 2026-02-20 17:41:39 -05:00
yindo closed this issue 2026-02-20 17:41:39 -05:00
Author
Owner

@hinthornw commented on GitHub (Jul 4, 2025):

  1. Reuse it
  2. InMemory* objects are for testing functionality not for prod. Use a database.
  3. Keep the connection open however long it makes sense for you. Typically make a connection pool at app startup and pull a connection for a single execution.

Do graph.copy(checkpointer=checkpointer) if you want to assign.

@hinthornw commented on GitHub (Jul 4, 2025): 1. Reuse it 2. InMemory* objects are for testing functionality not for prod. Use a database. 3. Keep the connection open however long it makes sense for you. Typically make a connection pool at app startup and pull a connection for a single execution. Do graph.copy(checkpointer=checkpointer) if you want to assign.
Author
Owner

@stt-mapa commented on GitHub (Jul 4, 2025):

@hinthornw Thank you for the quick response. I figured that reusing would be the more sensible approach, thanks for confirming this.

One more follow up question: the from_conn_string utiltiy functions provided by the sqlite- and postgres checkpointer make me wonder, is it possible to pass a checkpointer with the runnable config? Similar as you could with callbacks.

I checked the implementation of the Pregel.astream method which is used when calling graph.astream. It uses Pregel._defaults to unpack a few things, including a checkpointer from the "__pregel_checkpointer" config key (also accessible via langgraph.constants.CONFIG_KEY_CHECKPOINTER). Is it OK to pass a checkpointer this way, or is this just for internal use? Especially when using a supervisor setup, where you would normally only provide a checkpointer on the topmost graph, would this config-passed checkpointer also affect the subgraph execution somehow?

@stt-mapa commented on GitHub (Jul 4, 2025): @hinthornw Thank you for the quick response. I figured that reusing would be the more sensible approach, thanks for confirming this. One more follow up question: the `from_conn_string` utiltiy functions provided by the sqlite- and postgres checkpointer make me wonder, is it possible to pass a checkpointer with the runnable config? Similar as you could with callbacks. I checked the implementation of the `Pregel.astream` method which is used when calling `graph.astream`. It uses `Pregel._defaults` to unpack a few things, including a checkpointer from the `"__pregel_checkpointer"` config key (also accessible via `langgraph.constants.CONFIG_KEY_CHECKPOINTER`). Is it OK to pass a checkpointer this way, or is this just for internal use? Especially when using a supervisor setup, where you would normally only provide a checkpointer on the topmost graph, would this config-passed checkpointer also affect the subgraph execution somehow?
Author
Owner

@sydney-runkle commented on GitHub (Jul 20, 2025):

Hi! This is just for internal use and is subject to change at any time -- good call to check. Generally if something is prefixed with __, we're reserving for internal use.

When using a supervisor setup, if you provide the checkpointer to the parent graph, we'll automatically propagate to subgraphs :), see https://langchain-ai.github.io/langgraph/how-tos/subgraph/#add-persistence for more details.

@sydney-runkle commented on GitHub (Jul 20, 2025): Hi! This is just for internal use and is subject to change at any time -- good call to check. Generally if something is prefixed with `__`, we're reserving for internal use. When using a supervisor setup, if you provide the checkpointer to the parent graph, we'll automatically propagate to subgraphs :), see https://langchain-ai.github.io/langgraph/how-tos/subgraph/#add-persistence for more details.
Author
Owner

@stt-mapa commented on GitHub (Jul 21, 2025):

@sydney-runkle thank you for the clarification.

After playing around with the options available to me, it seems like this is a missed opportunity. In my case, the database connection behind the checkpointer is kind of short lived, and I have to create modified copies as per hinthornw's suggestion. It would be nice to be able to have an intended way to pass in a checkpointer after a graph is built. If it is possible to pass callbacks along in the runnable config for a single execution, why not do the same for checkpointers?

@stt-mapa commented on GitHub (Jul 21, 2025): @sydney-runkle thank you for the clarification. After playing around with the options available to me, it seems like this is a missed opportunity. In my case, the database connection behind the checkpointer is kind of short lived, and I have to create modified copies as per hinthornw's suggestion. It would be nice to be able to have an intended way to pass in a checkpointer after a graph is built. If it is possible to pass callbacks along in the runnable config for a single execution, why not do the same for checkpointers?
Author
Owner

@satyaprakash1729 commented on GitHub (Sep 18, 2025):

+1.
@sydney-runkle It would be good if someone looks into postgres checkpointer issues. I've seen multiple people reporting various issues related to postgres checkpointer and for most of them the underlying issue is resilience for stale connections.

PostgresSaver has two bottlenecks:

  1. ALL checkpoint operations go through a single asyncio.Lock
  2. Looks like a single connection is bound to a checkpointer.

https://github.com/langchain-ai/langgraph/issues/5675
https://github.com/langchain-ai/langgraph/issues/3716

@satyaprakash1729 commented on GitHub (Sep 18, 2025): +1. @sydney-runkle It would be good if someone looks into postgres checkpointer issues. I've seen multiple people reporting various issues related to postgres checkpointer and for most of them the underlying issue is resilience for stale connections. PostgresSaver has two bottlenecks: 1. ALL checkpoint operations go through a single asyncio.Lock 2. Looks like a single connection is bound to a checkpointer. https://github.com/langchain-ai/langgraph/issues/5675 https://github.com/langchain-ai/langgraph/issues/3716
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#766