Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

135 changes: 67 additions & 68 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,103 +1,102 @@
[package]
name = "pg_doorman"
version = "3.11.0"
edition = "2021"
rust-version = "1.87.0"
license = "MIT"

name = "pg_doorman"
rust-version = "1.87.0"
version = "3.11.1"

[profile.release]
codegen-units = 1
lto = true

[profile.profiling]
inherits = "release"
debug = true
inherits = "release"

[dependencies]
tikv-jemallocator = { version = "0.6.0", features = ["background_threads_runtime_support"] }
tikv-jemalloc-ctl = { version = "0.6.0", features = ["stats"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.20", features = ["json", "env-filter", "std"]}
log = "0.4.27"
clap = { version = "4.5.37", features = ["derive", "env"] }
serde = { version = "1", features = ["derive"] }
serde_derive = "1"
serde_json = "1.0.140"
serde_yaml = "0.9"
ipnet = { version = "2.11.0", features = ["serde"] }
once_cell = "1.21.3"
quanta = "0.12"
arc-swap = "1.7.1"
toml = "0.8"
prometheus = "0.14.0"
tokio = { version = "1.48.4", features = ["rt-multi-thread", "fs", "parking_lot", "sync", "io-util", "net", "macros", "signal", "time"] }
exitcode = "1.1.2"
pin-project = "1"
base64 = "0.22.1"
bytes = "1.10.1"
md-5 = "0.10"
socket2 = { version = "0.6.1", features = ["all"] }
chrono = "0.4"
rand = "0.9.1"
base64 = "0.22.1"
clap = {version = "4.5.37", features = ["derive", "env"]}
exitcode = "1.1.2"
hmac = "0.12"
include_dir = "0.7"
ipnet = {version = "2.11.0", features = ["serde"]}
jwt = {version = "0.16.0", features = ["openssl"]}
libc = "0.2.172"
log = "0.4.27"
lru = "0.16.3"
md-5 = "0.10"
native-tls = {version = "0.2.14"}
nix = {version = "0.30.1", features = ["process", "signal"]}
num_cpus = "1.16.0"
once_cell = "1.21.3"
parking_lot = {version = "0.12.1", features = ["hardware-lock-elision"]}
pin-project = "1"
prometheus = "0.14.0"
quanta = "0.12"
rand = "0.9.1"
scopeguard = "1.2"
serde = {version = "1", features = ["derive"]}
serde-toml-merge = {version = "0.3.8"}
serde_derive = "1"
serde_json = "1.0.140"
serde_yaml = "0.9"
sha-1 = "0.10"
sha2 = "0.10"
socket2 = {version = "0.6.1", features = ["all"]}
stringprep = "0.1"
subtle = "2"
nix = { version = "0.30.1", features = ["process", "signal"] }
sha-1 = "0.10"
lru = "0.16.3"
scopeguard = "1.2"
parking_lot = {version = "0.12.1", features = ["hardware-lock-elision"]}
libc = "0.2.172"
include_dir = "0.7"
num_cpus = "1.16.0"
syslog = "7.0.0"
native-tls = { version = "0.2.14" }
tokio-native-tls = { version = "0.3.1" }
serde-toml-merge = { version = "0.3.8"}
jwt = { version = "0.16.0", features = ["openssl"] }
tikv-jemalloc-ctl = {version = "0.6.0", features = ["stats"]}
tikv-jemallocator = {version = "0.6.0", features = ["background_threads_runtime_support"]}
tokio = {version = "1.48.4", features = ["rt-multi-thread", "fs", "parking_lot", "sync", "io-util", "net", "macros", "signal", "time"]}
tokio-native-tls = {version = "0.3.1"}
toml = "0.8"
tracing = "0.1.37"
tracing-subscriber = {version = "0.3.20", features = ["json", "env-filter", "std"]}
# `jwt` (above) is shared with the web UI SSO flow. Both code paths
# verify RS256 against an RSA public key loaded through openssl, so
# adding a second JWT crate would only duplicate functionality and
# pull extra transitive licenses (ring, ISC) into the vendor set.
thiserror = "1"
openssl = { version = "0.10.78" }
openssl-sys = { version = "0.9.114" }
iota = { version = "0.2.3" }
iota = {version = "0.2.3"}
openssl = {version = "0.10.78"}
openssl-sys = {version = "0.9.114"}
pam-client = {version = "0.5.0", optional = true}
pin-project-lite = "0.2.16"
pam-client = { version = "0.5.0", optional = true }
postgres = "0.19.10"
thiserror = "1"
# `with-serde_json-1` lets `Row::try_get` decode `json`/`jsonb`
# columns directly into `serde_json::Value`, which allows auth_query
# startup_parameters to come from a native JSON column.
tokio-postgres = { version = "0.7", features = ["with-serde_json-1"] }
postgres-native-tls = "0.5.1"
flate2 = "1.0.28"
sd-notify = "0.4"
zerocopy = "0.8.24"
xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
ahash = "0.8"
smallvec = "1.13"
dashmap = "6.1"
reqwest = { version = "0.11", features = ["json"] }
flate2 = "1.0.28"
futures = "0.3"
hdrhistogram = "7.5"
postgres-native-tls = "0.5.1"
reqwest = {version = "0.11", features = ["json"]}
sd-notify = "0.4"
smallvec = "1.13"
tokio-postgres = {version = "0.7", features = ["with-serde_json-1"]}
xxhash-rust = {version = "0.8.15", features = ["xxh3"]}
zerocopy = "0.8.24"

[patch.crates-io]
native-tls = { path = "patches/rust-native-tls" }
tokio-native-tls = { path = "patches/tokio-native-tls" }
openssl-src = { path = "patches/openssl-src" }
native-tls = {path = "patches/rust-native-tls"}
openssl-src = {path = "patches/openssl-src"}
tokio-native-tls = {path = "patches/tokio-native-tls"}

[dev-dependencies]
criterion = "0.5"
cucumber = "0.21"
tempfile = "3"
portpicker = "0.1"
tokio-postgres = "0.7"
criterion = "0.5"
pprof = { version = "0.14", features = ["flamegraph", "criterion"] }
serial_test = "3"
pprof = {version = "0.14", features = ["flamegraph", "criterion"]}
regex = "1"
serial_test = "3"
tempfile = "3"
tokio-postgres = "0.7"

[[bin]]
name = "pg_doorman"
Expand All @@ -112,38 +111,38 @@ name = "metrics_stress"
path = "src/bin/metrics_stress.rs"

[[test]]
name = "bdd"
harness = false
name = "bdd"
path = "tests/bdd/main.rs"

[[test]]
name = "patroni_proxy_bdd"
harness = false
name = "patroni_proxy_bdd"
path = "src/bin/patroni_proxy/tests/bdd/main.rs"

[[bench]]
name = "hash_benchmarks"
harness = false
name = "hash_benchmarks"

[[bench]]
name = "cache_benchmarks"
harness = false
name = "cache_benchmarks"

[[bench]]
name = "coordinator_benchmarks"
harness = false
name = "coordinator_benchmarks"

[[bench]]
name = "log_benchmarks"
harness = false
name = "log_benchmarks"

[[bench]]
name = "memory_benchmarks"
harness = false
name = "memory_benchmarks"

[[bench]]
name = "pool_anticipation_benchmarks"
harness = false
name = "pool_anticipation_benchmarks"

[features]
default = []
Expand Down
35 changes: 35 additions & 0 deletions documentation/en/src/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# Changelog

### 3.11.1

#### Pool-level `sync_server_parameters` override

`sync_server_parameters` can now be set per pool in addition to the global
`[general]` section. When a pool defines `sync_server_parameters = true`,
client-sent session parameters (e.g. `search_path`, `work_mem`,
`statement_timeout`) are applied via `SET` queries on checkout for that pool only. When omitted at pool level, the global setting is used.

Enabling `sync_server_parameters` adds a round-trip to PostgreSQL on every
checkout where client parameters differ from the backend defaults. This
increases total query count and can affect TPS under high concurrency.
Enable it only for pools that really need per-client session parameters.
Note that parameters sent via libpq `options=-c ...` (`lock_timeout`, `statement_timeout`,
`idle_in_transaction_session_timeout`, etc.) are not applied on the
backend when `sync_server_parameters` is disabled.

Changing `sync_server_parameters` via RELOAD takes effect immediately for new checkouts.
Pool connections are recreated with the updated setting. In-flight transactions are not affected -
they continue with the server they were assigned at checkout time.

In-flight backend connections retain the `search_path` that was SET at
checkout, so a prepared statement compiled before RELOAD continues to
target the same schema. New connections that checkout after RELOAD may
have a different `search_path`, which causes PostgreSQL to re-plan the
prepared statement against the new schema resolution.

```toml
[general]
sync_server_parameters = false # global default

[pools.synced_db]
sync_server_parameters = true # override for this pool
```

### 3.11.0

#### Talos can route through client-specific pools
Expand Down
4 changes: 4 additions & 0 deletions pg_doorman.toml
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,10 @@ pool_mode = "transaction"
# Default: true
cleanup_server_connections = true

# Override global sync_server_parameters for this pool.
# Default: None (uses global setting)
# sync_server_parameters = false

# Override global prepared_statements_cache_size for this pool.
# prepared_statements_cache_size = 8192

Expand Down
4 changes: 4 additions & 0 deletions pg_doorman.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,10 @@ pools:
# Default: true
cleanup_server_connections: true

# Override global sync_server_parameters for this pool.
# Default: None (uses global setting)
# sync_server_parameters: false

# Override global prepared_statements_cache_size for this pool.
# prepared_statements_cache_size: 8192

Expand Down
9 changes: 9 additions & 0 deletions src/app/generate/annotated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ pub fn generate_reference_config(format: ConfigFormat, russian: bool) -> String
server_tls_certificate: None,
server_tls_private_key: None,
auth_query: None,
sync_server_parameters: None,
startup_parameters: std::collections::BTreeMap::new(),
users: vec![User {
username: "app_user".to_string(),
Expand Down Expand Up @@ -1385,6 +1386,14 @@ fn write_single_pool(w: &mut ConfigWriter, pool_name: &str, pool: &Pool) {
);
w.blank();

write_field_comment(w, fi, "pool", "sync_server_parameters");
if let Some(val) = pool.sync_server_parameters {
w.kv(fi, "sync_server_parameters", &w.bool_val(val));
} else {
w.commented_kv(fi, "sync_server_parameters", "false");
}
w.blank();

write_field_desc(w, fi, "pool", "prepared_statements_cache_size");
if let Some(val) = pool.prepared_statements_cache_size {
w.kv(fi, "prepared_statements_cache_size", &w.num_val(val));
Expand Down
1 change: 1 addition & 0 deletions src/app/generate/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ fn write_pool_fields(out: &mut String, f: &FieldsData) {
"reserve_pool_size",
"reserve_pool_timeout",
"min_guaranteed_pool_size",
"sync_server_parameters",
"startup_parameters",
];

Expand Down
7 changes: 7 additions & 0 deletions src/app/generate/fields.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,13 @@ fields:
doc: "Close server connections in this pool that have been opened for longer than this value, in milliseconds. Only applied to idle connections. If not specified, the global server_lifetime setting is used."
default: "None (uses global setting)"

sync_server_parameters:
config:
en: "Override global sync_server_parameters for this pool."
ru: "Переопределить глобальный sync_server_parameters для этого пула."
doc: "Override global sync_server_parameters for this pool. When enabled, client-sent session parameters (e.g. search_path, work_mem) are applied via SET queries on checkout. If not specified, the global setting is used."
default: "None (uses global setting)"

cleanup_server_connections:
config:
en: |
Expand Down
1 change: 1 addition & 0 deletions src/app/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ pub fn generate_config_with_client(
server_tls_certificate: None,
server_tls_private_key: None,
auth_query: None,
sync_server_parameters: None,
startup_parameters: std::collections::BTreeMap::new(),
users: users.clone(),
},
Expand Down
1 change: 1 addition & 0 deletions src/app/generate/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub fn generate_config_with_client<
server_tls_certificate: None,
server_tls_private_key: None,
auth_query: None,
sync_server_parameters: None,
patroni_api_urls: None,
fallback_cooldown: None,
patroni_api_timeout: None,
Expand Down
11 changes: 11 additions & 0 deletions src/config/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ pub struct Pool {
#[serde(skip_serializing_if = "Option::is_none")]
pub server_tls_private_key: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
pub sync_server_parameters: Option<bool>,

#[serde(skip_serializing_if = "Option::is_none")]
pub auth_query: Option<AuthQueryConfig>,

Expand Down Expand Up @@ -202,6 +205,13 @@ impl Pool {
s.finish()
}

/// Return the effective `sync_server_parameters` flag by falling back
/// to the global default when the pool-level override is `None`.
pub fn effective_sync_server_parameters(&self, general: &super::General) -> bool {
self.sync_server_parameters
.unwrap_or(general.sync_server_parameters)
}

pub fn default_pool_mode() -> PoolMode {
PoolMode::Transaction
}
Expand Down Expand Up @@ -471,6 +481,7 @@ impl Default for Pool {
server_tls_certificate: None,
server_tls_private_key: None,
auth_query: None,
sync_server_parameters: None,
startup_parameters: std::collections::BTreeMap::new(),
}
}
Expand Down
Loading
Loading