#ifndef ODYSSEY_STAT_H #define ODYSSEY_STAT_H /* * Odyssey. * * Scalable PostgreSQL connection pooler. */ #define QUANTILES_WINDOW 2 #define QUANTILES_COMPRESSION 100 typedef struct od_stat_state od_stat_state_t; typedef struct od_stat od_stat_t; struct od_stat_state { uint64_t query_time_start; uint64_t tx_time_start; }; struct od_stat { bool enable_quantiles; uint8_t current_tdigest; od_atomic_u64_t count_query; od_atomic_u64_t count_tx; od_atomic_u64_t query_time; od_atomic_u64_t tx_time; od_atomic_u64_t recv_server; od_atomic_u64_t recv_client; td_histogram_t *transaction_hgram[QUANTILES_WINDOW]; td_histogram_t *query_hgram[QUANTILES_WINDOW]; }; static inline void od_stat_state_init(od_stat_state_t *state) { memset(state, 0, sizeof(*state)); } static inline void od_stat_init(od_stat_t *stat) { memset(stat, 0, sizeof(*stat)); } static inline void od_stat_free(od_stat_t *stat) { for (size_t i = 0; i < QUANTILES_WINDOW; ++i) { td_free(stat->transaction_hgram[i]); td_free(stat->query_hgram[i]); } } static inline void od_stat_query_start(od_stat_state_t *state) { if (!state->query_time_start) state->query_time_start = machine_time_us(); if (!state->tx_time_start) state->tx_time_start = machine_time_us(); } static inline void od_stat_query_end(od_stat_t *stat, od_stat_state_t *state, int in_transaction, int64_t *query_time) { int64_t diff; if (state->query_time_start) { diff = machine_time_us() - state->query_time_start; if (diff > 0) { *query_time = diff; od_atomic_u64_add(&stat->query_time, diff); od_atomic_u64_inc(&stat->count_query); if (stat->enable_quantiles) { td_add(stat->query_hgram[stat->current_tdigest], diff, 1); } } state->query_time_start = 0; } if (in_transaction) return; if (state->tx_time_start) { diff = machine_time_us() - state->tx_time_start; if (diff > 0) { od_atomic_u64_add(&stat->tx_time, diff); od_atomic_u64_inc(&stat->count_tx); if (stat->enable_quantiles) { td_add(stat->transaction_hgram [stat->current_tdigest], diff, 1); } } state->tx_time_start = 0; } } static inline void od_stat_recv_server(od_stat_t *stat, uint64_t bytes) { od_atomic_u64_add(&stat->recv_server, bytes); } static inline void od_stat_recv_client(od_stat_t *stat, uint64_t bytes) { od_atomic_u64_add(&stat->recv_client, bytes); } static inline void od_stat_copy(od_stat_t *dst, od_stat_t *src) { dst->count_query = od_atomic_u64_of(&src->count_query); dst->count_tx = od_atomic_u64_of(&src->count_tx); dst->query_time = od_atomic_u64_of(&src->query_time); dst->tx_time = od_atomic_u64_of(&src->tx_time); dst->recv_client = od_atomic_u64_of(&src->recv_client); dst->recv_server = od_atomic_u64_of(&src->recv_server); } static inline void od_stat_sum(od_stat_t *sum, od_stat_t *stat) { sum->count_query += od_atomic_u64_of(&stat->count_query); sum->count_tx += od_atomic_u64_of(&stat->count_tx); sum->query_time += od_atomic_u64_of(&stat->query_time); sum->tx_time += od_atomic_u64_of(&stat->tx_time); sum->recv_client += od_atomic_u64_of(&stat->recv_client); sum->recv_server += od_atomic_u64_of(&stat->recv_server); } static inline void od_stat_update_of(od_atomic_u64_t *prev, od_atomic_u64_t *current) { /* todo: this could be made more optimal */ /* prev <= current */ __atomic_store(prev, current, __ATOMIC_SEQ_CST); } static inline void od_stat_update(od_stat_t *dst, od_stat_t *stat) { od_stat_update_of(&dst->count_query, &stat->count_query); od_stat_update_of(&dst->count_tx, &stat->count_tx); od_stat_update_of(&dst->query_time, &stat->query_time); od_stat_update_of(&dst->tx_time, &stat->tx_time); od_stat_update_of(&dst->recv_client, &stat->recv_client); od_stat_update_of(&dst->recv_server, &stat->recv_server); } static inline void od_stat_average(od_stat_t *avg, od_stat_t *current, od_stat_t *prev, uint64_t prev_time_us) { const uint64_t interval_usec = 1000000; uint64_t interval_us; interval_us = machine_time_us() - prev_time_us; if (interval_us <= 0) return; uint64_t count_query; count_query = od_atomic_u64_of(¤t->count_query) - od_atomic_u64_of(&prev->count_query); uint64_t count_tx; count_tx = od_atomic_u64_of(¤t->count_tx) - od_atomic_u64_of(&prev->count_tx); avg->count_query = (count_query * interval_usec) / interval_us; avg->count_tx = (count_tx * interval_usec) / interval_us; if (count_query > 0) { avg->query_time = (od_atomic_u64_of(¤t->query_time) - od_atomic_u64_of(&prev->query_time)) / count_query; } if (count_tx > 0) { avg->tx_time = (od_atomic_u64_of(¤t->tx_time) - od_atomic_u64_of(&prev->tx_time)) / count_tx; } avg->recv_client = ((od_atomic_u64_of(¤t->recv_client) - od_atomic_u64_of(&prev->recv_client)) * interval_usec) / interval_us; avg->recv_server = ((od_atomic_u64_of(¤t->recv_server) - od_atomic_u64_of(&prev->recv_server)) * interval_usec) / interval_us; } #endif /* ODYSSEY_STAT_H */