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: writes stay fire-and-forget on the shared 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. 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, and _READ_OPS are internal plumbing on PraisonAIDB, not public API. They are named here only to describe the shared dispatch mechanism.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
Sync reads reached from inside a running event loop now fail loudly instead of silently dropping the read.Which hook to call from where
Pick sync or async by whether you sit inside a running loop and whether you read or write.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

