Skip to main content

doido_model/
databases.rs

1//! Multiple databases / read-write splitting (Rails `connects_to database:
2//! { writing:, reading: }`).
3//!
4//! [`Databases`] holds a primary (writing) connection and an optional replica
5//! (reading) connection; [`Databases::connection`] routes by [`Role`], falling
6//! back to the writer when no replica is configured.
7
8use crate::sea_orm::DatabaseConnection;
9
10/// Which connection a query should use.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum Role {
13    Writing,
14    Reading,
15}
16
17/// A primary connection plus an optional read replica.
18pub struct Databases {
19    writing: DatabaseConnection,
20    reading: Option<DatabaseConnection>,
21}
22
23impl Databases {
24    /// A single-database setup (all queries hit `writing`).
25    pub fn new(writing: DatabaseConnection) -> Self {
26        Self {
27            writing,
28            reading: None,
29        }
30    }
31
32    /// Add a read replica; reads then route to it.
33    pub fn with_reading(mut self, reading: DatabaseConnection) -> Self {
34        self.reading = Some(reading);
35        self
36    }
37
38    /// Whether a read replica is configured.
39    pub fn has_replica(&self) -> bool {
40        self.reading.is_some()
41    }
42
43    /// The connection for `role` — reads fall back to the writer without a replica.
44    pub fn connection(&self, role: Role) -> &DatabaseConnection {
45        match role {
46            Role::Writing => &self.writing,
47            Role::Reading => self.reading.as_ref().unwrap_or(&self.writing),
48        }
49    }
50
51    /// The writing connection.
52    pub fn writing(&self) -> &DatabaseConnection {
53        &self.writing
54    }
55
56    /// The reading connection (falls back to the writer).
57    pub fn reading(&self) -> &DatabaseConnection {
58        self.connection(Role::Reading)
59    }
60}