fix(pregel): async write-ordering safety for DeltaChannel via _delta_write_futs

In durability="async" mode (the default), put_writes calls are
fire-and-forget coroutines — a process crash between write submission and
checkpoint commit leaves a DELTA_SENTINEL blob with no backing writes,
causing silent data loss on replay.

AsyncPregelLoop now maintains _delta_write_futs: any write to a
DeltaChannel channel appends its asyncio.Future to this list in
accept_writes. _checkpointer_put_after_previous drains the list with
await asyncio.gather() before calling aput(), guaranteeing
checkpoint_writes are durable before the sentinel blob is committed.

The sync loop is unchanged: BackgroundExecutor.__exit__ already ensures
all background tasks complete before invoke() returns.

Also fixes DeltaChannel(list, add_messages) constructor call in
checkpoint-postgres async test (missing typ arg).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sydney Runkle
2026-04-30 14:49:09 -04:00
co-authored by Claude Sonnet 4.6
parent cdf5682bf3
commit 023bba2bde
4 changed files with 57 additions and 70 deletions
+1 -1
View File
@@ -389,7 +389,7 @@ async def test_delta_channel_chain_reconstruction(saver_name: str) -> None:
from typing_extensions import TypedDict
class State(TypedDict):
messages: Annotated[list, DeltaChannel(add_messages)]
messages: Annotated[list, DeltaChannel(list, add_messages)]
def respond(state: State) -> dict:
n = len(state["messages"])