bevy_persistence_database
Persistence for Bevy ECS to ArangoDB or Postgres with an idiomatic Bevy Query API, explicit load triggers, and manual commits you can await.
Highlights
PersistenceQuerymirrors BevyQuery: use iter/get/single/get_many/iter_combinations after callingensure_loaded().- Smart caching and coalesced loads within a frame;
force_refresh()bypasses cache when needed. - Presence/value filters:
With,Without,Or, optionals, comparisons, and key filters viaGuid::key_field(). - Resources persisted alongside components with
#[persist(resource)]. - Batching + parallel commit execution; per-document versioning for optimistic concurrency.
Bevy Version Support
| Bevy | bevy_persistence_database |
|---|---|
| 0.16 | 0.1.x |
| 0.17 | 0.2.x - 0.5.x |
| 0.18 | 0.6.x |
| 0.19 | 0.5.x (yanked) |
Install
[]
= { = "0.18", = false, = ["bevy_log"] }
= { = "0.6.0", = ["arango", "postgres"] }
Enable arango or postgres features based on your backend and supply an Arc<dyn DatabaseConnection> at startup.
Define persistable types
use persist;
Add the plugin
use *;
use ;
use Arc;
Loading data
use *;
use ;
After ensure_loaded(), PersistenceQuery derefs to a regular Bevy Query for pass-through reads without additional DB I/O. Use force_refresh() to bypass cache.
Joins and transmute
use *;
use ;
Use join_filtered to correlate data across multiple queries without reloading, and transmute to widen the component view for reuse in systems or for table-style assertions in tests.
Committing changes
Changes are not auto-committed. Use the helpers:
use ;
// Async (drives its own updates internally)
let _ = commit.await?;
// Blocking convenience
let _ = commit_sync?;
Or trigger manually if you’re already inside a running app:
use ;
use oneshot;
let correlation_id = job.operation_id; // choose your own handle
let = channel;
register_commit_listener;
app.world_mut.write_message;
// hold `rx` to await the commit result in your orchestrator
Listeners are just oneshot senders keyed by a correlation ID. Each TriggerCommit should use a unique ID (you can reuse your job/operation ID) so the completion is routed to the right waiter. The plugin cleans up the entry when it sends the result.
Advanced configuration
use ;
let config = PersistencePluginConfig ;
app.add_plugins;
thread_count: Rayon pool size used for parallel commit preparation (serialization).default_store: fallback store when queries/commits don’t override.store().
Load-induced dirty flags are suppressed automatically during hydration ([PersistenceSession::materialize_entity_document], [PersistenceSession::materialize_resource], and related load APIs open a scope; PostUpdate [PersistenceSystemSet::FinishHydration] closes it after dirty tracking). Use PersistenceQuery::reconcile_versions manually after ops/migration if the in-memory version cache must be realigned to the database.
Commit scheduling helper
persistence_plugin::commit_in_flight is a run-condition that is true while a commit is in progress or queued. Gate a periodic commit-trigger system with .run_if(not(commit_in_flight)) to avoid preparing a redundant trigger (and its bookkeeping) while one is still running.
Scheduling notes
- Loads can run in
UpdateorPostUpdate. - Deferred world mutations from loads are applied in
PersistenceSystemSet::LoadApply, beforeTrackChanges. - Commit pipeline runs in
PersistenceSystemSet::Commit; readers that need fresh data should run afterPreCommit.
Error handling
All public APIs return Result<_, PersistenceError>. Version conflicts, connection issues, and timeouts surface through that error type so you can decide whether to retry, fail the job, or surface an error to callers.
bevy_many_relationship_edges local development
The optional bevy_many_relationship_edges feature depends on the published bevy_many_relationships crate. To exercise both relationship backends against an unpublished checkout, patch crates.io locally (do not commit monorepo-specific paths into this repository):
# .cargo/config.toml (local only)
[]
= { = "../bevy_many_relationships" }
Then run cargo test --features bevy_many_relationship_edges. In the dicemind monorepo, the parent repository supplies this patch automatically; see libraries/README.md.