Skip to content

Commit 54220bd

Browse files
committed
extract database pool into new database subcrate
1 parent 7bd70f7 commit 54220bd

File tree

19 files changed

+161
-94
lines changed

19 files changed

+161
-94
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ axum-extra = { version = "0.12.0", features = ["typed-header", "routing", "middl
2727
bincode = "2.0.1"
2828
chrono = { version = "0.4.11", default-features = false, features = ["clock", "serde"] }
2929
derive_more = { version = "2.0.0", features = ["display", "deref", "from", "into", "from_str"] }
30+
futures-util = "0.3.5"
3031
http = "1.0.0"
3132
itertools = { version = "0.14.0" }
3233
mockito = "1.0.2"
@@ -41,12 +42,14 @@ serde = { version = "1.0", features = ["derive"] }
4142
serde_json = "1.0"
4243
sqlx = { version = "0.8", features = [ "runtime-tokio", "postgres", "sqlite", "chrono" ] }
4344
test-case = "3.0.0"
45+
thiserror = "2.0.3"
4446
tokio = { version = "1.0", features = ["rt-multi-thread", "signal", "macros", "process", "sync"] }
4547
tracing = "0.1.37"
4648
url = { version = "2.1.1", features = ["serde"] }
4749

4850
[dependencies]
4951
docs_rs_cargo_metadata = { path = "crates/lib/docs_rs_cargo_metadata" }
52+
docs_rs_database = { path = "crates/lib/docs_rs_database" }
5053
docs_rs_env_vars = { path = "crates/lib/docs_rs_env_vars" }
5154
docs_rs_fastly = { path = "crates/lib/docs_rs_fastly" }
5255
docs_rs_headers = { path = "crates/lib/docs_rs_headers" }
@@ -73,7 +76,7 @@ sqlx = { workspace = true }
7376
url = { workspace = true }
7477
docsrs-metadata = { path = "crates/lib/metadata" }
7578
anyhow = { workspace = true }
76-
thiserror = "2.0.3"
79+
thiserror = { workspace = true }
7780
comrak = { version = "0.49.0", default-features = false }
7881
syntect = { version = "5.0.0", default-features = false, features = ["parsing", "html", "dump-load", "regex-onig"] }
7982
toml = "0.9.2"
@@ -104,7 +107,7 @@ async-compression = { version = "0.4.32", features = ["tokio", "bzip2", "zstd",
104107
tokio = { workspace = true }
105108
tokio-util = { version = "0.7.15", default-features = false, features = ["io"] }
106109
tracing-futures= { version = "0.2.5", features = ["std-future", "futures-03"] }
107-
futures-util = "0.3.5"
110+
futures-util = { workspace = true }
108111
async-stream = "0.3.5"
109112
aws-config = { version = "1.0.0", default-features = false, features = ["rt-tokio", "default-https-client"] }
110113
aws-sdk-s3 = "1.3.0"
@@ -140,6 +143,7 @@ constant_time_eq = "0.4.2"
140143

141144
[dev-dependencies]
142145
docs_rs_cargo_metadata = { path = "crates/lib/docs_rs_cargo_metadata", features = ["testing"] }
146+
docs_rs_database = { path = "crates/lib/docs_rs_database", features = ["testing"] }
143147
docs_rs_fastly = { path = "crates/lib/docs_rs_fastly", features = ["testing"] }
144148
docs_rs_headers = { path = "crates/lib/docs_rs_headers", features = ["testing"] }
145149
docs_rs_opentelemetry = { path = "crates/lib/docs_rs_opentelemetry", features = ["testing"] }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "docs_rs_database"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
anyhow = { workspace = true }
8+
docs_rs_env_vars = { path = "../docs_rs_env_vars" }
9+
docs_rs_opentelemetry = { path = "../docs_rs_opentelemetry" }
10+
futures-util = { workspace = true }
11+
opentelemetry = { workspace = true }
12+
sqlx = { workspace = true }
13+
thiserror = { workspace = true }
14+
tokio = { workspace = true }
15+
tracing = { workspace = true }
16+
17+
[features]
18+
testing = []
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use docs_rs_env_vars::{env, require_env};
2+
3+
#[derive(Debug)]
4+
pub struct Config {
5+
pub database_url: String,
6+
pub max_pool_size: u32,
7+
pub min_pool_idle: u32,
8+
}
9+
10+
impl Config {
11+
pub fn from_environment() -> anyhow::Result<Self> {
12+
Ok(Self {
13+
database_url: require_env("DOCSRS_DATABASE_URL")?,
14+
max_pool_size: env("DOCSRS_MAX_POOL_SIZE", 90u32)?,
15+
min_pool_idle: env("DOCSRS_MIN_POOL_IDLE", 10u32)?,
16+
})
17+
}
18+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#[derive(Debug, thiserror::Error)]
2+
pub enum PoolError {
3+
#[error("failed to create the database connection pool")]
4+
AsyncPoolCreationFailed(#[source] sqlx::Error),
5+
6+
#[error("failed to get a database connection")]
7+
AsyncClientError(#[source] sqlx::Error),
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
mod config;
2+
mod errors;
3+
mod metrics;
4+
mod pool;
5+
6+
pub use config::Config;
7+
pub use errors::PoolError;
8+
pub use pool::{AsyncPoolClient, Pool};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use docs_rs_opentelemetry::AnyMeterProvider;
2+
use opentelemetry::metrics::{Counter, ObservableGauge};
3+
4+
#[derive(Debug)]
5+
pub(crate) struct PoolMetrics {
6+
pub(crate) failed_connections: Counter<u64>,
7+
_idle_connections: ObservableGauge<u64>,
8+
_used_connections: ObservableGauge<u64>,
9+
_max_connections: ObservableGauge<u64>,
10+
}
11+
12+
impl PoolMetrics {
13+
pub(crate) fn new(pool: sqlx::PgPool, meter_provider: &AnyMeterProvider) -> Self {
14+
let meter = meter_provider.meter("pool");
15+
const PREFIX: &str = "docsrs.db.pool";
16+
Self {
17+
failed_connections: meter
18+
.u64_counter(format!("{PREFIX}.failed_connections"))
19+
.with_unit("1")
20+
.build(),
21+
_idle_connections: meter
22+
.u64_observable_gauge(format!("{PREFIX}.idle_connections"))
23+
.with_unit("1")
24+
.with_callback({
25+
let pool = pool.clone();
26+
move |observer| {
27+
observer.observe(pool.num_idle() as u64, &[]);
28+
}
29+
})
30+
.build(),
31+
_used_connections: meter
32+
.u64_observable_gauge(format!("{PREFIX}.used_connections"))
33+
.with_unit("1")
34+
.with_callback({
35+
let pool = pool.clone();
36+
move |observer| {
37+
let used = pool.size() as u64 - pool.num_idle() as u64;
38+
observer.observe(used, &[]);
39+
}
40+
})
41+
.build(),
42+
_max_connections: meter
43+
.u64_observable_gauge(format!("{PREFIX}.max_connections"))
44+
.with_unit("1")
45+
.with_callback({
46+
let pool = pool.clone();
47+
move |observer| {
48+
observer.observe(pool.size() as u64, &[]);
49+
}
50+
})
51+
.build(),
52+
}
53+
}
54+
}
Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use crate::Config;
1+
use crate::{Config, errors::PoolError, metrics::PoolMetrics};
22
use docs_rs_opentelemetry::AnyMeterProvider;
33
use futures_util::{future::BoxFuture, stream::BoxStream};
4-
use opentelemetry::metrics::{Counter, ObservableGauge};
54
use sqlx::{Executor, postgres::PgPoolOptions};
65
use std::{
76
ops::{Deref, DerefMut},
@@ -13,58 +12,6 @@ use tracing::debug;
1312

1413
const DEFAULT_SCHEMA: &str = "public";
1514

16-
#[derive(Debug)]
17-
struct PoolMetrics {
18-
failed_connections: Counter<u64>,
19-
_idle_connections: ObservableGauge<u64>,
20-
_used_connections: ObservableGauge<u64>,
21-
_max_connections: ObservableGauge<u64>,
22-
}
23-
24-
impl PoolMetrics {
25-
fn new(pool: sqlx::PgPool, meter_provider: &AnyMeterProvider) -> Self {
26-
let meter = meter_provider.meter("pool");
27-
const PREFIX: &str = "docsrs.db.pool";
28-
Self {
29-
failed_connections: meter
30-
.u64_counter(format!("{PREFIX}.failed_connections"))
31-
.with_unit("1")
32-
.build(),
33-
_idle_connections: meter
34-
.u64_observable_gauge(format!("{PREFIX}.idle_connections"))
35-
.with_unit("1")
36-
.with_callback({
37-
let pool = pool.clone();
38-
move |observer| {
39-
observer.observe(pool.num_idle() as u64, &[]);
40-
}
41-
})
42-
.build(),
43-
_used_connections: meter
44-
.u64_observable_gauge(format!("{PREFIX}.used_connections"))
45-
.with_unit("1")
46-
.with_callback({
47-
let pool = pool.clone();
48-
move |observer| {
49-
let used = pool.size() as u64 - pool.num_idle() as u64;
50-
observer.observe(used, &[]);
51-
}
52-
})
53-
.build(),
54-
_max_connections: meter
55-
.u64_observable_gauge(format!("{PREFIX}.max_connections"))
56-
.with_unit("1")
57-
.with_callback({
58-
let pool = pool.clone();
59-
move |observer| {
60-
observer.observe(pool.size() as u64, &[]);
61-
}
62-
})
63-
.build(),
64-
}
65-
}
66-
}
67-
6815
#[derive(Debug, Clone)]
6916
pub struct Pool {
7017
async_pool: sqlx::PgPool,
@@ -83,8 +30,8 @@ impl Pool {
8330
Self::new_inner(config, DEFAULT_SCHEMA, otel_meter_provider).await
8431
}
8532

86-
#[cfg(test)]
87-
pub(crate) async fn new_with_schema(
33+
#[cfg(feature = "testing")]
34+
pub async fn new_with_schema(
8835
config: &Config,
8936
schema: &str,
9037
otel_meter_provider: &AnyMeterProvider,
@@ -233,12 +180,3 @@ impl Drop for AsyncPoolClient {
233180
drop(self.inner.take())
234181
}
235182
}
236-
237-
#[derive(Debug, thiserror::Error)]
238-
pub enum PoolError {
239-
#[error("failed to create the database connection pool")]
240-
AsyncPoolCreationFailed(#[source] sqlx::Error),
241-
242-
#[error("failed to get a database connection")]
243-
AsyncClientError(#[source] sqlx::Error),
244-
}

src/build_queue.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
BuildPackageSummary, Config, Context, Index, RustwideBuilder,
3-
db::{AsyncPoolClient, Pool, delete_crate, delete_version, update_latest_version_id},
3+
db::{delete_crate, delete_version, update_latest_version_id},
44
docbuilder::{BuilderMetrics, PackageKind},
55
error::Result,
66
storage::AsyncStorage,
@@ -9,6 +9,7 @@ use crate::{
99
use anyhow::Context as _;
1010
use chrono::NaiveDate;
1111
use crates_index_diff::{Change, CrateVersion};
12+
use docs_rs_database::{AsyncPoolClient, Pool};
1213
use docs_rs_fastly::{Cdn, CdnBehaviour as _};
1314
use docs_rs_opentelemetry::AnyMeterProvider;
1415
use docs_rs_types::{CrateId, KrateName, Version};

src/config.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ pub struct Config {
1717
/// How long to wait between registry checks
1818
pub(crate) delay_between_registry_fetches: Duration,
1919

20-
// Database connection params
21-
pub(crate) database_url: String,
22-
pub(crate) max_pool_size: u32,
23-
pub(crate) min_pool_idle: u32,
24-
2520
// Storage params
2621
pub(crate) storage_backend: StorageKind,
2722

@@ -131,6 +126,7 @@ pub struct Config {
131126
pub(crate) fastly: docs_rs_fastly::Config,
132127
pub(crate) opentelemetry: docs_rs_opentelemetry::Config,
133128
pub(crate) registry_api: docs_rs_registry_api::Config,
129+
pub(crate) database: docs_rs_database::Config,
134130
}
135131

136132
impl Config {
@@ -170,9 +166,6 @@ impl Config {
170166
.registry_index_path(env("REGISTRY_INDEX_PATH", prefix.join("crates.io-index"))?)
171167
.registry_url(maybe_env("REGISTRY_URL")?)
172168
.prefix(prefix.clone())
173-
.database_url(require_env("DOCSRS_DATABASE_URL")?)
174-
.max_pool_size(env("DOCSRS_MAX_POOL_SIZE", 90u32)?)
175-
.min_pool_idle(env("DOCSRS_MIN_POOL_IDLE", 10u32)?)
176169
.storage_backend(env("DOCSRS_STORAGE_BACKEND", StorageKind::Database)?)
177170
.aws_sdk_max_retries(env("DOCSRS_AWS_SDK_MAX_RETRIES", 6u32)?)
178171
.s3_bucket(env("DOCSRS_S3_BUCKET", "rust-docs-rs".to_string())?)
@@ -229,7 +222,8 @@ impl Config {
229222
.context("error reading fastly config from environment")?,
230223
)
231224
.opentelemetry(docs_rs_opentelemetry::Config::from_environment()?)
232-
.registry_api(docs_rs_registry_api::Config::from_environment()?))
225+
.registry_api(docs_rs_registry_api::Config::from_environment()?)
226+
.database(docs_rs_database::Config::from_environment()?))
233227
}
234228

235229
pub fn max_file_size_for(&self, path: impl AsRef<Path>) -> usize {

0 commit comments

Comments
 (0)