pub mod plugin;
pub mod plugins;
pub use plugin::{
Backend, BackendConformanceReport, BackendPluginContract, BackendPluginSurface,
BackendSupportState, all_plugins, has_runtime_implementation, plugin_for, plugin_for_kind,
support_state_for_kind, support_state_for_token,
};
use serde::{Deserialize, Serialize};
mod kind;
pub use kind::BackendKind;
impl BackendKind {
pub fn from_token(token: &str) -> Option<Self> {
match token.trim().to_ascii_lowercase().as_str() {
"postgres" => Some(Self::Postgres),
"mysql" => Some(Self::Mysql),
"sqlite" => Some(Self::Sqlite),
"sqlserver" => Some(Self::Mssql),
"clickhouse" => Some(Self::Clickhouse),
"redis" => Some(Self::Redis),
"memcached" => Some(Self::Memcached),
"qdrant" => Some(Self::Qdrant),
"weaviate" => Some(Self::Weaviate),
"pinecone" => Some(Self::Pinecone),
"minio" => Some(Self::Minio),
"s3" => Some(Self::S3),
"azureblob" => Some(Self::AzureBlob),
"gcs" => Some(Self::Gcs),
"mongodb" => Some(Self::Mongodb),
"elasticsearch" => Some(Self::Elasticsearch),
"neo4j" => Some(Self::Neo4j),
"cassandra" => Some(Self::Cassandra),
_ => None,
}
}
pub fn tier(&self) -> BackendTier {
match self {
Self::Postgres | Self::Mysql | Self::Sqlite | Self::Mssql | Self::Clickhouse => {
BackendTier::Sql
}
Self::Redis | Self::Memcached => BackendTier::Cache,
Self::Qdrant | Self::Weaviate | Self::Pinecone | Self::Elasticsearch => {
BackendTier::Vector
}
Self::Minio | Self::S3 | Self::AzureBlob | Self::Gcs => BackendTier::Object,
Self::Mongodb => BackendTier::Document,
Self::Neo4j => BackendTier::Graph,
Self::Cassandra => BackendTier::Column,
}
}
pub fn orm_tier(&self) -> &'static str {
match self.tier() {
BackendTier::Sql | BackendTier::Column => "relational",
BackendTier::Document => "document",
BackendTier::Cache => "kv",
BackendTier::Vector => "vector",
BackendTier::Object => "blob",
BackendTier::Graph => "graph",
}
}
pub const ALL: [BackendKind; 18] = [
BackendKind::Postgres,
BackendKind::Mysql,
BackendKind::Sqlite,
BackendKind::Mssql,
BackendKind::Clickhouse,
BackendKind::Redis,
BackendKind::Memcached,
BackendKind::Qdrant,
BackendKind::Weaviate,
BackendKind::Pinecone,
BackendKind::Minio,
BackendKind::S3,
BackendKind::AzureBlob,
BackendKind::Gcs,
BackendKind::Mongodb,
BackendKind::Elasticsearch,
BackendKind::Neo4j,
BackendKind::Cassandra,
];
pub fn role(&self) -> BackendRole {
match self {
Self::Postgres | Self::Mysql | Self::Sqlite | Self::Mssql => BackendRole::Canonical,
#[cfg(feature = "redis")]
Self::Redis => BackendRole::Canonical,
#[cfg(not(feature = "redis"))]
Self::Redis => BackendRole::Projection,
#[cfg(feature = "mongodb-native")]
Self::Mongodb => BackendRole::Canonical,
#[cfg(not(feature = "mongodb-native"))]
Self::Mongodb => BackendRole::Projection,
#[cfg(feature = "cassandra")]
Self::Cassandra => BackendRole::Canonical,
#[cfg(not(feature = "cassandra"))]
Self::Cassandra => BackendRole::Projection,
#[cfg(feature = "neo4j")]
Self::Neo4j => BackendRole::Canonical,
#[cfg(not(feature = "neo4j"))]
Self::Neo4j => BackendRole::Projection,
Self::Qdrant | Self::Pinecone | Self::Weaviate | Self::Elasticsearch => {
BackendRole::Projection
}
#[cfg(feature = "clickhouse")]
Self::Clickhouse => BackendRole::Canonical,
#[cfg(not(feature = "clickhouse"))]
Self::Clickhouse => BackendRole::Projection,
Self::Memcached | Self::Minio | Self::S3 | Self::AzureBlob | Self::Gcs => {
BackendRole::Projection
}
}
}
pub fn capabilities(&self) -> BackendCapability {
self.capabilities_v2().derive_v1()
}
fn capabilities_v1_fields(&self) -> BackendCapability {
match self {
Self::Postgres => BackendCapability {
supports_sql_ddl: true,
supports_transactions: true,
supports_xa: false,
supports_two_phase_commit: true,
supports_rls: true,
supports_vector_search: false,
supports_streaming: true,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: true,
supports_idempotency: true,
supports_schema_migration: true,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "strong".into(),
},
Self::Mysql => BackendCapability {
supports_sql_ddl: true,
supports_transactions: true,
supports_xa: cfg!(feature = "mysql"),
supports_two_phase_commit: cfg!(feature = "mysql"),
supports_rls: false, supports_vector_search: false,
supports_streaming: true,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: true, supports_idempotency: true,
supports_schema_migration: true,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "strong".into(),
},
Self::Mssql => BackendCapability {
supports_sql_ddl: true,
supports_transactions: true, supports_xa: false, supports_two_phase_commit: false,
supports_rls: false, supports_vector_search: false,
supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true, supports_schema_migration: true,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "strong".into(),
},
Self::Sqlite => BackendCapability {
supports_sql_ddl: true,
supports_transactions: true,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: false, supports_vector_search: false,
supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: true, supports_idempotency: true,
supports_schema_migration: true,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "strong".into(),
},
Self::Clickhouse => BackendCapability {
supports_sql_ddl: true,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: true,
supports_ttl: true,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: false,
supports_schema_migration: true,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "eventual".into(),
},
Self::Redis => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: true,
supports_ttl: true,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: false,
supports_resource_lifecycle: false,
max_payload_bytes: 536_870_912, consistency_model: "eventual".into(),
},
Self::Memcached => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false, supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: false,
supports_ttl: true, is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true, supports_schema_migration: false,
supports_hybrid_search: false,
supports_resource_lifecycle: false, max_payload_bytes: 1_048_576, consistency_model: "eventual".into(),
},
Self::Qdrant => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: true,
supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: true,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "eventual".into(),
},
Self::Weaviate => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true,
supports_vector_search: true,
supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: true,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "eventual".into(),
},
Self::Pinecone => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: true,
supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: true, supports_resource_lifecycle: true,
max_payload_bytes: 2_097_152, consistency_model: "eventual".into(),
},
Self::Minio | Self::S3 => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: true,
supports_ttl: true,
is_object_store: true,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 5_368_709_120, consistency_model: "strong".into(),
},
Self::AzureBlob | Self::Gcs => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: true,
supports_ttl: true,
is_object_store: true,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 5_368_709_120, consistency_model: "strong".into(),
},
Self::Mongodb => BackendCapability {
supports_sql_ddl: false,
supports_transactions: true,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: true,
supports_ttl: true,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true,
supports_schema_migration: false,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 16_777_216, consistency_model: "causal".into(),
},
Self::Elasticsearch => BackendCapability {
supports_sql_ddl: false,
supports_transactions: false, supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: true, supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true, supports_schema_migration: false,
supports_hybrid_search: true, supports_resource_lifecycle: true,
max_payload_bytes: 104_857_600, consistency_model: "eventual".into(),
},
Self::Cassandra => BackendCapability {
supports_sql_ddl: true,
supports_transactions: false, supports_xa: false,
supports_two_phase_commit: false,
supports_rls: false, supports_vector_search: false,
supports_streaming: false,
supports_ttl: true, is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: true, supports_schema_migration: true,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "tunable".into(), },
Self::Neo4j => BackendCapability {
supports_sql_ddl: false,
supports_transactions: true,
supports_xa: false,
supports_two_phase_commit: false,
supports_rls: true, supports_vector_search: false,
supports_streaming: false,
supports_ttl: false,
is_object_store: false,
is_migration_ledger_capable: false,
supports_idempotency: false,
supports_schema_migration: false,
supports_hybrid_search: false,
supports_resource_lifecycle: true,
max_payload_bytes: 0,
consistency_model: "strong".into(),
},
}
}
pub fn capabilities_v2(&self) -> BackendCapabilityV2 {
let v1 = self.capabilities_v1_fields();
let dispatch_operations = self.supported_operations();
let lifecycle = self.lifecycle_support();
let system_store = self.system_store_support();
let canonical_candidate = self.canonical_candidate_profile();
let canonical_goal = self.canonical_promotion_goal();
let consistency_model: &'static str = match v1.consistency_model.as_str() {
"strong" => "strong",
"eventual" => "eventual",
"causal" => "causal",
"tunable" => "tunable",
"read-your-writes" => "read-your-writes",
_ => "unknown",
};
BackendCapabilityV2 {
native_executor: crate::backend::has_runtime_implementation(self),
compiler_mediated: !matches!(
self,
Self::Redis
| Self::Memcached
| Self::Minio
| Self::S3
| Self::AzureBlob
| Self::Gcs
),
dispatch_operations,
lifecycle,
system_store,
control_plane_ha_level: self.control_plane_ha_level(),
canonical_candidate,
canonical_goal,
transport_label: self.transport_label(),
live_probe: self.has_runtime_probe(),
supports_sql_ddl: v1.supports_sql_ddl,
supports_transactions: v1.supports_transactions,
supports_xa: v1.supports_xa,
supports_two_phase_commit: v1.supports_two_phase_commit,
supports_rls: v1.supports_rls,
supports_vector_search: v1.supports_vector_search,
supports_streaming: v1.supports_streaming,
supports_ttl: v1.supports_ttl,
is_object_store: v1.is_object_store,
is_migration_ledger_capable: v1.is_migration_ledger_capable,
supports_idempotency: v1.supports_idempotency,
supports_schema_migration: v1.supports_schema_migration,
supports_hybrid_search: v1.supports_hybrid_search,
max_payload_bytes: v1.max_payload_bytes,
consistency_model,
}
}
pub fn lifecycle_support(&self) -> LifecycleSupport {
match self {
Self::Postgres | Self::Mysql | Self::Sqlite => LifecycleSupport::CatalogMigration,
Self::Mssql | Self::Cassandra => LifecycleSupport::CompilerMediated,
Self::Redis | Self::Memcached => LifecycleSupport::None,
Self::Minio
| Self::S3
| Self::AzureBlob
| Self::Gcs
| Self::Qdrant
| Self::Weaviate
| Self::Pinecone
| Self::Mongodb
| Self::Elasticsearch
| Self::Neo4j
| Self::Clickhouse => LifecycleSupport::Native,
}
}
pub fn system_store_support(&self) -> SystemStoreSupport {
if CANONICAL_SYSTEM_STORE_BACKENDS.contains(self) {
SystemStoreSupport::Full
} else {
SystemStoreSupport::None
}
}
pub fn control_plane_ha_level(&self) -> ControlPlaneHaLevel {
match self {
Self::Sqlite => ControlPlaneHaLevel::DevSingleNode,
Self::Memcached | Self::Minio | Self::S3 | Self::AzureBlob | Self::Gcs => {
ControlPlaneHaLevel::ProjectionOnly
}
Self::Postgres
| Self::Mysql
| Self::Mssql
| Self::Mongodb
| Self::Cassandra
| Self::Neo4j
| Self::Redis
| Self::Clickhouse
| Self::Qdrant
| Self::Weaviate
| Self::Pinecone
| Self::Elasticsearch => ControlPlaneHaLevel::HaCanonical,
}
}
pub fn transport_label(&self) -> &'static str {
match self {
Self::Postgres => "postgres",
Self::Mysql => "mysql",
Self::Sqlite => "sqlite",
Self::Mssql => "tds",
Self::Clickhouse => "http",
Self::Redis => "async-redis",
Self::Memcached => "memcache",
Self::Qdrant => "http",
Self::Weaviate => "http",
Self::Pinecone => "http",
Self::Minio | Self::S3 => "aws-sdk-s3",
Self::AzureBlob => "azure-sdk-blob",
Self::Gcs => "google-cloud-storage",
Self::Mongodb => "atlas_data_api",
Self::Elasticsearch => "http",
Self::Neo4j => "http",
Self::Cassandra => "cql",
}
}
pub fn has_runtime_probe(&self) -> bool {
matches!(
self,
Self::Postgres
| Self::Redis
| Self::Qdrant
| Self::Minio
| Self::S3
| Self::Mongodb
| Self::Neo4j
| Self::Clickhouse
)
}
pub fn canonical_candidate_profile(&self) -> CanonicalCandidateProfile {
match self {
Self::Postgres | Self::Mysql | Self::Sqlite | Self::Mssql => {
CanonicalCandidateProfile::Implemented
}
#[cfg(feature = "redis")]
Self::Redis => CanonicalCandidateProfile::Implemented,
#[cfg(not(feature = "redis"))]
Self::Redis => CanonicalCandidateProfile::CacheDurabilityRequired,
#[cfg(feature = "mongodb-native")]
Self::Mongodb => CanonicalCandidateProfile::Implemented,
#[cfg(not(feature = "mongodb-native"))]
Self::Mongodb => CanonicalCandidateProfile::NativeDriverRequired,
#[cfg(feature = "cassandra")]
Self::Cassandra => CanonicalCandidateProfile::Implemented,
#[cfg(not(feature = "cassandra"))]
Self::Cassandra => CanonicalCandidateProfile::SemanticsGated,
#[cfg(feature = "neo4j")]
Self::Neo4j => CanonicalCandidateProfile::Implemented,
#[cfg(not(feature = "neo4j"))]
Self::Neo4j => CanonicalCandidateProfile::SemanticsGated,
#[cfg(feature = "clickhouse")]
Self::Clickhouse => CanonicalCandidateProfile::Implemented,
#[cfg(not(feature = "clickhouse"))]
Self::Clickhouse => CanonicalCandidateProfile::SemanticsGated,
Self::Qdrant => CanonicalCandidateProfile::VectorNativeCasUnsupported,
Self::Pinecone | Self::Weaviate | Self::Elasticsearch => {
CanonicalCandidateProfile::VectorFeasibilityRequired
}
Self::Minio | Self::S3 | Self::AzureBlob | Self::Gcs => {
CanonicalCandidateProfile::ObjectConditionalWrites
}
Self::Memcached => CanonicalCandidateProfile::ExplicitlyNotSupported,
}
}
pub fn canonical_promotion_goal(&self) -> &'static str {
match self {
Self::Postgres | Self::Mysql | Self::Sqlite | Self::Mssql => {
"canonical store implemented; keep five-contract conformance live"
}
#[cfg(feature = "redis")]
Self::Redis => {
"B.14: native Redis SystemStores implemented; runtime registration requires durable AOF profile and live conformance"
}
#[cfg(not(feature = "redis"))]
Self::Redis => {
"B.14: compile Redis native SystemStores and require durable AOF profile before canonical promotion"
}
#[cfg(feature = "mongodb-native")]
Self::Mongodb => {
"canonical store implemented for native MongoDB replica-set/sharded topology; keep topology-gated conformance live"
}
#[cfg(not(feature = "mongodb-native"))]
Self::Mongodb => {
"B.9: compile native MongoDB SystemStores and prove replica-set/sharded durability; scalar/Data-API builds remain projection"
}
Self::Clickhouse => {
"B.10: prove canonical semantics for leases, outbox, saga, audit, and migration stores or keep append analytics projection"
}
Self::Neo4j => {
"B.10: implement graph-native SystemStores with transactional claims, idempotent audit, and recoverable saga/outbox state"
}
Self::Cassandra => {
"B.10: implement wide-column SystemStores with compare-and-set claims, quorum guidance, and replay-safe progress tokens"
}
Self::Qdrant => {
"B.11: do not use Qdrant as a HA SystemStores backend until distributed lease CAS, monotonic outbox sequence allocation, and recovery fences are conformance-proven"
}
Self::Pinecone => {
"B.12: keep Pinecone projection-only until canonical SystemStores semantics are proven independently of vector upsert/search APIs"
}
Self::Weaviate => {
"B.12: keep Weaviate projection-only until canonical SystemStores semantics are proven independently of vector object APIs"
}
Self::Elasticsearch => {
"B.12: keep Elasticsearch projection-only until canonical SystemStores semantics are proven independently of search/index APIs"
}
Self::Minio | Self::S3 | Self::AzureBlob | Self::Gcs => {
"B.13: prove object-store canonical profile using conditional writes, generation/etag fencing, listing recovery, and multipart safety"
}
Self::Memcached => {
"B.14: explicitly unsupported for canonical state unless a durable backend profile is added and conformance-proven"
}
}
}
pub fn canonical_feasibility_profile(&self) -> CanonicalFeasibilityProfile {
let backend = self.as_str();
let candidate = self.canonical_candidate_profile();
let implemented = self.system_store_support().is_canonical();
match self {
Self::Postgres | Self::Mysql | Self::Sqlite | Self::Mssql => {
CanonicalFeasibilityProfile {
backend,
family: "sql",
candidate,
implemented,
atomic_claim_strategy: "row-level UPDATE ... WHERE owner IS NULL guarded by a transactional lock",
ordered_progress_strategy: "monotonic outbox sequence column under transactional isolation",
tenant_isolation_strategy: "tenant_id column + RLS / session-context enforcement",
read_fence_strategy: "transactional write-progress token (LSN / GTID / rowversion)",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("sql"),
durability_prerequisites: &[],
blocking_gaps: &[],
live_conformance_env: Some(match self {
Self::Mysql => "UDB_MYSQL_DSN",
Self::Mssql => "UDB_MSSQL_DSN",
_ => "UDB_PG_DSN",
}),
}
}
Self::Mongodb => CanonicalFeasibilityProfile {
backend,
family: "document",
candidate,
implemented,
atomic_claim_strategy: "findOneAndUpdate with an owner guard (single-document atomic CAS)",
ordered_progress_strategy: "monotonic counter document + change-stream resume token",
tenant_isolation_strategy: "tenant_id field + per-collection scoping",
read_fence_strategy: "majority read-concern resume token / cluster time",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("document"),
durability_prerequisites: &[
"replica set or sharded cluster with majority write concern",
],
blocking_gaps: if implemented {
&[]
} else {
&[
"mongodb-native feature not compiled — Data-API/scalar build stays projection",
]
},
live_conformance_env: Some("UDB_MONGODB_DSN"),
},
Self::Clickhouse => CanonicalFeasibilityProfile {
backend,
family: "column",
candidate,
implemented,
atomic_claim_strategy: "none native — append-only engine has no row update/lock for CAS claims",
ordered_progress_strategy: "insert-block ordering only (eventual)",
tenant_isolation_strategy: "session-setting scoping (no row policy)",
read_fence_strategy: "none proven — eventual visibility of inserted blocks",
read_fence_supported: false,
supported_consistency_modes: supported_consistency_modes_for_family("column"),
durability_prerequisites: &[],
blocking_gaps: if cfg!(feature = "clickhouse") {
&[]
} else {
&[
"no atomic claim/lease primitive",
"no multi-statement transaction for saga/outbox",
"no canonical SystemStores module for column store",
]
},
live_conformance_env: Some("UDB_COLUMN_DSN"),
},
Self::Neo4j => CanonicalFeasibilityProfile {
backend,
family: "graph",
candidate,
implemented,
atomic_claim_strategy: "MERGE under a write transaction with a node lock",
ordered_progress_strategy: "sequence node + transaction commit order",
tenant_isolation_strategy: "tenant property + label scoping",
read_fence_strategy: "transaction bookmark",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("graph"),
durability_prerequisites: &[],
blocking_gaps: if cfg!(feature = "neo4j") {
&[]
} else {
&["graph-native SystemStores (leases/outbox/saga/audit) not implemented"]
},
live_conformance_env: Some("UDB_GRAPH_DSN"),
},
Self::Cassandra => CanonicalFeasibilityProfile {
backend,
family: "column",
candidate,
implemented,
atomic_claim_strategy: "lightweight transaction compare-and-set (IF clause, Paxos)",
ordered_progress_strategy: "monotonic timeuuid under quorum writes",
tenant_isolation_strategy: "tenant partition-key prefix",
read_fence_strategy: "LWT applied flag at quorum",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("column"),
durability_prerequisites: &[
"QUORUM/LOCAL_QUORUM read+write consistency configured",
],
blocking_gaps: if cfg!(feature = "cassandra") {
&[]
} else {
&[
"wide-column SystemStores not implemented",
"quorum operator guidance + replay-safe progress token unproven",
]
},
live_conformance_env: Some("UDB_CASSANDRA_DSN"),
},
Self::Qdrant => CanonicalFeasibilityProfile {
backend,
family: "vector",
candidate,
implemented,
atomic_claim_strategy: "dedicated system collection + deterministic point IDs + strongly ordered point writes; adapter serializes read-modify-write system operations through the canonical store",
ordered_progress_strategy: "monotonic outbox sequence document persisted as a Qdrant point under strong write ordering",
tenant_isolation_strategy: "dedicated system collection per UDB instance plus tenant/project fields inside system records",
read_fence_strategy: "outbox sequence durability token stored in the system collection and polled by wait_for_token",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("vector"),
durability_prerequisites: VECTOR_CANONICAL_PLANE_PREREQS,
blocking_gaps: if implemented {
&[]
} else {
QDRANT_BLOCKING_GAPS
},
live_conformance_env: Some("UDB_QDRANT_URL"),
},
Self::Pinecone => CanonicalFeasibilityProfile {
backend,
family: "vector",
candidate,
implemented,
atomic_claim_strategy: "dedicated namespace + deterministic vector IDs; adapter serializes read-modify-write system operations through the canonical store",
ordered_progress_strategy: "monotonic outbox sequence metadata record persisted through Pinecone vector upsert",
tenant_isolation_strategy: "dedicated namespace per UDB instance plus tenant/project fields inside system records",
read_fence_strategy: "outbox sequence durability token stored in the namespace and polled by wait_for_token",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("vector"),
durability_prerequisites: VECTOR_CANONICAL_PLANE_PREREQS,
blocking_gaps: if implemented {
&[]
} else {
PINECONE_BLOCKING_GAPS
},
live_conformance_env: Some("UDB_PINECONE_DSN"),
},
Self::Weaviate => CanonicalFeasibilityProfile {
backend,
family: "vector",
candidate,
implemented,
atomic_claim_strategy: "dedicated class + deterministic object UUIDs; adapter serializes read-modify-write system operations through the canonical store",
ordered_progress_strategy: "monotonic outbox sequence object persisted through Weaviate object upsert",
tenant_isolation_strategy: "dedicated system class per UDB instance plus tenant/project fields inside system records",
read_fence_strategy: "outbox sequence durability token stored in the class and polled by wait_for_token",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("vector"),
durability_prerequisites: VECTOR_CANONICAL_PLANE_PREREQS,
blocking_gaps: if implemented {
&[]
} else {
WEAVIATE_BLOCKING_GAPS
},
live_conformance_env: Some("UDB_WEAVIATE_DSN"),
},
Self::Elasticsearch => CanonicalFeasibilityProfile {
backend,
family: "vector",
candidate,
implemented,
atomic_claim_strategy: "dedicated system index + deterministic document IDs; adapter serializes read-modify-write system operations through the canonical store",
ordered_progress_strategy: "monotonic outbox sequence document persisted with refresh=wait_for",
tenant_isolation_strategy: "dedicated system index per UDB instance plus tenant/project fields inside system records",
read_fence_strategy: "outbox sequence durability token stored in the index and polled by wait_for_token",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("vector"),
durability_prerequisites: VECTOR_CANONICAL_PLANE_PREREQS,
blocking_gaps: if implemented {
&[]
} else {
ELASTICSEARCH_VECTOR_BLOCKING_GAPS
},
live_conformance_env: Some("UDB_ELASTIC_DSN"),
},
Self::S3 | Self::Minio => CanonicalFeasibilityProfile {
backend,
family: "object",
candidate,
implemented,
atomic_claim_strategy: "conditional PUT via If-None-Match / If-Match ETag + version-id",
ordered_progress_strategy: "no native cross-object sequence — needs single-writer or external sequencer profile",
tenant_isolation_strategy: "tenant/project key prefix + bucket policy",
read_fence_strategy: "ETag / version-id per object",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("object"),
durability_prerequisites: OBJECT_DURABILITY_PREREQS,
blocking_gaps: OBJECT_BLOCKING_GAPS,
live_conformance_env: Some("UDB_BENCH_S3_ENDPOINT"),
},
Self::AzureBlob => CanonicalFeasibilityProfile {
backend,
family: "object",
candidate,
implemented,
atomic_claim_strategy: "blob lease + If-Match ETag conditional write",
ordered_progress_strategy: "no native cross-blob sequence — needs single-writer or external sequencer profile",
tenant_isolation_strategy: "tenant/project key prefix + container policy",
read_fence_strategy: "ETag per blob",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("object"),
durability_prerequisites: OBJECT_DURABILITY_PREREQS,
blocking_gaps: OBJECT_BLOCKING_GAPS,
live_conformance_env: Some("UDB_BENCH_AZURE_BLOB"),
},
Self::Gcs => CanonicalFeasibilityProfile {
backend,
family: "object",
candidate,
implemented,
atomic_claim_strategy: "x-goog-if-generation-match generation precondition on write",
ordered_progress_strategy: "no native cross-object sequence — needs single-writer or external sequencer profile",
tenant_isolation_strategy: "tenant/project key prefix + bucket IAM",
read_fence_strategy: "object generation number",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("object"),
durability_prerequisites: OBJECT_DURABILITY_PREREQS,
blocking_gaps: OBJECT_BLOCKING_GAPS,
live_conformance_env: Some("UDB_BENCH_GCS"),
},
Self::Redis => CanonicalFeasibilityProfile {
backend,
family: "cache",
candidate,
implemented,
atomic_claim_strategy: "Lua / MULTI compare-and-set on an owner key with a TTL lease",
ordered_progress_strategy: "INCR sequence + per-record JSON documents (Streams-ready)",
tenant_isolation_strategy: "udb:system:{instance} key-prefix namespacing",
read_fence_strategy: "INCR durability token gated on AOF fsync",
read_fence_supported: true,
supported_consistency_modes: supported_consistency_modes_for_family("cache"),
durability_prerequisites: REDIS_DURABILITY_PREREQS,
blocking_gaps: if implemented {
&[]
} else {
&["redis feature not compiled — native SystemStores absent"]
},
live_conformance_env: Some("UDB_INTEGRATION_REDIS_URL"),
},
Self::Memcached => CanonicalFeasibilityProfile {
backend,
family: "cache",
candidate,
implemented,
atomic_claim_strategy: "single-key CAS token only — no multi-key claim",
ordered_progress_strategy: "none — no durable log or scan to recover outbox order",
tenant_isolation_strategy: "key-prefix only (no enumeration to audit)",
read_fence_strategy: "none — no durable write-progress token",
read_fence_supported: false,
supported_consistency_modes: supported_consistency_modes_for_family("cache"),
durability_prerequisites: &[
"a durable persistence layer Memcached does not provide",
],
blocking_gaps: MEMCACHED_BLOCKING_GAPS,
live_conformance_env: None,
},
}
}
pub fn default_env_key(&self) -> &'static str {
match self {
Self::Postgres => "UDB_SQL_DSN",
Self::Mysql => "UDB_MYSQL_DSN",
Self::Sqlite => "UDB_SQLITE_DSN",
Self::Mssql => "UDB_MSSQL_DSN",
Self::Clickhouse => "UDB_COLUMN_DSN",
Self::Redis => "UDB_CACHE_DSN",
Self::Memcached => "UDB_MEMCACHED_DSN",
Self::Qdrant => "UDB_VECTOR_DSN",
Self::Weaviate => "UDB_WEAVIATE_DSN",
Self::Pinecone => "UDB_PINECONE_DSN",
Self::Minio => "UDB_OBJECT_DSN",
Self::S3 => "UDB_S3_DSN",
Self::AzureBlob => "UDB_AZUREBLOB_DSN",
Self::Gcs => "UDB_GCS_DSN",
Self::Mongodb => "UDB_NOSQL_DSN",
Self::Elasticsearch => "UDB_ELASTIC_DSN",
Self::Neo4j => "UDB_GRAPH_DSN",
Self::Cassandra => "UDB_CASSANDRA_DSN",
}
}
pub fn dsn_scheme(&self) -> String {
format!("udb+{}+{}", self.tier().as_str(), self.as_str())
}
pub fn from_store_kind(store_kind: &str, backend_hint: &str) -> Option<Self> {
if !backend_hint.is_empty() {
match backend_hint.to_lowercase().as_str() {
"postgres" | "postgresql" => return Some(Self::Postgres),
"mysql" | "mariadb" => return Some(Self::Mysql),
"sqlite" => return Some(Self::Sqlite),
"sqlserver" | "mssql" => return Some(Self::Mssql),
"clickhouse" => return Some(Self::Clickhouse),
"redis" => return Some(Self::Redis),
"memcached" => return Some(Self::Memcached),
"qdrant" => return Some(Self::Qdrant),
"weaviate" => return Some(Self::Weaviate),
"pinecone" => return Some(Self::Pinecone),
"minio" => return Some(Self::Minio),
"s3" => return Some(Self::S3),
"azureblob" | "azure" => return Some(Self::AzureBlob),
"gcs" => return Some(Self::Gcs),
"mongodb" | "mongo" => return Some(Self::Mongodb),
"elasticsearch" | "elastic" => return Some(Self::Elasticsearch),
"neo4j" => return Some(Self::Neo4j),
"cassandra" | "scylla" => return Some(Self::Cassandra),
_ => {}
}
}
match store_kind {
"sql" | "relational" => Some(Self::Postgres),
"cache" | "kv" | "key_value" | "key-value" | "keyvalue" => Some(Self::Redis),
"vector" => Some(Self::Qdrant),
"storage" | "object" | "blob" => Some(Self::Minio),
"nosql" | "document" => Some(Self::Mongodb),
"graph" => Some(Self::Neo4j),
"timeseries" | "time-series" | "column" | "columnar" | "wide-column" => {
Some(Self::Clickhouse)
}
"search" => Some(Self::Elasticsearch),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BackendTier {
Sql,
Cache,
Vector,
Object,
Document,
Graph,
Column,
}
impl BackendTier {
pub fn as_str(&self) -> &'static str {
match self {
Self::Sql => "sql",
Self::Cache => "cache",
Self::Vector => "vector",
Self::Object => "object",
Self::Document => "document",
Self::Graph => "graph",
Self::Column => "column",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct BackendCapability {
pub supports_sql_ddl: bool,
pub supports_transactions: bool,
pub supports_xa: bool,
pub supports_two_phase_commit: bool,
pub supports_rls: bool,
pub supports_vector_search: bool,
pub supports_streaming: bool,
pub supports_ttl: bool,
pub is_object_store: bool,
pub is_migration_ledger_capable: bool,
pub supports_idempotency: bool,
pub supports_schema_migration: bool,
pub supports_hybrid_search: bool,
pub supports_resource_lifecycle: bool,
pub max_payload_bytes: u64,
pub consistency_model: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LifecycleSupport {
None,
CompilerMediated,
Native,
CatalogMigration,
}
impl LifecycleSupport {
pub fn as_str(self) -> &'static str {
match self {
Self::None => "none",
Self::CompilerMediated => "compiler_mediated",
Self::Native => "native",
Self::CatalogMigration => "catalog_migration",
}
}
pub fn admits_dispatch(self) -> bool {
!matches!(self, Self::None)
}
pub fn is_native_executor(self) -> bool {
matches!(self, Self::Native)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SystemStoreSupport {
None,
Full,
}
impl SystemStoreSupport {
pub fn as_str(self) -> &'static str {
match self {
Self::None => "none",
Self::Full => "full",
}
}
pub fn is_canonical(self) -> bool {
matches!(self, Self::Full)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ControlPlaneHaLevel {
ProjectionOnly,
DevSingleNode,
SystemStoreCapable,
HaCanonical,
}
impl ControlPlaneHaLevel {
pub fn as_str(self) -> &'static str {
match self {
Self::ProjectionOnly => "projection_only",
Self::DevSingleNode => "dev_single_node",
Self::SystemStoreCapable => "system_store_capable",
Self::HaCanonical => "ha_canonical",
}
}
pub fn parse_deployment_tier(raw: &str) -> Option<Self> {
match raw.trim().to_ascii_lowercase().as_str() {
"dev_single_node" | "dev-single-node" | "dev" | "single_node" | "single-node" => {
Some(Self::DevSingleNode)
}
"system_store_capable" | "system-store-capable" | "system" | "system_store" => {
Some(Self::SystemStoreCapable)
}
"ha_canonical" | "ha-canonical" | "ha" | "canonical" => Some(Self::HaCanonical),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CanonicalCandidateProfile {
Implemented,
NativeDriverRequired,
SemanticsGated,
VectorAtomicClaims,
VectorNativeCasUnsupported,
VectorFeasibilityRequired,
ObjectConditionalWrites,
CacheDurabilityRequired,
ExplicitlyNotSupported,
}
impl CanonicalCandidateProfile {
pub fn as_str(self) -> &'static str {
match self {
Self::Implemented => "implemented",
Self::NativeDriverRequired => "native_driver_required",
Self::SemanticsGated => "semantics_gated",
Self::VectorAtomicClaims => "vector_atomic_claims",
Self::VectorNativeCasUnsupported => "vector_native_cas_unsupported",
Self::VectorFeasibilityRequired => "vector_feasibility_required",
Self::ObjectConditionalWrites => "object_conditional_writes",
Self::CacheDurabilityRequired => "cache_durability_required",
Self::ExplicitlyNotSupported => "explicitly_not_supported",
}
}
}
fn default_canonical_candidate_profile() -> CanonicalCandidateProfile {
CanonicalCandidateProfile::ExplicitlyNotSupported
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct CanonicalFeasibilityProfile {
pub backend: &'static str,
pub family: &'static str,
pub candidate: CanonicalCandidateProfile,
pub implemented: bool,
pub atomic_claim_strategy: &'static str,
pub ordered_progress_strategy: &'static str,
pub tenant_isolation_strategy: &'static str,
pub read_fence_strategy: &'static str,
pub read_fence_supported: bool,
pub supported_consistency_modes: Vec<&'static str>,
pub durability_prerequisites: &'static [&'static str],
pub blocking_gaps: &'static [&'static str],
pub live_conformance_env: Option<&'static str>,
}
fn supported_consistency_modes_for_family(family: &str) -> Vec<&'static str> {
match family {
"sql" => vec![
"strong",
"read_your_writes",
"bounded_staleness",
"eventual",
"projection_ok",
"cache_ok",
],
"document" | "graph" | "vector" => {
vec!["strong", "bounded_staleness", "eventual", "projection_ok"]
}
"column" => vec!["bounded_staleness", "eventual"],
"object" => vec!["strong", "eventual"],
"cache" => vec!["cache_ok", "eventual"],
_ => vec!["eventual"],
}
}
const OBJECT_DURABILITY_PREREQS: &[&str] = &[
"bucket/container versioning enabled",
"object-lock / retention enabled for audit-chain immutability",
"conditional-write (precondition) support enabled on the endpoint",
"lifecycle/retention policy provisioned for outbox compaction",
];
const OBJECT_BLOCKING_GAPS: &[&str] = &[
"no native cross-object monotonic sequence — outbox ordering needs a documented single-writer or external-sequencer profile",
"list-after-write / read-after-overwrite consistency must be proven per provider",
"multipart write atomicity for large system records is unproven",
"no canonical SystemStores module compiled for object backends",
];
const REDIS_DURABILITY_PREREQS: &[&str] = &[
"AOF persistence enabled (INFO persistence aof_enabled:1)",
"appendfsync everysec or always",
"replica or RDB+AOF retention for failover durability",
"non-volatile maxmemory-policy (no eviction of system keys)",
];
const MEMCACHED_BLOCKING_GAPS: &[&str] = &[
"no durable storage — items live in volatile RAM",
"no key scan / enumeration to recover or audit system state",
"no atomic multi-key claim (single-key CAS only)",
"eviction can silently drop committed system state",
];
const VECTOR_CANONICAL_PLANE_PREREQS: &[&str] = &[
"atomic claim winner for leases and task claims",
"durable monotonic outbox progress token across writers",
"tenant/project-scoped system-state namespace",
"read fence that can prove later reads observe writes up to a token",
];
const QDRANT_BLOCKING_GAPS: &[&str] = &[
"distributed leases and outbox sequence allocation are not conformance-proven for multi-writer HA",
"live Qdrant HA SystemStores conformance proof has not run for this build profile",
"native compare-and-set / insert-if-absent primitive is not documented for point or payload updates",
"insert_only avoids overwriting an existing point but does not return a distributed lease winner",
"filtered payload updates can select existing points but do not return an atomic claim winner",
"WAL-backed writes make individual point mutations durable, but do not create a monotonic cross-point outbox sequence",
"strong write ordering orders submitted operations but does not make read-modify-write lease acquisition atomic across brokers",
];
const PINECONE_BLOCKING_GAPS: &[&str] = &[
"distributed leases and outbox sequence allocation are not conformance-proven for multi-writer HA",
"metadata-filter update can change matching records, but does not expose compare-and-set or a claim winner",
"upsert/update by id has no native monotonic outbox sequence",
"namespace scoping is usable for tenant isolation but not for durable read fencing",
"no canonical SystemStores module for the vector plane",
];
const WEAVIATE_BLOCKING_GAPS: &[&str] = &[
"distributed leases and outbox sequence allocation are not conformance-proven for multi-writer HA",
"object update/PATCH and consistency levels do not expose native compare-and-set claim semantics",
"leaderless/tunable consistency is not a durable ordered outbox token",
"multi-tenancy scopes records but does not provide recovery ordering for SystemStores",
"no canonical SystemStores module for the vector plane",
];
const ELASTICSEARCH_VECTOR_BLOCKING_GAPS: &[&str] = &[
"distributed leases and outbox sequence allocation are not conformance-proven for multi-writer HA",
"optimistic concurrency can guard a single document update, but vector/search SystemStores are not implemented",
"ordered outbox, lease, saga, audit, and migration contracts are not mapped to Elasticsearch",
"tenant scoping and read fencing need a canonical index/profile, not projection search indexes",
];
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct BackendCapabilityV2 {
pub native_executor: bool,
pub compiler_mediated: bool,
pub dispatch_operations: Vec<&'static str>,
pub lifecycle: LifecycleSupport,
pub system_store: SystemStoreSupport,
pub control_plane_ha_level: ControlPlaneHaLevel,
pub canonical_candidate: CanonicalCandidateProfile,
pub canonical_goal: &'static str,
pub transport_label: &'static str,
pub live_probe: bool,
pub supports_sql_ddl: bool,
pub supports_transactions: bool,
pub supports_xa: bool,
pub supports_two_phase_commit: bool,
pub supports_rls: bool,
pub supports_vector_search: bool,
pub supports_streaming: bool,
pub supports_ttl: bool,
pub is_object_store: bool,
pub is_migration_ledger_capable: bool,
pub supports_idempotency: bool,
pub supports_schema_migration: bool,
pub supports_hybrid_search: bool,
pub max_payload_bytes: u64,
pub consistency_model: &'static str,
}
impl BackendCapabilityV2 {
pub fn derive_v1(&self) -> BackendCapability {
BackendCapability {
supports_sql_ddl: self.supports_sql_ddl,
supports_transactions: self.supports_transactions,
supports_xa: self.supports_xa,
supports_two_phase_commit: self.supports_two_phase_commit,
supports_rls: self.supports_rls,
supports_vector_search: self.supports_vector_search,
supports_streaming: self.supports_streaming,
supports_ttl: self.supports_ttl,
is_object_store: self.is_object_store,
is_migration_ledger_capable: self.is_migration_ledger_capable,
supports_idempotency: self.supports_idempotency,
supports_schema_migration: self.supports_schema_migration,
supports_hybrid_search: self.supports_hybrid_search,
supports_resource_lifecycle: self.lifecycle.admits_dispatch(),
max_payload_bytes: self.max_payload_bytes,
consistency_model: self.consistency_model.to_string(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BackendRole {
Canonical,
Projection,
Both,
}
impl BackendRole {
pub fn as_str(self) -> &'static str {
match self {
Self::Canonical => "canonical",
Self::Projection => "projection",
Self::Both => "both",
}
}
pub fn can_host_system_tables(self) -> bool {
matches!(self, Self::Canonical | Self::Both)
}
pub fn can_be_durability_anchor(self) -> bool {
matches!(self, Self::Canonical | Self::Both)
}
pub fn can_receive_projections(self) -> bool {
matches!(self, Self::Projection | Self::Both)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BackendCapabilityMatrixEntry {
pub backend: String,
pub tier: String,
pub operations: Vec<String>,
pub unsupported_error_code: String,
pub consistency_model: String,
pub max_payload_bytes: u64,
pub supports_xa: bool,
pub supports_two_phase_commit: bool,
#[serde(default = "default_transport_label")]
pub transport_label: String,
#[serde(default)]
pub live_probe: bool,
#[serde(default)]
pub configured: bool,
#[serde(default = "default_backend_role")]
pub role: BackendRole,
#[serde(default = "default_control_plane_ha_level")]
pub control_plane_ha_level: ControlPlaneHaLevel,
#[serde(default = "default_canonical_candidate_profile")]
pub canonical_candidate: CanonicalCandidateProfile,
#[serde(default)]
pub canonical_goal: String,
#[serde(default, skip_deserializing, skip_serializing_if = "Option::is_none")]
pub canonical_feasibility: Option<CanonicalFeasibilityProfile>,
}
fn default_backend_role() -> BackendRole {
BackendRole::Projection
}
fn default_control_plane_ha_level() -> ControlPlaneHaLevel {
ControlPlaneHaLevel::ProjectionOnly
}
fn default_transport_label() -> String {
"unknown".to_string()
}
const OP_PING: &str = "ping";
const OP_PROBE: &str = "probe";
const OP_QUERY: &str = "query";
const OP_MUTATE: &str = "mutate";
const OP_TRANSACTION: &str = "transaction";
const OP_SEARCH: &str = "search";
const OP_GET_OBJECT: &str = "get_object";
const OP_PUT_OBJECT: &str = "put_object";
const OP_DELETE_OBJECT: &str = "delete_object";
const OP_ENSURE_RESOURCE: &str = "ensure_resource";
const OP_DROP_RESOURCE: &str = "drop_resource";
const OP_LIST_RESOURCES: &str = "list_resources";
pub const UNSUPPORTED_OPERATION_CODE: &str = "UDB_UNSUPPORTED_OPERATION";
impl BackendKind {
pub fn all_known() -> &'static [BackendKind] {
const ALL: &[BackendKind] = &[
BackendKind::Postgres,
BackendKind::Mysql,
BackendKind::Sqlite,
BackendKind::Mssql,
BackendKind::Clickhouse,
BackendKind::Redis,
BackendKind::Memcached,
BackendKind::Qdrant,
BackendKind::Weaviate,
BackendKind::Pinecone,
BackendKind::Minio,
BackendKind::S3,
BackendKind::AzureBlob,
BackendKind::Gcs,
BackendKind::Mongodb,
BackendKind::Elasticsearch,
BackendKind::Neo4j,
BackendKind::Cassandra,
];
ALL
}
pub fn supported_operations(&self) -> Vec<&'static str> {
let cap = self.capabilities_v1_fields();
let mut ops = vec![OP_PING, OP_PROBE];
if cap.supports_resource_lifecycle {
ops.extend([OP_ENSURE_RESOURCE, OP_DROP_RESOURCE, OP_LIST_RESOURCES]);
}
match self {
Self::Postgres
| Self::Mysql
| Self::Sqlite
| Self::Mssql
| Self::Clickhouse
| Self::Redis
| Self::Memcached
| Self::Mongodb
| Self::Elasticsearch
| Self::Neo4j
| Self::Cassandra
| Self::Weaviate
| Self::Pinecone => ops.push(OP_QUERY),
_ => {}
}
match self {
Self::Postgres
| Self::Mysql
| Self::Sqlite
| Self::Mssql
| Self::Clickhouse
| Self::Redis
| Self::Memcached
| Self::Mongodb
| Self::Neo4j
| Self::Qdrant
| Self::Elasticsearch
| Self::Cassandra
| Self::Weaviate
| Self::Pinecone => ops.push(OP_MUTATE),
_ => {}
}
if cap.supports_transactions {
ops.push(OP_TRANSACTION);
}
if cap.supports_vector_search || cap.supports_hybrid_search {
ops.push(OP_SEARCH);
}
if cap.is_object_store {
ops.extend([OP_GET_OBJECT, OP_PUT_OBJECT, OP_DELETE_OBJECT]);
}
ops.sort_unstable();
ops.dedup();
ops
}
pub fn supports_operation(&self, operation: &str) -> bool {
match operation {
OP_PING | OP_PROBE | OP_ENSURE_RESOURCE | OP_DROP_RESOURCE | OP_LIST_RESOURCES
| OP_QUERY | OP_MUTATE | OP_TRANSACTION | OP_SEARCH | OP_GET_OBJECT | OP_PUT_OBJECT
| OP_DELETE_OBJECT => self.supported_operations().contains(&operation),
_ => false,
}
}
pub fn capability_matrix_entry(&self) -> BackendCapabilityMatrixEntry {
let cap = self.capabilities();
BackendCapabilityMatrixEntry {
backend: self.as_str().to_string(),
tier: self.tier().as_str().to_string(),
operations: self
.supported_operations()
.into_iter()
.map(ToString::to_string)
.collect(),
unsupported_error_code: UNSUPPORTED_OPERATION_CODE.to_string(),
consistency_model: cap.consistency_model,
max_payload_bytes: cap.max_payload_bytes,
supports_xa: cap.supports_xa,
supports_two_phase_commit: cap.supports_two_phase_commit,
transport_label: self.transport_label().to_string(),
live_probe: self.has_runtime_probe(),
role: self.role(),
control_plane_ha_level: self.control_plane_ha_level(),
canonical_candidate: self.canonical_candidate_profile(),
canonical_goal: self.canonical_promotion_goal().to_string(),
canonical_feasibility: Some(self.canonical_feasibility_profile()),
configured: false,
}
}
}
pub fn capability_matrix() -> Vec<BackendCapabilityMatrixEntry> {
all_plugins()
.into_iter()
.map(|plugin| plugin.kind().capability_matrix_entry())
.collect()
}
pub fn capability_matrix_configured(
configured_backend_tokens: &std::collections::HashSet<String>,
) -> Vec<BackendCapabilityMatrixEntry> {
capability_matrix()
.into_iter()
.map(|mut entry| {
entry.configured = configured_backend_tokens.contains(&entry.backend);
entry
})
.collect()
}
pub const CORE_BACKENDS: [BackendKind; 4] = [
BackendKind::Postgres,
BackendKind::Redis,
BackendKind::Qdrant,
BackendKind::Minio,
];
pub const CANONICAL_SYSTEM_STORE_BACKENDS: &[BackendKind] = &[
BackendKind::Postgres,
BackendKind::Mysql,
BackendKind::Sqlite,
BackendKind::Mssql,
#[cfg(feature = "redis")]
BackendKind::Redis,
#[cfg(feature = "mongodb-native")]
BackendKind::Mongodb,
#[cfg(feature = "cassandra")]
BackendKind::Cassandra,
#[cfg(feature = "neo4j")]
BackendKind::Neo4j,
#[cfg(feature = "clickhouse")]
BackendKind::Clickhouse,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn core_backends_have_correct_tiers() {
assert_eq!(BackendKind::Postgres.tier(), BackendTier::Sql);
assert_eq!(BackendKind::Redis.tier(), BackendTier::Cache);
assert_eq!(BackendKind::Qdrant.tier(), BackendTier::Vector);
assert_eq!(BackendKind::Minio.tier(), BackendTier::Object);
}
#[test]
fn only_postgres_is_ledger_capable() {
for b in &CORE_BACKENDS {
let cap = b.capabilities();
if *b == BackendKind::Postgres {
assert!(
cap.is_migration_ledger_capable,
"postgres must be ledger capable"
);
} else {
assert!(
!cap.is_migration_ledger_capable,
"{} must NOT be ledger capable",
b.as_str()
);
}
}
}
#[test]
fn dsn_scheme_follows_udb_convention() {
assert_eq!(BackendKind::Postgres.dsn_scheme(), "udb+sql+postgres");
assert_eq!(BackendKind::Redis.dsn_scheme(), "udb+cache+redis");
assert_eq!(BackendKind::Qdrant.dsn_scheme(), "udb+vector+qdrant");
assert_eq!(BackendKind::Minio.dsn_scheme(), "udb+object+minio");
}
#[test]
fn from_store_kind_falls_back_to_tier_default() {
assert_eq!(
BackendKind::from_store_kind("sql", ""),
Some(BackendKind::Postgres)
);
assert_eq!(
BackendKind::from_store_kind("cache", ""),
Some(BackendKind::Redis)
);
assert_eq!(
BackendKind::from_store_kind("vector", ""),
Some(BackendKind::Qdrant)
);
assert_eq!(
BackendKind::from_store_kind("storage", ""),
Some(BackendKind::Minio)
);
}
#[test]
fn from_store_kind_respects_backend_hint() {
assert_eq!(
BackendKind::from_store_kind("vector", "weaviate"),
Some(BackendKind::Weaviate)
);
assert_eq!(
BackendKind::from_store_kind("object", "s3"),
Some(BackendKind::S3)
);
}
#[test]
fn wired_vector_backends_support_vector_search() {
for b in [
BackendKind::Qdrant,
BackendKind::Elasticsearch,
BackendKind::Weaviate,
BackendKind::Pinecone,
] {
assert!(
b.capabilities().supports_vector_search,
"{} must support vector search",
b.as_str()
);
}
}
#[test]
fn metadata_only_backends_advertise_no_capabilities() {
use crate::backend::BackendKind;
for b in [
BackendKind::Postgres,
BackendKind::Mysql,
BackendKind::Sqlite,
BackendKind::Mssql,
BackendKind::Clickhouse,
BackendKind::Redis,
BackendKind::Memcached,
BackendKind::Qdrant,
BackendKind::Weaviate,
BackendKind::Pinecone,
BackendKind::Minio,
BackendKind::S3,
BackendKind::AzureBlob,
BackendKind::Gcs,
BackendKind::Mongodb,
BackendKind::Elasticsearch,
BackendKind::Neo4j,
BackendKind::Cassandra,
] {
let caps = b.capabilities();
assert_ne!(
caps.consistency_model,
"metadata_only",
"{} should NOT be metadata_only — all 18 backends wired in C9",
b.as_str()
);
}
}
#[test]
fn object_backends_are_object_stores() {
for b in [
BackendKind::Minio,
BackendKind::S3,
BackendKind::AzureBlob,
BackendKind::Gcs,
] {
assert!(
b.capabilities().is_object_store,
"{} must be object store",
b.as_str()
);
}
}
#[test]
fn test_capability_rejection() {
let postgres = BackendKind::Postgres;
assert!(postgres.capabilities().supports_sql_ddl);
let redis = BackendKind::Redis;
assert!(!redis.capabilities().supports_sql_ddl);
assert!(!redis.capabilities().supports_vector_search);
let qdrant = BackendKind::Qdrant;
assert!(qdrant.capabilities().supports_vector_search);
assert!(!qdrant.capabilities().supports_sql_ddl);
}
#[test]
fn capability_matrix_lists_generic_dispatch_operations() {
let matrix = capability_matrix();
#[cfg(feature = "redis")]
{
let redis = matrix
.iter()
.find(|entry| entry.backend == "redis")
.expect("redis matrix entry");
assert!(redis.operations.contains(&"query".to_string()));
assert!(redis.operations.contains(&"mutate".to_string()));
assert_eq!(redis.unsupported_error_code, UNSUPPORTED_OPERATION_CODE);
}
#[cfg(feature = "qdrant")]
{
let qdrant = matrix
.iter()
.find(|entry| entry.backend == "qdrant")
.expect("qdrant matrix entry");
assert!(qdrant.operations.contains(&"search".to_string()));
assert!(!qdrant.operations.contains(&"query".to_string()));
}
}
#[test]
fn advertised_generic_dispatch_operations_are_admitted() {
for backend in ALL_KINDS {
for operation in backend.supported_operations() {
assert!(
backend.supports_operation(operation),
"{} advertises '{operation}' but rejects it",
backend.as_str()
);
}
}
}
#[test]
fn generic_dispatch_operation_table_pins_known_drift_cases() {
for backend in [
BackendKind::Mysql,
BackendKind::Sqlite,
BackendKind::Mssql,
BackendKind::Memcached,
BackendKind::Elasticsearch,
BackendKind::Cassandra,
BackendKind::Weaviate,
BackendKind::Pinecone,
] {
assert!(
backend.supported_operations().contains(&OP_QUERY),
"{} has a real generic query executor",
backend.as_str()
);
assert!(
backend.supported_operations().contains(&OP_MUTATE),
"{} has a real generic mutate executor",
backend.as_str()
);
}
for backend in [
BackendKind::Qdrant,
BackendKind::AzureBlob,
BackendKind::Gcs,
] {
assert!(
!backend.supports_operation(OP_QUERY),
"{} query is an unsupported fallback and must not be admitted",
backend.as_str()
);
}
}
const ALL_KINDS: [BackendKind; 18] = BackendKind::ALL;
#[test]
fn orm_tier_projects_each_backend_to_the_right_category() {
let expect = |kind: BackendKind, tier: &str| {
assert_eq!(
kind.orm_tier(),
tier,
"{} should project to ORM tier {tier}",
kind.as_str()
);
};
expect(BackendKind::Postgres, "relational");
expect(BackendKind::Mysql, "relational");
expect(BackendKind::Sqlite, "relational");
expect(BackendKind::Mssql, "relational");
expect(BackendKind::Clickhouse, "relational");
expect(BackendKind::Cassandra, "relational"); expect(BackendKind::Mongodb, "document");
expect(BackendKind::Redis, "kv");
expect(BackendKind::Memcached, "kv");
expect(BackendKind::Qdrant, "vector");
expect(BackendKind::Weaviate, "vector");
expect(BackendKind::Pinecone, "vector");
expect(BackendKind::Elasticsearch, "vector");
expect(BackendKind::Minio, "blob");
expect(BackendKind::S3, "blob");
expect(BackendKind::AzureBlob, "blob");
expect(BackendKind::Gcs, "blob");
expect(BackendKind::Neo4j, "graph");
}
#[test]
fn orm_tier_is_a_total_closed_projection_of_backend_tier() {
const KNOWN: [&str; 6] = ["relational", "document", "kv", "vector", "blob", "graph"];
for kind in ALL_KINDS {
let orm = kind.orm_tier();
assert!(
KNOWN.contains(&orm),
"{} produced unknown ORM tier {orm:?}",
kind.as_str()
);
let from_tier = match kind.tier() {
BackendTier::Sql | BackendTier::Column => "relational",
BackendTier::Document => "document",
BackendTier::Cache => "kv",
BackendTier::Vector => "vector",
BackendTier::Object => "blob",
BackendTier::Graph => "graph",
};
assert_eq!(
orm,
from_tier,
"{} orm_tier diverged from tier()",
kind.as_str()
);
}
}
#[test]
fn document_backend_is_not_relational_so_include_is_unsupported() {
assert_ne!(
BackendKind::Mongodb.orm_tier(),
"relational",
"a document store must not advertise relational eager-load capability"
);
assert_eq!(BackendKind::Mongodb.orm_tier(), "document");
}
fn serde_token(b: &BackendKind) -> String {
serde_json::to_string(b)
.unwrap()
.trim_matches('"')
.to_string()
}
#[test]
fn as_str_tokens_are_pinned() {
let expected: [(BackendKind, &str); 18] = [
(BackendKind::Postgres, "postgres"),
(BackendKind::Mysql, "mysql"),
(BackendKind::Sqlite, "sqlite"),
(BackendKind::Mssql, "sqlserver"),
(BackendKind::Clickhouse, "clickhouse"),
(BackendKind::Redis, "redis"),
(BackendKind::Memcached, "memcached"),
(BackendKind::Qdrant, "qdrant"),
(BackendKind::Weaviate, "weaviate"),
(BackendKind::Pinecone, "pinecone"),
(BackendKind::Minio, "minio"),
(BackendKind::S3, "s3"),
(BackendKind::AzureBlob, "azureblob"),
(BackendKind::Gcs, "gcs"),
(BackendKind::Mongodb, "mongodb"),
(BackendKind::Elasticsearch, "elasticsearch"),
(BackendKind::Neo4j, "neo4j"),
(BackendKind::Cassandra, "cassandra"),
];
for (kind, token) in expected {
assert_eq!(kind.as_str(), token, "as_str token changed for {kind:?}");
}
}
#[test]
fn serde_round_trips_for_every_variant() {
for kind in ALL_KINDS {
let json = serde_json::to_string(&kind).unwrap();
let back: BackendKind = serde_json::from_str(&json).unwrap();
assert_eq!(back, kind, "serde round-trip failed for {kind:?}");
}
}
#[test]
fn mysql_xa_capability_matches_compiled_runtime() {
let cap = BackendKind::Mysql.capabilities();
let matrix = BackendKind::Mysql.capability_matrix_entry();
assert_eq!(
cap.supports_xa,
cfg!(feature = "mysql"),
"MySQL must advertise XA only when the mysql feature compiles the runtime participant"
);
assert_eq!(
cap.supports_two_phase_commit,
cfg!(feature = "mysql"),
"MySQL must advertise 2PC only when the mysql feature compiles the runtime participant"
);
assert_eq!(matrix.supports_xa, cap.supports_xa);
assert_eq!(
matrix.supports_two_phase_commit,
cap.supports_two_phase_commit
);
}
#[test]
fn config_backend_tokens_agree_across_as_str_and_serde() {
for kind in [
BackendKind::Postgres,
BackendKind::Redis,
BackendKind::Qdrant,
BackendKind::Clickhouse,
BackendKind::Mongodb,
BackendKind::Neo4j,
BackendKind::Minio,
BackendKind::S3,
] {
assert_eq!(
kind.as_str(),
serde_token(&kind),
"as_str() and serde token diverged for config backend {kind:?}"
);
}
}
#[test]
fn from_token_is_exact_inverse_of_as_str() {
for kind in ALL_KINDS {
assert_eq!(
BackendKind::from_token(kind.as_str()),
Some(kind.clone()),
"from_token(as_str()) must round-trip for {kind:?}"
);
}
}
#[test]
fn from_token_is_case_insensitive_and_rejects_unknown() {
assert_eq!(
BackendKind::from_token("POSTGRES"),
Some(BackendKind::Postgres)
);
assert_eq!(
BackendKind::from_token(" Qdrant "),
Some(BackendKind::Qdrant)
);
assert_eq!(
BackendKind::from_token("sqlserver"),
Some(BackendKind::Mssql)
);
assert_eq!(BackendKind::from_token("mssql"), None);
assert_eq!(BackendKind::from_token("not_a_backend"), None);
assert_eq!(BackendKind::from_token(""), None);
}
#[test]
fn known_as_str_vs_serde_divergences_are_locked() {
assert_eq!(BackendKind::Mssql.as_str(), "sqlserver");
assert_eq!(serde_token(&BackendKind::Mssql), "mssql");
assert_eq!(BackendKind::AzureBlob.as_str(), "azureblob");
assert_eq!(serde_token(&BackendKind::AzureBlob), "azure_blob");
}
#[test]
fn v1_capabilities_are_a_pure_projection_of_v2() {
for kind in ALL_KINDS {
assert_eq!(
kind.capabilities(),
kind.capabilities_v2().derive_v1(),
"V1 capabilities diverged from V2 projection for {kind:?}"
);
}
}
#[test]
fn v1_resource_lifecycle_equals_dispatch_admission_dimension() {
for kind in ALL_KINDS {
let v2 = kind.capabilities_v2();
assert_eq!(
kind.capabilities().supports_resource_lifecycle,
v2.lifecycle.admits_dispatch(),
"{kind:?}: V1 supports_resource_lifecycle must equal lifecycle.admits_dispatch()"
);
}
}
#[test]
fn advertised_operations_have_compiler_or_executor_evidence() {
for kind in ALL_KINDS {
let v2 = kind.capabilities_v2();
for op in &v2.dispatch_operations {
if *op == OP_PING || *op == OP_PROBE {
continue;
}
assert!(
v2.native_executor || v2.compiler_mediated,
"{kind:?} advertises operation '{op}' but has neither native \
executor nor compiler evidence"
);
}
}
}
#[test]
fn lifecycle_op_admission_requires_nonzero_lifecycle_dimension() {
for kind in ALL_KINDS {
let v2 = kind.capabilities_v2();
let advertises_lifecycle = v2.dispatch_operations.contains(&OP_ENSURE_RESOURCE)
|| v2.dispatch_operations.contains(&OP_DROP_RESOURCE)
|| v2.dispatch_operations.contains(&OP_LIST_RESOURCES);
assert_eq!(
advertises_lifecycle,
v2.lifecycle.admits_dispatch(),
"{kind:?}: lifecycle op admission must match lifecycle.admits_dispatch()"
);
}
}
#[test]
fn mssql_lifecycle_is_compiler_mediated_not_native() {
let v2 = BackendKind::Mssql.capabilities_v2();
assert_eq!(v2.lifecycle, LifecycleSupport::CompilerMediated);
assert!(v2.lifecycle.admits_dispatch());
assert!(
!v2.lifecycle.is_native_executor(),
"MSSQL lifecycle must NOT claim native executor coverage"
);
assert!(
v2.compiler_mediated,
"MSSQL lifecycle is reached through the T-SQL compiler"
);
assert!(
BackendKind::Mssql
.capabilities()
.supports_resource_lifecycle
);
}
#[test]
fn cassandra_lifecycle_is_compiler_mediated_not_native() {
let v2 = BackendKind::Cassandra.capabilities_v2();
assert_eq!(v2.lifecycle, LifecycleSupport::CompilerMediated);
assert!(v2.lifecycle.admits_dispatch());
assert!(
!v2.lifecycle.is_native_executor(),
"Cassandra lifecycle must NOT claim native executor coverage"
);
assert!(v2.compiler_mediated);
assert!(
BackendKind::Cassandra
.capabilities()
.supports_resource_lifecycle
);
}
#[test]
fn native_lifecycle_backends_are_distinct_from_compiler_mediated() {
for kind in [
BackendKind::Minio,
BackendKind::S3,
BackendKind::Qdrant,
BackendKind::Mongodb,
BackendKind::Neo4j,
BackendKind::Clickhouse,
BackendKind::Elasticsearch,
BackendKind::Weaviate,
BackendKind::Pinecone,
] {
let v2 = kind.capabilities_v2();
assert_eq!(
v2.lifecycle,
LifecycleSupport::Native,
"{kind:?} has a native ResourceAdminExecutor lifecycle"
);
assert!(v2.lifecycle.is_native_executor(), "{kind:?}");
}
}
#[test]
fn sql_canonical_backends_use_catalog_migration_lifecycle() {
for kind in [
BackendKind::Postgres,
BackendKind::Mysql,
BackendKind::Sqlite,
] {
let v2 = kind.capabilities_v2();
assert_eq!(
v2.lifecycle,
LifecycleSupport::CatalogMigration,
"{kind:?} relational lifecycle is catalog-migration managed"
);
assert!(!v2.lifecycle.is_native_executor(), "{kind:?}");
assert!(v2.lifecycle.admits_dispatch(), "{kind:?}");
}
}
#[test]
fn no_lifecycle_backends_reject_lifecycle_ops() {
for kind in [BackendKind::Redis, BackendKind::Memcached] {
let v2 = kind.capabilities_v2();
assert_eq!(v2.lifecycle, LifecycleSupport::None, "{kind:?}");
assert!(!v2.lifecycle.admits_dispatch(), "{kind:?}");
assert!(
!kind.capabilities().supports_resource_lifecycle,
"{kind:?} must not advertise V1 lifecycle"
);
}
}
#[test]
fn role_matches_system_store_evidence() {
for kind in ALL_KINDS {
let role_is_canonical = kind.role().can_host_system_tables();
let has_evidence = CANONICAL_SYSTEM_STORE_BACKENDS.contains(&kind);
assert_eq!(
role_is_canonical, has_evidence,
"{kind:?}: role.can_host_system_tables()={role_is_canonical} but \
SystemStores evidence={has_evidence} — role and committed \
canonical_store impls must agree"
);
assert_eq!(
kind.system_store_support().is_canonical(),
has_evidence,
"{kind:?}: system_store_support() must mirror the allowlist"
);
}
}
#[test]
fn target_backends_stay_projection_until_canonical_stores_exist() {
let projection_until_store: &[BackendKind] = &[
#[cfg(not(feature = "mongodb-native"))]
BackendKind::Mongodb,
#[cfg(not(feature = "clickhouse"))]
BackendKind::Clickhouse,
#[cfg(not(feature = "neo4j"))]
BackendKind::Neo4j,
#[cfg(not(feature = "cassandra"))]
BackendKind::Cassandra,
];
for kind in projection_until_store {
assert_eq!(
kind.role(),
BackendRole::Projection,
"{kind:?} must stay Projection until its canonical SystemStores exists"
);
assert!(
!CANONICAL_SYSTEM_STORE_BACKENDS.contains(kind),
"{kind:?} must not be on the SystemStores allowlist yet"
);
}
}
#[test]
fn all_backend_rows_have_canonical_candidate_goals() {
for kind in ALL_KINDS {
let v2 = kind.capabilities_v2();
let matrix = kind.capability_matrix_entry();
assert_eq!(
matrix.canonical_candidate, v2.canonical_candidate,
"{kind:?}: matrix candidate profile must mirror V2"
);
assert_eq!(
matrix.canonical_goal, v2.canonical_goal,
"{kind:?}: matrix canonical goal must mirror V2"
);
assert!(
!v2.canonical_goal.trim().is_empty(),
"{kind:?}: canonical goal must be explicit"
);
}
}
#[test]
fn b14_cache_backends_have_durability_or_rejection_profiles() {
#[cfg(feature = "redis")]
assert_eq!(
BackendKind::Redis.canonical_candidate_profile(),
CanonicalCandidateProfile::Implemented
);
#[cfg(not(feature = "redis"))]
assert_eq!(
BackendKind::Redis.canonical_candidate_profile(),
CanonicalCandidateProfile::CacheDurabilityRequired
);
assert_eq!(
BackendKind::Memcached.canonical_candidate_profile(),
CanonicalCandidateProfile::ExplicitlyNotSupported
);
#[cfg(feature = "redis")]
assert_eq!(BackendKind::Redis.role(), BackendRole::Canonical);
#[cfg(not(feature = "redis"))]
assert_eq!(BackendKind::Redis.role(), BackendRole::Projection);
assert_eq!(BackendKind::Memcached.role(), BackendRole::Projection);
}
#[test]
fn vector_plane_backends_share_native_canonical_blockers() {
for kind in [
BackendKind::Qdrant,
BackendKind::Pinecone,
BackendKind::Weaviate,
BackendKind::Elasticsearch,
] {
let profile = kind.canonical_feasibility_profile();
assert_eq!(profile.family, "vector", "{kind:?}");
assert!(!profile.implemented, "{kind:?} must not fake SystemStores");
assert_eq!(kind.role(), BackendRole::Projection, "{kind:?}");
assert_eq!(
kind.system_store_support(),
SystemStoreSupport::None,
"{kind:?}"
);
assert!(
!profile.blocking_gaps.is_empty(),
"{kind:?} must expose native vector-plane blockers"
);
assert!(
profile
.durability_prerequisites
.contains(&"atomic claim winner for leases and task claims"),
"{kind:?} must share the vector-plane atomic-claim prerequisite"
);
assert!(
profile.live_conformance_env.is_some(),
"{kind:?} must advertise the live conformance env for promotion"
);
assert!(
!CANONICAL_SYSTEM_STORE_BACKENDS.contains(&kind),
"{kind:?} must not enter the canonical allowlist"
);
}
}
#[test]
fn qdrant_stays_projection_until_ha_system_store_conformance_exists() {
let profile = BackendKind::Qdrant.canonical_feasibility_profile();
assert_eq!(
BackendKind::Qdrant.canonical_candidate_profile(),
CanonicalCandidateProfile::VectorNativeCasUnsupported
);
assert_eq!(BackendKind::Qdrant.role(), BackendRole::Projection);
assert_eq!(
BackendKind::Qdrant.system_store_support(),
SystemStoreSupport::None
);
assert_eq!(
BackendKind::Qdrant.control_plane_ha_level(),
ControlPlaneHaLevel::HaCanonical
);
assert!(
profile
.blocking_gaps
.iter()
.any(|gap| gap.contains("distributed leases")),
"Qdrant must explain the HA SystemStores blocker"
);
}
#[test]
fn control_plane_ha_levels_are_explicit() {
assert_eq!(
BackendKind::Postgres.control_plane_ha_level(),
ControlPlaneHaLevel::HaCanonical
);
assert_eq!(
BackendKind::Sqlite.control_plane_ha_level(),
ControlPlaneHaLevel::DevSingleNode
);
assert_eq!(
BackendKind::Clickhouse.control_plane_ha_level(),
ControlPlaneHaLevel::HaCanonical
);
for vector in [
BackendKind::Qdrant,
BackendKind::Weaviate,
BackendKind::Pinecone,
BackendKind::Elasticsearch,
] {
assert_eq!(
vector.control_plane_ha_level(),
ControlPlaneHaLevel::HaCanonical,
"{vector:?} must be HA-canonical tier (maintainer decision)"
);
}
assert_eq!(
BackendKind::S3.control_plane_ha_level(),
ControlPlaneHaLevel::ProjectionOnly
);
let matrix = BackendKind::Qdrant.capability_matrix_entry();
assert_eq!(
matrix.control_plane_ha_level,
ControlPlaneHaLevel::HaCanonical
);
}
#[test]
fn deployment_tier_ordering_and_parsing() {
use ControlPlaneHaLevel as L;
assert!(L::ProjectionOnly < L::DevSingleNode);
assert!(L::DevSingleNode < L::SystemStoreCapable);
assert!(L::SystemStoreCapable < L::HaCanonical);
assert!(L::HaCanonical >= L::SystemStoreCapable);
assert!(L::DevSingleNode < L::HaCanonical);
assert_eq!(
L::parse_deployment_tier("ha_canonical"),
Some(L::HaCanonical)
);
assert_eq!(L::parse_deployment_tier(" HA "), Some(L::HaCanonical));
assert_eq!(
L::parse_deployment_tier("system_store_capable"),
Some(L::SystemStoreCapable)
);
assert_eq!(
L::parse_deployment_tier("dev_single_node"),
Some(L::DevSingleNode)
);
assert_eq!(L::parse_deployment_tier("projection_only"), None);
assert_eq!(L::parse_deployment_tier("bogus"), None);
assert_eq!(L::parse_deployment_tier(""), None);
assert_eq!(L::parse_deployment_tier(" "), None);
}
#[test]
fn noncanonical_candidate_profiles_do_not_claim_system_store_support() {
for kind in ALL_KINDS {
if kind.canonical_candidate_profile() == CanonicalCandidateProfile::Implemented {
assert!(
kind.system_store_support().is_canonical(),
"{kind:?}: implemented canonical profile must have SystemStores evidence"
);
} else {
assert_eq!(
kind.role(),
BackendRole::Projection,
"{kind:?}: non-implemented canonical profile must stay Projection"
);
assert_eq!(
kind.system_store_support(),
SystemStoreSupport::None,
"{kind:?}: non-implemented canonical profile must not claim SystemStores"
);
}
}
}
#[test]
fn clickhouse_keeps_append_analytics_profile() {
let cap = BackendKind::Clickhouse.capabilities();
assert_eq!(cap.consistency_model, "eventual");
assert!(!cap.supports_transactions);
#[cfg(feature = "clickhouse")]
assert_eq!(BackendKind::Clickhouse.role(), BackendRole::Canonical);
#[cfg(not(feature = "clickhouse"))]
assert_eq!(BackendKind::Clickhouse.role(), BackendRole::Projection);
}
#[test]
fn only_real_database_rls_backends_advertise_rls() {
assert!(BackendKind::Postgres.capabilities().supports_rls);
for kind in [BackendKind::Mysql, BackendKind::Sqlite, BackendKind::Mssql] {
assert!(
!kind.capabilities().supports_rls,
"{kind:?} must not advertise RLS when isolation is broker-enforced"
);
}
}
#[test]
fn canonical_allowlist_backends_are_runtime_capable() {
for kind in CANONICAL_SYSTEM_STORE_BACKENDS {
assert!(
crate::backend::has_runtime_implementation(kind),
"{kind:?} is on the canonical allowlist but has no runtime impl"
);
}
}
#[test]
fn every_backend_exposes_a_feasibility_profile() {
for kind in ALL_KINDS {
let p = kind.canonical_feasibility_profile();
assert_eq!(
p.backend,
kind.as_str(),
"{kind:?}: profile.backend must equal as_str()"
);
assert_eq!(
p.candidate,
kind.canonical_candidate_profile(),
"{kind:?}: profile.candidate must mirror canonical_candidate_profile()"
);
assert!(
!p.family.trim().is_empty(),
"{kind:?}: family must be explicit"
);
for (name, s) in [
("atomic_claim_strategy", p.atomic_claim_strategy),
("ordered_progress_strategy", p.ordered_progress_strategy),
("tenant_isolation_strategy", p.tenant_isolation_strategy),
("read_fence_strategy", p.read_fence_strategy),
] {
assert!(
!s.trim().is_empty(),
"{kind:?}: {name} must be a non-empty strategy description"
);
}
assert_eq!(
p.read_fence_supported,
!p.read_fence_strategy.trim_start().starts_with("none"),
"{kind:?}: read_fence_supported must agree with read_fence_strategy"
);
assert!(
!p.supported_consistency_modes.is_empty(),
"{kind:?}: supported_consistency_modes must not be empty"
);
}
let clickhouse = BackendKind::Clickhouse.canonical_feasibility_profile();
assert!(!clickhouse.read_fence_supported);
assert!(
!clickhouse
.supported_consistency_modes
.iter()
.any(|m| matches!(*m, "strong" | "read_your_writes" | "projection_ok")),
"clickhouse must advertise no fence-bearing mode"
);
let postgres = BackendKind::Postgres.canonical_feasibility_profile();
assert!(postgres.read_fence_supported);
assert!(
postgres
.supported_consistency_modes
.contains(&"read_your_writes")
);
}
#[test]
fn feasibility_implemented_flag_matches_system_store_evidence() {
for kind in ALL_KINDS {
let p = kind.canonical_feasibility_profile();
assert_eq!(
p.implemented,
kind.system_store_support().is_canonical(),
"{kind:?}: implemented flag must mirror system_store_support().is_canonical()"
);
assert_eq!(
p.implemented,
p.blocking_gaps.is_empty(),
"{kind:?}: a backend ships a real SystemStores impl IFF it has no blocking gaps"
);
}
}
#[test]
fn feasibility_profile_is_mirrored_in_capability_matrix() {
for entry in capability_matrix() {
let kind = ALL_KINDS
.into_iter()
.find(|k| entry.backend == k.as_str())
.unwrap_or_else(|| panic!("matrix row {} has no matching kind", entry.backend));
assert_eq!(
entry.canonical_feasibility,
Some(kind.canonical_feasibility_profile()),
"{kind:?}: matrix feasibility must mirror canonical_feasibility_profile()"
);
assert!(
entry.canonical_feasibility.is_some(),
"{kind:?}: every matrix row must expose a feasibility profile"
);
}
}
#[test]
fn b13_object_family_feasibility_is_complete() {
for kind in [
BackendKind::S3,
BackendKind::Minio,
BackendKind::AzureBlob,
BackendKind::Gcs,
] {
let p = kind.canonical_feasibility_profile();
assert_eq!(p.family, "object", "{kind:?}: object family");
assert_eq!(
p.candidate,
CanonicalCandidateProfile::ObjectConditionalWrites,
"{kind:?}: object stores use the conditional-writes candidate bucket"
);
assert!(
!p.durability_prerequisites.is_empty(),
"{kind:?}: object operator prerequisites must be explicit"
);
assert!(
!p.blocking_gaps.is_empty(),
"{kind:?}: object stores have remaining blocking gaps"
);
assert!(
p.blocking_gaps.iter().any(|g| g.contains("sequence")
|| g.contains("ordering")
|| g.contains("order")),
"{kind:?}: a blocking gap must call out the ordering/sequence proof"
);
assert!(
p.live_conformance_env.is_some(),
"{kind:?}: a live-conformance gate env must be wired"
);
assert_eq!(
kind.role(),
BackendRole::Projection,
"{kind:?}: object stores stay Projection until proofs land"
);
}
}
#[test]
fn b14_cache_family_feasibility_is_explicit() {
let p = BackendKind::Redis.canonical_feasibility_profile();
assert_eq!(p.family, "cache", "Redis: cache family");
assert!(
p.durability_prerequisites
.iter()
.any(|s| s.contains("AOF") || s.contains("aof")),
"Redis durability prerequisites must mention AOF persistence"
);
assert!(
p.live_conformance_env.is_some(),
"Redis: a live-conformance gate env must be wired"
);
let p = BackendKind::Memcached.canonical_feasibility_profile();
assert_eq!(p.family, "cache", "Memcached: cache family");
assert_eq!(
p.candidate,
CanonicalCandidateProfile::ExplicitlyNotSupported,
"Memcached is explicitly not supported for canonical state"
);
assert!(!p.implemented, "Memcached: no canonical SystemStores impl");
assert!(
!p.blocking_gaps.is_empty(),
"Memcached: must enumerate blocking gaps"
);
assert!(
p.blocking_gaps
.iter()
.any(|g| g.contains("durable") || g.contains("volatile") || g.contains("eviction")),
"Memcached: a blocking gap must call out durability/volatility/eviction"
);
assert!(
p.live_conformance_env.is_none(),
"Memcached: no live-conformance gate (nothing to prove canonical)"
);
}
#[test]
fn memcached_can_never_claim_canonical() {
assert_eq!(BackendKind::Memcached.role(), BackendRole::Projection);
assert!(
!BackendKind::Memcached.role().can_host_system_tables(),
"Memcached role must not be able to host system tables"
);
assert_eq!(
BackendKind::Memcached.system_store_support(),
SystemStoreSupport::None
);
assert_eq!(
BackendKind::Memcached.canonical_candidate_profile(),
CanonicalCandidateProfile::ExplicitlyNotSupported
);
}
#[test]
fn dispatch_matrix_parity_for_every_backend() {
for kind in ALL_KINDS {
let entry = kind.capability_matrix_entry();
let expected: Vec<String> = kind
.supported_operations()
.into_iter()
.map(ToString::to_string)
.collect();
assert_eq!(
entry.operations, expected,
"{kind:?}: matrix operations must match supported_operations() in order"
);
for op in &entry.operations {
assert!(
kind.supports_operation(op),
"{kind:?}: advertised op '{op}' must be accepted by supports_operation()"
);
}
let admits_object_rpc = entry
.operations
.iter()
.any(|o| o == "get_object" || o == "put_object");
assert_eq!(
admits_object_rpc,
kind.capabilities().is_object_store,
"{kind:?}: object RPCs are admitted IFF the backend is an object store"
);
}
}
}