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}