Skip to main content

Architecture

Ryx is built in three layers, each with a clear responsibility.

Layer Diagramโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Python Layer (ryx/) โ”‚
โ”‚ Model ยท QuerySet ยท Q ยท Fields ยท Validators ยท Signals โ”‚
โ”‚ Transactions ยท Relations ยท Migrations ยท CLI โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ PyO3 Boundary (ryx-python crate) โ”‚
โ”‚ QueryBuilder ยท TransactionHandle ยท Type Bridge ยท Async โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Modular Query Engine (ryx-query crate) โ”‚
โ”‚ AST ยท Q-Trees ยท SQL Compiler ยท Lookup Registry โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Backend Logic (ryx-backend crate) โ”‚
โ”‚ Executor ยท RowView ยท Decoding Logic โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Core Types (ryx-core crate) โ”‚
โ”‚ Connection & Transaction Enums ยท Base Traits โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ sqlx 0.8.6 + tokio 1.40 โ”‚
โ”‚ AnyPool ยท Async Drivers ยท Transactions โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ PostgreSQL ยท MySQL ยท SQLite โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Query Execution Flowโ€‹

Python: Post.objects.filter(active=True).order_by("-views").limit(10)
โ”‚
โ–ผ
QuerySet builds QueryNode (immutable builder pattern)
โ”‚
โ–ผ
PyQueryBuilder.fetch_all() โ€” crosses PyO3 boundary (ryx-python)
โ”‚
โ–ผ
compiler::compile(&QueryNode) โ†’ CompiledQuery { sql, values } (ryx-query)
โ”‚
โ–ผ
executor::fetch_all(compiled) โ†’ sqlx::query(sql).bind(values).fetch_all(pool) (ryx-backend)
โ”‚
โ–ผ
decode_rows(AnyRow) โ†’ Vec<RowView> (Zero-allocation) (ryx-backend)
โ”‚
โ–ผ
RowView โ†’ PyDict (ryx-python)
โ”‚
โ–ผ
Model._from_row(row) โ†’ List[Model]

Key Design Decisionsโ€‹

Performance Firstโ€‹

Ryx is designed for extreme throughput. It avoids expensive abstractions in the hot path:

  • Enum Dispatch: Replaces dyn traits to eliminate vtable lookups and enable inlining.
  • Zero-Allocation Rows: Replaces HashMap with RowView to reduce allocator pressure.
  • GIL Minimization: Performs all DB operations and decoding in pure Rust.

For a detailed breakdown, see Performance Optimizations.

Immutable Buildersโ€‹

Both Python QuerySet and Rust QueryNode use immutable builders โ€” every method returns a new instance:

// Rust: self by value, not &mut self
pub fn add_filter(self, field: &str, lookup: &str, value: SqlValue) -> Self { ... }
# Python: returns new QuerySet
def filter(self, **kwargs) -> "QuerySet":
clone = self._clone()
clone.builder.add_filter(...)
return clone

GIL Minimizationโ€‹

The Rust executor holds no Python GIL during SQL execution:

  1. Decode AnyRow โ†’ HashMap<String, JsonValue> (no GIL)
  2. Convert HashMap โ†’ PyDict (brief GIL hold at boundary)
  3. Python wraps PyDict โ†’ Model instances

ContextVar Transaction Propagationโ€‹

Active transactions use contextvars.ContextVar โ€” they propagate through async call stacks automatically:

async with ryx.transaction():
await some_function() # Uses the same transaction
await another_function() # Still in the same transaction

AnyPool Over Typed Poolsโ€‹

sqlx::any::AnyPool provides a single code path for all backends:

  • Pro: One codebase, runtime database selection
  • Con: No compile-time query checking
  • Trade-off: Worth it for a dynamic ORM

Next Stepsโ€‹

โ†’ Rust Core โ€” Deep dive into each Rust module