palpo_data/
config.rs

1//! Configuration for setting up database pools
2//!
3//! - `DATABASE_URL`: The URL of the postgres database to use.
4//! - `READ_ONLY_REPLICA_URL`: The URL of an optional postgres read-only replica database.
5//! - `DB_DIESEL_POOL_SIZE`: The number of connections of the primary database.
6//! - `DB_REPLICA_POOL_SIZE`: The number of connections of the read-only / replica database.
7//! - `DB_PRIMARY_MIN_IDLE`: The primary pool will maintain at least this number of connections.
8//! - `DB_REPLICA_MIN_IDLE`: The replica pool will maintain at least this number of connections.
9//! - `DB_OFFLINE`: If set to `leader` then use the read-only follower as if it was the leader.
10//!   If set to `follower` then act as if `READ_ONLY_REPLICA_URL` was unset.
11//! - `READ_ONLY_MODE`: If defined (even as empty) then force all connections to be read-only.
12//! - `DB_TCP_TIMEOUT_MS`: TCP timeout in milliseconds. See the doc comment for more details.
13
14use std::fmt;
15
16use serde::{Deserialize, Serialize};
17use diesel::prelude::*;
18use diesel::r2d2::{self, CustomizeConnection, State};
19
20use crate::core::serde::default_false;
21
22fn default_db_pool_size() -> u32 {
23    10
24}
25fn default_tcp_timeout() -> u64 {
26    10000
27}
28fn default_connection_timeout() -> u64 {
29    30000
30}
31fn default_statement_timeout() -> u64 {
32    30000
33}
34fn default_helper_threads() -> usize {
35    10
36}
37
38#[derive(Deserialize, Serialize, Clone, Debug)]
39pub struct DbConfig {
40    /// Settings for the primary database. This is usually writeable, but will be read-only in
41    /// some configurations.
42    /// An optional follower database. Always read-only.
43    pub url: String,
44    #[serde(default = "default_db_pool_size")]
45    pub pool_size: u32,
46    pub min_idle: Option<u32>,
47
48    /// Number of seconds to wait for unacknowledged TCP packets before treating the connection as
49    /// broken. This value will determine how long crates.io stays unavailable in case of full
50    /// packet loss between the application and the database: setting it too high will result in an
51    /// unnecessarily long outage (before the unhealthy database logic kicks in), while setting it
52    /// too low might result in healthy connections being dropped.
53    #[serde(default = "default_tcp_timeout")]
54    pub tcp_timeout: u64,
55    /// Time to wait for a connection to become available from the connection
56    /// pool before returning an error.
57    /// Time to wait for a connection to become available from the connection
58    /// pool before returning an error.
59    #[serde(default = "default_connection_timeout")]
60    pub connection_timeout: u64,
61    /// Time to wait for a query response before canceling the query and
62    /// returning an error.
63    #[serde(default = "default_statement_timeout")]
64    pub statement_timeout: u64,
65    /// Number of threads to use for asynchronous operations such as connection
66    /// creation.
67    #[serde(default = "default_helper_threads")]
68    pub helper_threads: usize,
69    /// Whether to enforce that all the database connections are encrypted with TLS.
70    #[serde(default = "default_false")]
71    pub enforce_tls: bool,
72}
73
74impl fmt::Display for DbConfig {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        // Prepare a list of config values to show
77        let lines = [
78            ("tcp_timeout", self.tcp_timeout),
79            // ("connection_timeout", &self.connection_timeout),
80            // ("helper_threads", &self.helper_threads),
81            // ("enforce_tls", self.enforce_tls.to_string()),
82        ];
83
84        let mut msg: String = "Active config values:\n\n".to_owned();
85
86        for line in lines.into_iter().enumerate() {
87            msg += &format!("{}: {}\n", line.1 .0, line.1 .1);
88        }
89
90        write!(f, "{msg}")
91    }
92}
93
94// impl DbConfig {
95//     const DEFAULT_POOL_SIZE: u32 = 1;
96
97//     pub fn are_all_read_only(&self) -> bool {
98//         self.primary.read_only_mode
99//     }
100// }
101
102
103#[derive(Debug, Clone, Copy)]
104pub struct ConnectionConfig {
105    pub statement_timeout: u64,
106    // pub read_only: bool,
107}
108
109impl CustomizeConnection<PgConnection, r2d2::Error> for ConnectionConfig {
110    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
111        use diesel::sql_query;
112
113        sql_query(format!("SET statement_timeout = {}", self.statement_timeout))
114            .execute(conn)
115            .map_err(r2d2::Error::QueryError)?;
116        // if self.read_only {
117        //     sql_query("SET default_transaction_read_only = 't'")
118        //         .execute(conn)
119        //         .map_err(r2d2::Error::QueryError)?;
120        // }
121        Ok(())
122    }
123}