pub trait DatabaseObserver: Send + Sync {
// Provided methods
fn on_collection_created(&self, _name: &str, _kind: &CollectionType) { ... }
fn on_collection_deleted(&self, _name: &str) { ... }
fn on_upsert(&self, _collection: &str, _point_count: usize) { ... }
fn on_query(&self, _collection: &str, _duration_us: u64) { ... }
fn on_ddl_request(
&self,
operation: &str,
collection_name: &str,
) -> Result<()> { ... }
fn on_dml_mutation_request(
&self,
operation: &str,
collection_name: &str,
) -> Result<()> { ... }
fn on_query_request(
&self,
ctx: &QueryAccessContext<'_>,
) -> Result<AccessDecision> { ... }
}Expand description
Lifecycle and control-plane hooks for database events.
Implement this trait in velesdb-premium to attach RBAC, audit logging,
multi-tenant routing, or replication logic without modifying the core.
§Contract
- All methods have default implementations: telemetry/lifecycle hooks are no-ops and gate hooks are allow-all. A consumer that supplies no observer, or an observer overriding only a subset of methods, behaves exactly as it did before any hook existed.
- Implementations MUST be
Send + Sync. - Implementations MUST NOT panic.
- Access denial flows through a decision value
(
AccessDecision::Deny), not through theResulterror channel: theErrvariant of a gate is reserved for internal observer failures.
Every method has a default implementation, so adding future hooks is not a
breaking change for downstream implementers — they keep compiling by
relying on the defaults (the trait-level equivalent of #[non_exhaustive],
which Rust does not permit on traits directly).
§Example (Premium side)
use velesdb_core::{DatabaseObserver, CollectionType};
struct PremiumObserver { /* audit_log, rbac, tenant_router */ }
impl DatabaseObserver for PremiumObserver {
fn on_collection_created(&self, name: &str, kind: &CollectionType) {
// self.audit_log.record(...)
}
}Provided Methods§
Sourcefn on_collection_created(&self, _name: &str, _kind: &CollectionType)
fn on_collection_created(&self, _name: &str, _kind: &CollectionType)
Called after a collection is successfully created.
Sourcefn on_collection_deleted(&self, _name: &str)
fn on_collection_deleted(&self, _name: &str)
Called after a collection is successfully deleted.
Sourcefn on_upsert(&self, _collection: &str, _point_count: usize)
fn on_upsert(&self, _collection: &str, _point_count: usize)
Called after points are upserted into a collection.
Sourcefn on_query(&self, _collection: &str, _duration_us: u64)
fn on_query(&self, _collection: &str, _duration_us: u64)
Called after a query is executed, with the duration in microseconds.
Sourcefn on_ddl_request(&self, operation: &str, collection_name: &str) -> Result<()>
fn on_ddl_request(&self, operation: &str, collection_name: &str) -> Result<()>
Called before a DDL statement is executed.
Premium extensions can implement this to enforce RBAC policies (e.g., only admin users can CREATE/DROP collections).
Returns Ok(()) to allow the DDL operation, or Err(Error) to reject it.
Default implementation allows all DDL operations.
§Errors
Implementations should return an error to reject the DDL operation.
Sourcefn on_dml_mutation_request(
&self,
operation: &str,
collection_name: &str,
) -> Result<()>
fn on_dml_mutation_request( &self, operation: &str, collection_name: &str, ) -> Result<()>
Called before a mutating DML statement is executed.
Premium extensions can implement this to enforce RBAC policies (e.g., restrict INSERT EDGE, DELETE, or DELETE EDGE to authorized users).
Returns Ok(()) to allow the DML mutation, or Err(Error) to reject it.
Default implementation allows all DML mutations.
§Errors
Implementations should return an error to reject the DML mutation.
Sourcefn on_query_request(
&self,
ctx: &QueryAccessContext<'_>,
) -> Result<AccessDecision>
fn on_query_request( &self, ctx: &QueryAccessContext<'_>, ) -> Result<AccessDecision>
Called in the core use-case layer immediately before a query/read
executes, for every read path (vector search, text/BM25, hybrid,
graph traversal, VelesQL SELECT).
Premium extensions can implement this to enforce RBAC, tenant scoping, and row/collection filtering on the read path so that every consumer of core inherits control-plane behavior through the port.
Returns an AccessDecision the core enforces before producing
results: Allow executes unmodified,
Deny aborts with the supplied error and zero
results, and AllowWithScope narrows
the query before execution. The default allows every read unmodified,
so the open path is unchanged.
§Errors
Implementations return Err only for internal failures; access denial
is expressed through AccessDecision::Deny, not the Result error.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".