Skip to content

Commit 7326069

Browse files
authored
Fixes average stats bug (#436)
* Add test * Fix test * Add fix
1 parent 5056cbe commit 7326069

File tree

4 files changed

+39
-14
lines changed

4 files changed

+39
-14
lines changed

src/stats.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,19 @@ impl Collector {
107107
loop {
108108
interval.tick().await;
109109

110-
for stats in SERVER_STATS.read().values() {
111-
stats.address_stats().update_averages();
110+
// Hold read lock for duration of update to retain all server stats
111+
let server_stats = SERVER_STATS.read();
112+
113+
for stats in server_stats.values() {
114+
if !stats.check_address_stat_average_is_updated_status() {
115+
stats.address_stats().update_averages();
116+
stats.set_address_stat_average_is_updated_status(true);
117+
}
118+
}
119+
120+
// Reset to false for next update
121+
for stats in server_stats.values() {
122+
stats.set_address_stat_average_is_updated_status(false);
112123
}
113124
}
114125
});

src/stats/address.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub struct AddressStats {
3030
pub avg_xact_time: Arc<AtomicU64>,
3131
pub avg_xact_count: Arc<AtomicU64>,
3232
pub avg_wait_time: Arc<AtomicU64>,
33+
34+
// Determines if the averages have been updated since the last time they were reported
35+
pub averages_updated: Arc<AtomicBool>,
3336
}
3437

3538
impl IntoIterator for AddressStats {
@@ -114,15 +117,14 @@ impl AddressStats {
114117

115118
pub fn update_averages(&self) {
116119
let (totals, averages, old_totals) = self.fields_iterators();
117-
for data in itertools::izip!(totals, averages, old_totals) {
118-
let (total, average, old_total) = data;
119-
let total = total.load(Ordering::Relaxed);
120-
let old = old_total.load(Ordering::Relaxed);
120+
for (total, average, old_total) in itertools::izip!(totals, averages, old_totals) {
121+
let total_value = total.load(Ordering::Relaxed);
122+
let old_total_value = old_total.load(Ordering::Relaxed);
121123
average.store(
122-
(total - old) / (crate::stats::STAT_PERIOD / 1_000),
124+
(total_value - old_total_value) / (crate::stats::STAT_PERIOD / 1_000),
123125
Ordering::Relaxed,
124126
); // Avg / second
125-
old_total.store(total, Ordering::Relaxed);
127+
old_total.store(total_value, Ordering::Relaxed);
126128
}
127129
}
128130

src/stats/server.rs

+11
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,17 @@ impl ServerStats {
139139
self.address.stats.clone()
140140
}
141141

142+
pub fn check_address_stat_average_is_updated_status(&self) -> bool {
143+
self.address.stats.averages_updated.load(Ordering::Relaxed)
144+
}
145+
146+
pub fn set_address_stat_average_is_updated_status(&self, is_checked: bool) {
147+
self.address
148+
.stats
149+
.averages_updated
150+
.store(is_checked, Ordering::Relaxed);
151+
}
152+
142153
// Helper methods for show_servers
143154
pub fn pool_name(&self) -> String {
144155
self.pool_stats.database()

tests/ruby/admin_spec.rb

+7-6
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@
1414
describe "SHOW STATS" do
1515
context "clients connect and make one query" do
1616
it "updates *_query_time and *_wait_time" do
17-
connection = PG::connect("#{pgcat_conn_str}?application_name=one_query")
18-
connection.async_exec("SELECT pg_sleep(0.25)")
19-
connection.async_exec("SELECT pg_sleep(0.25)")
20-
connection.async_exec("SELECT pg_sleep(0.25)")
21-
connection.close
17+
connections = Array.new(3) { PG::connect("#{pgcat_conn_str}?application_name=one_query") }
18+
connections.each do |c|
19+
Thread.new { c.async_exec("SELECT pg_sleep(0.25)") }
20+
end
21+
sleep(1)
22+
connections.map(&:close)
2223

2324
# wait for averages to be calculated, we shouldn't do this too often
2425
sleep(15.5)
2526
admin_conn = PG::connect(processes.pgcat.admin_connection_string)
2627
results = admin_conn.async_exec("SHOW STATS")[0]
2728
admin_conn.close
2829
expect(results["total_query_time"].to_i).to be_within(200).of(750)
29-
expect(results["avg_query_time"].to_i).to_not eq(0)
30+
expect(results["avg_query_time"].to_i).to be_within(20).of(50)
3031

3132
expect(results["total_wait_time"].to_i).to_not eq(0)
3233
expect(results["avg_wait_time"].to_i).to_not eq(0)

0 commit comments

Comments
 (0)