Skip to content

Commit 2dbbfa3

Browse files
committed
Move doc comments to crate/module level per review feedback
Move DB model conventions (field ordering, Diesel derives) from nexus/db-model/src/fm.rs to the nexus-db-model crate-level docs in lib.rs, with a vmm example showing schema-to-model type mapping. Move _on_conn/_in_txn naming conventions from the FM datastore module to nexus/db-queries/src/db/datastore/mod.rs. Fix the framing: connection sharing is primarily about pool efficiency, not consistency. Keep the FM-specific correctness detail (concurrent delete detection, #9594) inline with a pointer to the general docs.
1 parent b6ee77e commit 2dbbfa3

5 files changed

Lines changed: 97 additions & 30 deletions

File tree

nexus/db-model/src/fm.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,8 @@
1111
//!
1212
//! These types are used when inserting and reading sitreps in CRDB; when in
1313
//! use, the sitrep is represented as a [`nexus_types::fm::Sitrep`]. See the
14-
//! documentation in [`nexus_types::fm`] for more information.
15-
//!
16-
//! # Conventions
17-
//!
18-
//! Fields in `Queryable`/`Insertable` structs should be ordered to match the
19-
//! column order in `dbinit.sql`. Diesel maps `Queryable` fields positionally,
20-
//! so matching the schema column order prevents subtle bugs if a query ever
21-
//! omits `Selectable`, and keeps the Rust types easy to cross-reference with
22-
//! the schema definition.
14+
//! documentation in [`nexus_types::fm`] for more information, and the
15+
//! [crate-level documentation](crate) for general conventions.
2316
2417
use crate::SqlU32;
2518
use crate::typed_uuid::DbTypedUuid;

nexus/db-model/src/lib.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,78 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
//! Structures stored to the database.
5+
//! Rust types that represent rows in the Omicron database (CockroachDB).
6+
//!
7+
//! Each struct in this crate maps to a database table. The table's columns and
8+
//! SQL types are declared in [`nexus_db_schema`] via Diesel's
9+
//! [`table!`](diesel::table) macro; the struct here provides the corresponding
10+
//! Rust types. [`nexus_db_queries`] then uses both to build and execute
11+
//! queries in its [`DataStore`](nexus_db_queries::db::DataStore) methods.
12+
//!
13+
//! # How the mapping works
14+
//!
15+
//! Consider the `vmm` table. The schema declares columns and SQL types:
16+
//!
17+
//! ```text
18+
//! // in nexus-db-schema
19+
//! table! {
20+
//! vmm (id) {
21+
//! id -> Uuid,
22+
//! sled_id -> Uuid,
23+
//! state_generation -> Int8,
24+
//! state -> VmmStateEnum,
25+
//! ...
26+
//! }
27+
//! }
28+
//! ```
29+
//!
30+
//! The model struct provides the Rust representation of a row:
31+
//!
32+
//! ```text
33+
//! // in nexus-db-model
34+
//! #[derive(Queryable, Insertable, Selectable)]
35+
//! #[diesel(table_name = vmm)]
36+
//! pub struct Vmm {
37+
//! pub id: Uuid,
38+
//! pub sled_id: DbTypedUuid<SledKind>,
39+
//! #[diesel(column_name = state_generation)]
40+
//! pub generation: Generation,
41+
//! pub state: VmmState,
42+
//! ...
43+
//! }
44+
//! ```
45+
//!
46+
//! A few things to note:
47+
//!
48+
//! - **Type translation.** Each field's Rust type must implement Diesel's
49+
//! [`FromSql`](diesel::deserialize::FromSql) and
50+
//! [`ToSql`](diesel::serialize::ToSql) traits for the corresponding column's
51+
//! SQL type. Diesel provides these impls for common types (`Uuid`,
52+
//! `DateTime<Utc>`, `String`, etc.); this crate defines wrapper types like
53+
//! [`DbTypedUuid`](typed_uuid::DbTypedUuid) for domain-specific conversions
54+
//! (e.g. distinguishing a sled UUID from any other UUID).
55+
//! - **Column renaming.** Field names must match column names by default, but
56+
//! `#[diesel(column_name = ...)]` allows the Rust name to differ (e.g.
57+
//! `generation` for the `state_generation` column).
58+
//! - **Positional mapping.** [`diesel::Queryable`] maps SQL result columns to
59+
//! struct fields by position, not by name. If the field order doesn't match
60+
//! the column order in the query, fields will silently receive wrong values.
61+
//! Deriving [`diesel::Selectable`] mitigates this by generating an explicit
62+
//! column list, so the result columns are always in the order `Queryable`
63+
//! expects. **Always derive both.**
64+
//!
65+
//! # Field ordering convention
66+
//!
67+
//! Fields in `Queryable`/`Insertable` structs should be ordered to match the
68+
//! column order in `dbinit.sql`. This keeps the Rust types easy to
69+
//! cross-reference with the schema definition and prevents subtle bugs if a
70+
//! query ever omits `Selectable`.
71+
//!
72+
//! # Representing enums
73+
//!
74+
//! For types that map to database enum columns, see the [`impl_enum_type!`]
75+
//! and [`impl_enum_wrapper!`] macros defined below. For string-backed enums,
76+
//! see the [`DatabaseString`] trait.
677
778
#[macro_use]
879
extern crate diesel;

nexus/db-queries/src/db/datastore/fm.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,8 @@
66
//! reports (sitreps).
77
//!
88
//! See [RFD 603](https://rfd.shared.oxide.computer/rfd/0603) for details on the
9-
//! fault management sitrep.
10-
//!
11-
//! # Conventions
12-
//!
13-
//! Methods with an `_on_conn` suffix take an explicit database connection
14-
//! parameter. In other parts of the codebase this suffix sometimes implies the
15-
//! method is called within a transaction, but that isn't always the case here.
16-
//! In the FM datastore, `_on_conn` methods exist primarily so that multiple
17-
//! queries can share the same connection for consistency (e.g. loading a sitrep
18-
//! reads child records first and metadata last on the same connection, to
19-
//! detect concurrent deletes without requiring a transaction, see
20-
//! [`DataStore::fm_sitrep_read_on_conn`] and issue #9594).
9+
//! fault management sitrep, and the [datastore module documentation](super) for
10+
//! general conventions.
2111
2212
use super::DataStore;
2313
use crate::authz;

nexus/db-queries/src/db/datastore/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
//! Primary control plane interface for database read and write operations
6+
//!
7+
//! # Method naming conventions
8+
//!
9+
//! Many methods in this module have a public entry point that acquires a
10+
//! database connection from the pool, paired with a private `_on_conn` (or
11+
//! `_on_connection`) helper that takes an explicit connection parameter. The
12+
//! helper exists so that multiple queries can share the same connection without
13+
//! re-acquiring one from the pool for each step of a multi-query operation.
14+
//!
15+
//! A related convention is `_in_txn` (or `_in_transaction`), for helpers that
16+
//! run inside an explicit database transaction. These typically take an
17+
//! [`OptionalError`](crate::transaction_retry::OptionalError) so they can bail
18+
//! out of the entire transaction on application-level errors.
619
720
// TODO-scalability review all queries for use of indexes (may need
821
// "time_deleted IS NOT NULL" conditions) Figure out how to automate this.

nexus/src/app/background/tasks/fm_rendezvous.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ impl FmRendezvous {
8787
// `Conflict` errors from individual inserts, since multiple Nexus
8888
// instances may run this task concurrently.
8989
//
90-
// Currently, these `alert_create` calls have no guard against a stale
91-
// Nexus inserting alerts from an outdated sitrep. This is fine for now
92-
// because alert requests are carried forward into newer sitreps, so a
93-
// stale insert is redundant rather than incorrect. However, if alerts
94-
// are ever hard-deleted (e.g. when a case is closed), a lagging Nexus
95-
// could re-create "zombie" alert records after deletion. At that point,
96-
// the INSERT should be guarded by a CTE that checks the sitrep
97-
// generation matches the current one.
90+
// TODO(#9592) Currently, these `alert_create` calls have no guard
91+
// against a stale Nexus inserting alerts from an outdated sitrep. This
92+
// is fine for now because alert requests are carried forward into newer
93+
// sitreps, so a stale insert is redundant rather than incorrect.
94+
// However, if alerts are ever hard-deleted (e.g. when a case is
95+
// closed), a lagging Nexus could re-create "zombie" alert records after
96+
// deletion. At that point, the INSERT should be guarded by a CTE that
97+
// checks the sitrep generation matches the current one.
9898
for (case_id, req) in sitrep.alerts_requested() {
9999
let &AlertRequest { id, class, requested_sitrep_id, .. } = req;
100100
status.total_alerts_requested += 1;

0 commit comments

Comments
 (0)