asyncio.to_thread fallback.
Quick Start
1
Async Context Manager
Use async DB operations with the async context manager for automatic cleanup.
2
Wire to Async Agent
Connect async DB hooks to an async agent for seamless persistence.
How It Works
Shared dispatch for both entry points
Both persistence entry points flow through the same async-store dispatch. As of PR #4394, the async-store dispatch (isinstance(store, AsyncConversationStore) → await store.method(...) vs. asyncio.to_thread(store.method, ...)) lives on PraisonAIDB, in its internal _call_store / _dispatch_async helpers.
PersistenceOrchestrator.on_message / aon_message / on_agent_end / aon_agent_end delegate to those helpers instead of keeping a second copy, so the async/sync-store behaviour is identical no matter which entry point you pick — MemoryConfig(db=PraisonAIDB(...)) or the orchestrator-based wrap_agent_with_persistence / PersistentAgent / create_persistent_session.
_call_store splits by op kind through the shared bridge helper praisonai._async_bridge.dispatch_maybe_awaitable: writes stay fire-and-forget on the bridge (idempotent, uuid-keyed); reads (_READ_OPS = {"get", "get_session", "get_messages", "list_sessions"}) route through run_sync_or_offload, returning the real value on a sync path and raising loudly inside a running loop. Read-vs-write intent is passed explicitly as kind=DispatchKind.READ|WRITE at the call site (PR #5038); _READ_OPS is retained as the backward-compatible default classifier _call_store uses to pick the kind from the op’s name. The same helper backs _merge_and_set (used by all four sync completion hooks) and on_agent_end. As of PR #4861, _call_store and flush_pending_writes are instance methods on PraisonAIDB (previously staticmethods), and each instance tracks its own fire-and-forget writes in self._bg_writes — not a process-global set. _call_store, _dispatch_async, _merge_and_set, and _READ_OPS are internal plumbing on PraisonAIDB, not public API; they are named here only to describe the shared dispatch mechanism. DispatchKind and dispatch_maybe_awaitable in praisonai._async_bridge are public — see Async Bridge → dispatch_maybe_awaitable.Because bg-write tracking is per-instance (PR #4861), a tenant’s
close() / aclose() flushes only its own in-flight writes — one tenant’s stuck backend can no longer consume another tenant’s 5-second flush budget or discard its writes. See Thread Safety.Sync reads from inside a running loop
Bare sync reads reached from inside a running event loop fail loudly instead of silently dropping the read.The four sync completion hooks —
on_run_end, on_agent_end, on_trace_end, on_span_end — read internally (a read-modify-write) but do not fail loudly from a running loop. They persist correctly via the async completion write path below. See PR #4948.Async completion write path
The four sync completion hooks persist correctly from inside a running loop by routing the whole read-modify-write through one bridge submission.on_run_end, on_agent_end, on_trace_end, and on_span_end each call _merge_and_set, which builds a single coroutine that does get → merge → set on the state store. Since PR #5038, _merge_and_set hands that coroutine to dispatch_maybe_awaitable(..., kind=DispatchKind.WRITE, tracker=self._bg_writes, tracker_lock=self._bg_writes_lock, op_name=f"merge_and_set:{key}") instead of hand-rolling the submit/track/callback — same behaviour, one path. It becomes a tracked fire-and-forget write (recorded in self._bg_writes). Because the read never crosses the loop boundary, the write half always executes — the completion actually persists instead of leaving the record stuck at status="running".
The merge is uuid-keyed and idempotent, so deferring the write is safe by construction. Pre-existing fields (run_id, started_at, input_content, spans, events) are preserved; completion fields (output_content, status, ended_at) are merged in.
Ordering guarantee. Callers who must see the persisted record before returning (tests, request handlers reading the state store synchronously) should flush the pending write first.
get, get_session, get_messages, list_sessions) still fail loudly from inside a running loop — PR #4821 semantics are unchanged.
Which hook to call from where
Pick sync or async by whether you sit inside a running loop and whether you read or write.Follow-up note on completion hooks. The diagram’s “bare read” branch covers
get, get_session, get_messages, and list_sessions. The completion hooks (on_run_end / on_agent_end / on_trace_end / on_span_end) read internally but take the safe “fire-and-forget on bridge” branch via _merge_and_set — see Async completion write path.State-store lifecycle key
The wrapper writes both start and end underagent:{session_id}, so the end transition updates the same record the start wrote instead of a disconnected one.
Start previously used agent:{session_id}:{agent_id or name} and end wrote a separate record. Consumers who query the state store directly must update their key format to agent:{session_id}.
Configuration Options
All async hooks support these signatures from the DB adapter:Common Patterns
Complete Async Lifecycle
Sync Store Compatibility
Native Async Store
Best Practices
Prefer async with for exception-safety
Prefer async with for exception-safety
Manual
aclose() is now safe and idempotent — calling it twice does nothing harmful. Prefer async with for exception-safety and readability, so cleanup runs even when an error is raised mid-block:Reusing a PraisonAIDB after close
Reusing a PraisonAIDB after close
close() and aclose() reset the internal stores, so re-entering a with db: ... block after close cleanly re-initializes instead of dispatching to closed handles:Implement async_* methods for performance
Implement async_* methods for performance
For high-throughput async applications, implement native async methods:
Handle metadata consistently
Handle metadata consistently
All hooks accept optional metadata dictionaries:
Use sync context manager for mixed environments
Use sync context manager for mixed environments
Both sync and async context managers are supported:
Related
Persistence Overview
Complete persistence system documentation
Agent Architecture
Learn about async agent patterns

