Skip to content
Open
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
16 changes: 16 additions & 0 deletions .schema/pgdog.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
"prepared_statements_limit": 9223372036854775807,
"pub_sub_channel_size": 0,
"query_cache_limit": 1000,
"query_cache_memory_limit": 0,
"query_cache_ttl": 0,
"query_log": null,
"query_log_stdout": false,
"query_parser": "auto",
Expand Down Expand Up @@ -978,6 +980,20 @@
"default": 1000,
"minimum": 0
},
"query_cache_memory_limit": {
"description": "Approximate memory budget (bytes) for the query (AST) cache; entries are evicted (LRU) once the sum of their sizes exceeds it. Bounds RAM regardless of query complexity, unlike the count-based `query_cache_limit`. `0` disables the memory cap.\n\n**Note:** Entry sizes are measured with jemalloc; on builds without it the budget is approximated from query text length.\n\n_Default:_ `0`\n\n<https://docs.pgdog.dev/configuration/pgdog.toml/general/#query_cache_memory_limit>",
"type": "integer",
"format": "uint",
"default": 0,
"minimum": 0
},
"query_cache_ttl": {
"description": "Time-to-idle (seconds) for query (AST) cache entries; an entry not accessed within this window is dropped by the maintenance sweep. `0` disables idle expiry.\n\n_Default:_ `0`\n\n<https://docs.pgdog.dev/configuration/pgdog.toml/general/#query_cache_ttl>",
"type": "integer",
"format": "uint",
"default": 0,
"minimum": 0
},
"query_log": {
"description": "Path to a file where all queries are logged. Logging every query is slow; do not use in production.",
"type": [
Expand Down
18 changes: 18 additions & 0 deletions Cargo.lock

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

36 changes: 36 additions & 0 deletions pgdog-config/src/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,24 @@ pub struct General {
#[serde(default = "General::query_cache_limit")]
pub query_cache_limit: usize,

/// Approximate memory budget (bytes) for the query (AST) cache; entries are evicted (LRU) once the sum of their sizes exceeds it. Bounds RAM regardless of query complexity, unlike the count-based `query_cache_limit`. `0` disables the memory cap.
///
/// **Note:** Entry sizes are measured with jemalloc; on builds without it the budget is approximated from query text length.
///
/// _Default:_ `0`
///
/// <https://docs.pgdog.dev/configuration/pgdog.toml/general/#query_cache_memory_limit>
#[serde(default = "General::query_cache_memory_limit")]
pub query_cache_memory_limit: usize,

/// Time-to-idle (seconds) for query (AST) cache entries; an entry not accessed within this window is dropped by the maintenance sweep. `0` disables idle expiry.
///
/// _Default:_ `0`
///
/// <https://docs.pgdog.dev/configuration/pgdog.toml/general/#query_cache_ttl>
#[serde(default = "General::query_cache_ttl")]
pub query_cache_ttl: usize,

/// Toggle automatic creation of connection pools given the user name, database and password.
///
/// _Default:_ `disabled`
Expand Down Expand Up @@ -854,6 +872,8 @@ impl Default for General {
query_parser_engine: QueryParserEngine::default(),
prepared_statements_limit: Self::prepared_statements_limit(),
query_cache_limit: Self::query_cache_limit(),
query_cache_memory_limit: Self::query_cache_memory_limit(),
query_cache_ttl: Self::query_cache_ttl(),
passthrough_auth: Self::default_passthrough_auth(),
connect_timeout: Self::default_connect_timeout(),
connect_attempt_delay: Self::default_connect_attempt_delay(),
Expand Down Expand Up @@ -1325,6 +1345,14 @@ impl General {
Self::env_or_default("PGDOG_QUERY_CACHE_LIMIT", 1_000)
}

pub fn query_cache_memory_limit() -> usize {
Self::env_or_default("PGDOG_QUERY_CACHE_MEMORY_LIMIT", 0)
}

pub fn query_cache_ttl() -> usize {
Self::env_or_default("PGDOG_QUERY_CACHE_TTL", 0)
}

pub fn log_format() -> LogFormat {
Self::env_enum_or_default("PGDOG_LOG_FORMAT")
}
Expand Down Expand Up @@ -1743,6 +1771,8 @@ mod tests {
let _guard = set_env_var("PGDOG_OPENMETRICS_PORT", "9090");
let _guard = set_env_var("PGDOG_PREPARED_STATEMENTS_LIMIT", "1000");
let _guard = set_env_var("PGDOG_QUERY_CACHE_LIMIT", "500");
let _guard = set_env_var("PGDOG_QUERY_CACHE_MEMORY_LIMIT", "1048576");
let _guard = set_env_var("PGDOG_QUERY_CACHE_TTL", "600");
let _guard = set_env_var("PGDOG_CONNECT_ATTEMPTS", "3");
let _guard = set_env_var("PGDOG_MIRROR_QUEUE", "256");
let _guard = set_env_var("PGDOG_MIRROR_EXPOSURE", "0.5");
Expand All @@ -1755,6 +1785,8 @@ mod tests {
assert_eq!(General::openmetrics_port(), Some(9090));
assert_eq!(General::prepared_statements_limit(), 1000);
assert_eq!(General::query_cache_limit(), 500);
assert_eq!(General::query_cache_memory_limit(), 1048576);
assert_eq!(General::query_cache_ttl(), 600);
assert_eq!(General::connect_attempts(), 3);
assert_eq!(General::mirror_queue(), 256);
assert_eq!(General::mirror_exposure(), 0.5);
Expand All @@ -1767,6 +1799,8 @@ mod tests {
let _guard = remove_env_var("PGDOG_OPENMETRICS_PORT");
let _guard = remove_env_var("PGDOG_PREPARED_STATEMENTS_LIMIT");
let _guard = remove_env_var("PGDOG_QUERY_CACHE_LIMIT");
let _guard = remove_env_var("PGDOG_QUERY_CACHE_MEMORY_LIMIT");
let _guard = remove_env_var("PGDOG_QUERY_CACHE_TTL");
let _guard = remove_env_var("PGDOG_CONNECT_ATTEMPTS");
let _guard = remove_env_var("PGDOG_MIRROR_QUEUE");
let _guard = remove_env_var("PGDOG_MIRROR_EXPOSURE");
Expand All @@ -1779,6 +1813,8 @@ mod tests {
assert_eq!(General::openmetrics_port(), None);
assert_eq!(General::prepared_statements_limit(), i64::MAX as usize);
assert_eq!(General::query_cache_limit(), 1_000);
assert_eq!(General::query_cache_memory_limit(), 0);
assert_eq!(General::query_cache_ttl(), 0);
assert_eq!(General::connect_attempts(), 1);
assert_eq!(General::mirror_queue(), 128);
assert_eq!(General::mirror_exposure(), 1.0);
Expand Down
1 change: 1 addition & 0 deletions pgdog/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ libc = "0.2"

[target.'cfg(not(target_env = "msvc"))'.dependencies]
tikv-jemallocator = "0.6"
tikv-jemalloc-ctl = "0.6"


[build-dependencies]
Expand Down
16 changes: 12 additions & 4 deletions pgdog/src/backend/databases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,12 @@ pub fn init() -> Result<(), Error> {
let config = config();
replace_databases(from_config(&config), false)?;

// Resize query cache
Cache::resize(config.config.general.query_cache_limit);
// Configure query cache limits
Cache::configure(
config.config.general.query_cache_limit,
config.config.general.query_cache_memory_limit,
config.config.general.query_cache_ttl,
);

// Start two-pc manager.
let _monitor = Manager::get();
Expand Down Expand Up @@ -151,8 +155,12 @@ pub fn reload() -> Result<(), Error> {
.write()
.close_unused(new_config.config.general.prepared_statements_limit);

// Resize query cache.
Cache::resize(new_config.config.general.query_cache_limit);
// Configure query cache limits.
Cache::configure(
new_config.config.general.query_cache_limit,
new_config.config.general.query_cache_memory_limit,
new_config.config.general.query_cache_ttl,
);

Ok(())
}
Expand Down
3 changes: 3 additions & 0 deletions pgdog/src/frontend/prepared_statements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use tracing::debug;

use crate::{
config::{PreparedStatements as PreparedStatementsLevel, config},
frontend::router::parser::Cache,
net::{Parse, ProtocolMessage},
};

Expand Down Expand Up @@ -189,6 +190,8 @@ pub fn start_maintenance() {
pub fn run_maintenance() {
let capacity = config().config.general.prepared_statements_limit;
PreparedStatements::global().write().close_unused(capacity);
// Drop AST cache entries idle beyond their time-to-idle (no-op when disabled).
Cache::sweep();
}

#[cfg(test)]
Expand Down
24 changes: 16 additions & 8 deletions pgdog/src/frontend/router/parser/cache/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,22 @@ pub struct AstInner {
impl AstInner {
/// Create new AST record, with no rewrite or comment routing.
#[cfg(feature = "new_parser")]
pub(crate) fn new(ast: Owned<StmtList>) -> Self {
pub(crate) fn new(ast: Owned<StmtList>, query_without_comment: Arc<str>) -> Self {
Self {
ast,
stats: Mutex::new(Stats::new()),
rewrite_plan: RewritePlan::default(),
query_without_comment: "".into(),
query_without_comment,
}
}

#[cfg(not(feature = "new_parser"))]
pub(crate) fn old(ast: ParseResult) -> Self {
pub(crate) fn old(ast: ParseResult, query_without_comment: Arc<str>) -> Self {
Self {
ast,
stats: Mutex::new(Stats::new()),
rewrite_plan: RewritePlan::default(),
query_without_comment: "".into(),
query_without_comment,
}
}
}
Expand All @@ -84,6 +84,14 @@ impl Deref for Ast {
}

impl Ast {
/// Rough byte footprint of this cache entry, used as a fallback when the
/// jemalloc allocation measurement is unavailable. The query text length
/// tracks entry weight well enough for the memory budget: a wide report
/// query has a long body, `SELECT 1` a short one.
pub fn approx_size(&self) -> usize {
self.query_without_comment.len()
}

/// Parse statement and run the rewrite engine, if necessary.
pub(super) fn new(
query: &AstQuery,
Expand Down Expand Up @@ -195,7 +203,7 @@ impl Ast {
comment_role: None,
comment_shard: None,
query_parser_engine,
inner: Arc::new(AstInner::new(ast.into_inner())),
inner: Arc::new(AstInner::new(ast.into_inner(), query.into())),
})
}

Expand All @@ -213,7 +221,7 @@ impl Ast {
comment_role: None,
comment_shard: None,
query_parser_engine,
inner: Arc::new(AstInner::old(ast)),
inner: Arc::new(AstInner::old(ast, query.into())),
})
}
}
Expand All @@ -228,7 +236,7 @@ impl Ast {
comment_role: None,
comment_shard: None,
query_parser_engine: QueryParserEngine::default(),
inner: Arc::new(AstInner::new(stmts)),
inner: Arc::new(AstInner::new(stmts, "".into())),
}
}

Expand All @@ -240,7 +248,7 @@ impl Ast {
comment_role: None,
comment_shard: None,
query_parser_engine: QueryParserEngine::default(),
inner: Arc::new(AstInner::old(parse_result)),
inner: Arc::new(AstInner::old(parse_result, "".into())),
}
}

Expand Down
Loading