From 998cc16a3c6f8f5f2b34715674590e54950cfb32 Mon Sep 17 00:00:00 2001 From: Calvin Hughes Date: Mon, 13 Nov 2023 19:24:39 +0000 Subject: [PATCH] Expose clients maxwait time in SHOW CLIENTS response via admin (#639) * Expose clients maxwait time in SHOW CLIENTS response via PgCat admin Displays the maxwait via maxwait_seconds and maxwait_us columns for each client that can be used to track down the wait time per client in a case where the overall pool stats shows waiting time. The maxwait_us, similar to the pool stats setup, is configured to display as a remainder alongside the maxwait_seconds. * Use maxwait instead of maxwait_seconds to match pools column name --------- Co-authored-by: Calvin Hughes <9379992+calvinhughes@users.noreply.github.com> --- src/admin.rs | 5 +++++ tests/ruby/stats_spec.rb | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/admin.rs b/src/admin.rs index 80baa3fb..22bbb0af 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -690,6 +690,8 @@ where ("query_count", DataType::Numeric), ("error_count", DataType::Numeric), ("age_seconds", DataType::Numeric), + ("maxwait", DataType::Numeric), + ("maxwait_us", DataType::Numeric), ]; let new_map = get_client_stats(); @@ -697,6 +699,7 @@ where res.put(row_description(&columns)); for (_, client) in new_map { + let max_wait = client.max_wait_time.load(Ordering::Relaxed); let row = vec![ format!("{:#010X}", client.client_id()), client.pool_name(), @@ -710,6 +713,8 @@ where .duration_since(client.connect_time()) .as_secs() .to_string(), + (max_wait / 1_000_000).to_string(), + (max_wait % 1_000_000).to_string(), ]; res.put(data_row(&row)); diff --git a/tests/ruby/stats_spec.rb b/tests/ruby/stats_spec.rb index 132b80a9..ddf63cd3 100644 --- a/tests/ruby/stats_spec.rb +++ b/tests/ruby/stats_spec.rb @@ -329,6 +329,40 @@ admin_conn.close connections.map(&:close) end + + context "when client has waited for a server" do + let(:processes) { Helpers::Pgcat.single_instance_setup("sharded_db", 2) } + + it "shows correct maxwait" do + threads = [] + connections = Array.new(3) { |i| PG::connect("#{pgcat_conn_str}?application_name=app#{i}") } + connections.each do |c| + threads << Thread.new { c.async_exec("SELECT pg_sleep(1.5)") rescue nil } + end + + sleep(2.5) # Allow time for stats to update + admin_conn = PG::connect(processes.pgcat.admin_connection_string) + results = admin_conn.async_exec("SHOW CLIENTS") + + normal_client_results = results.reject { |r| r["database"] == "pgcat" } + + non_waiting_clients = normal_client_results.select { |c| c["maxwait"] == "0" } + waiting_clients = normal_client_results.select { |c| c["maxwait"].to_i > 0 } + + expect(non_waiting_clients.count).to eq(2) + non_waiting_clients.each do |client| + expect(client["maxwait_us"].to_i).to be_between(0, 50_000) + end + + expect(waiting_clients.count).to eq(1) + waiting_clients.each do |client| + expect(client["maxwait_us"].to_i).to be_within(200_000).of(500_000) + end + + admin_conn.close + connections.map(&:close) + end + end end