Conventions, patterns, and rules for writing code in the HyperbyteDB codebase.
- Use
thiserrorfor all error types. The central error type isHyperbytedbErrorinsrc/error.rs. - Return
Result<T, HyperbytedbError>from all fallible functions. - Use
?for propagation. Add context with descriptive error messages. - Never use
.unwrap()in production code. Use.expect()only for programmer errors (invariants that should never fail). - The crate root enforces this with
#![cfg_attr(not(test), warn(clippy::unwrap_used, clippy::expect_used))].
// Good
let db = metadata.get_database(name)
.ok_or_else(|| HyperbytedbError::DatabaseNotFound(name.to_string()))?;
// Bad
let db = metadata.get_database(name).unwrap();- Use
tokio::task::spawn_blockingfor CPU-intensive work (chDB queries) and other blocking I/O. - Use
tokio::spawnfor concurrent I/O tasks. - Never hold a
MutexorRwLockacross an.awaitpoint. - Use
tokio::sync::watchfor shutdown signaling. - Use
tokio::select!for timer + shutdown patterns.
pub async fn run(&self, interval: Duration, mut shutdown_rx: watch::Receiver<bool>) {
let mut ticker = tokio::time::interval(interval);
ticker.set_missed_tick_behavior(MissedTickBehavior::Skip);
loop {
tokio::select! {
_ = ticker.tick() => {
if let Err(e) = self.do_work().await {
tracing::error!("service error: {}", e);
}
}
_ = shutdown_rx.changed() => {
if *shutdown_rx.borrow() {
tracing::info!("service shutting down");
break;
}
}
}
}
}| Category | Convention | Examples |
|---|---|---|
| Types | UpperCamelCase |
CompactionService, FlushResult |
| Functions | snake_case |
partition_by_hour, build_schema |
| Constants | SCREAMING_SNAKE_CASE |
WAL_READ_CHUNK, MIN_BATCH_POINTS |
| Getters | No get_ prefix |
fn name(&self) -> &str |
| Builders | new() or build() |
ChdbPool::new(config) |
- Use
Arc<dyn Trait>for dependency injection. All services accept ports asArc<dyn SomePort>. - Use
async_traitfor async trait methods. - Keep domain types free of I/O dependencies.
- Wire dependencies in
src/bootstrap.rs(the composition root).
pub struct IngestionServiceImpl {
metadata: Arc<dyn MetadataPort>,
wal: Arc<dyn WalPort>,
}- Use the
metricscrate (counter!,gauge!,histogram!). - Prefix all metrics with
hyperbytedb_. - Use labels sparingly to avoid high-cardinality explosion.
- Emit metrics at the point of observation, not at call sites.
counter!("hyperbytedb_write_requests_total").increment(1);
histogram!("hyperbytedb_query_duration_seconds").record(elapsed.as_secs_f64());
gauge!("hyperbytedb_flush_points_total")
.set(count as f64);Use tracing with structured fields:
tracing::info!(points = count, db = %db, "flush complete");| Level | Usage |
|---|---|
ERROR |
Failures that need operator attention |
WARN |
Recoverable issues (replication failures, timeout retries) |
INFO |
Lifecycle events (startup, shutdown, flush/compaction summaries) |
DEBUG |
Per-request/per-operation details |
TRACE |
Internal algorithm details |
Port traits (src/ports/) define the boundaries between application logic and infrastructure:
- Traits must be
Send + Sync(for use in async contexts). - Use
#[async_trait]for async methods. - Prefer
&strand&[T]at trait boundaries over owned types. - Return
Result<T, HyperbytedbError>for all fallible operations.
domain/— Pure types with no I/O. Includesdomain/cluster/DTOs andchdb_naming. Never depends on adapters or application.ports/— Traits only. No implementations, no business logic.application/— Business logic. Depends on ports and domain. Never on adapters. Cluster orchestration lives inapplication/cluster/.adapters/— Concrete implementations of ports. Depends on domain and ports. Cluster I/O lives inadapters/cluster/.timeseriesql/— Self-contained Influx-compatible query language (parser, AST, ClickHouse translator).
Key architectural invariants enforced during review:
- Port boundary integrity — Application code never imports from
adapters::*. - Domain purity —
domain/types have no I/O dependencies. - Config documentation — New config keys must appear in
docs/user-guide/configuration.mdandconfig.toml.example. - WAL ordering — Flush must not drop acknowledged writes.
- Identifier quoting — TimeseriesQL-to-ClickHouse translation must quote/escape user input.
See Contributing for the full review rubric.
- Architecture — System design and patterns
- Contributing — Review process and rubric