bevy_persistence_database
Persistence for Bevy ECS to ArangoDB or Postgres with an idiomatic Bevy Query API.
Highlights
- Bevy Query API consistency
- PersistentQuery SystemParam derefs to Bevy’s Query, so iter/get/single/get_many/contains/iter_combinations work as usual.
- Explicit load trigger: ensure_loaded performs DB I/O, then you use pass-throughs on world data.
- Smart caching and explicit refresh
- Identical loads in the same frame coalesce into a single DB call.
- Repeated identical loads hit the cache. Use force_refresh to bypass it.
- Presence and value filters
- Type-driven presence via With, Without, and Or<(…)>.
- Optional components in Q (Option<&T>) are fetched when present.
- Value filters: eq, ne, gt, gte, lt, lte, combined with and/or/in.
- Key filtering via Guid::key_field().eq("…") or .in_([...]).
- Resources
- #[persist(resource)] resources are persisted and fetched alongside any query.
- Optimistic concurrency and conflict handling
- Per-document versioning with conflict detection.
- Batching and parallel commit
- Configurable batching with a thread pool and concurrent transaction execution.
- Backends
- ArangoDB and Postgres, selected at build time via features.
Install Add the core and derive crates, enabling one or both backends.
[]
= { = "0.16" }
= { = "bevy_persistence_database/bevy_persistence_database", = ["arango", "postgres"] }
= { = "bevy_persistence_database/bevy_persistence_database_derive" }
Backends and features
- Enable features on bevy_persistence_database:
- arango: builds the ArangoDB backend (arangors).
- postgres: builds the Postgres backend (tokio-postgres).
- Provide an Arc at startup:
- Arango:
- await ArangoDbConnection::ensure_database(url, user, pass, db_name)
- let db = ArangoDbConnection::connect(url, user, pass, db_name).await?
- Postgres:
- PostgresDbConnection::ensure_database(host, user, pass, db_name, Some(port)).await?
- let db = PostgresDbConnection::connect(host, user, pass, db_name, Some(port)).await?
- Arango:
Quickstart
- Persist-able types.
use persist;
- Add the plugin with a real database connection.
use *;
use ;
use Arc;
Loading pattern
- Use PersistentQuery as a SystemParam.
- Add value filters via .where(...) (alias of .filter(...)).
- Explicitly trigger the load with .ensure_loaded(); then use pass-throughs on world-only data.
use *;
use PersistentQuery;
Pass-throughs are world-only After a load, PersistentQuery derefs to bevy::prelude::Query. Use standard accessors without new DB I/O:
Key filtering
- Single key:
use ;
- Multiple keys via IN:
let _ = pq.where.ensure_loaded;
Optional Q semantics and presence via F
- Option<&T> in Q fetches T only when present, without gating presence.
- Presence is driven by type-level filters in F (With, Without, Or<(... )>), which gate backend queries.
Caching and force-refresh
- Default CachePolicy is UseCache. Identical loads coalesce within a frame and reuse cached results across frames.
- Use force_refresh to bypass cache and overwrite world state:
let _ = pq.force_refresh.ensure_loaded;
Resources
- #[persist(resource)] resources are fetched alongside any query call; versions are tracked and updated on commits automatically.
Scheduling notes
- Loads can run in Update or PostUpdate.
- Deferred world mutations from loads are applied before PreCommit; schedule readers after PersistenceSystemSet::PreCommit to observe fresh data in the same frame.