/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" static struct event ev_stats; static usec_t old_stamp, new_stamp; static void reset_stats(PgStats *stat) { stat->server_bytes = 0; stat->client_bytes = 0; stat->request_count = 0; stat->query_time = 0; } static void stat_add(PgStats *total, PgStats *stat) { total->server_bytes += stat->server_bytes; total->client_bytes += stat->client_bytes; total->request_count += stat->request_count; total->query_time += stat->query_time; } static void calc_average(PgStats *avg, PgStats *cur, PgStats *old) { uint64_t qcount; usec_t dur = get_cached_time() - old_stamp; reset_stats(avg); if (dur <= 0) return; avg->request_count = USEC * (cur->request_count - old->request_count) / dur; avg->client_bytes = USEC * (cur->client_bytes - old->client_bytes) / dur; avg->server_bytes = USEC * (cur->server_bytes - old->server_bytes) / dur; qcount = cur->request_count - old->request_count; if (qcount > 0) avg->query_time = (cur->query_time - old->query_time) / qcount; } static void write_stats(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) { PgStats avg; calc_average(&avg, stat, old); pktbuf_write_DataRow(buf, "sqqqqqqqq", dbname, stat->request_count, stat->client_bytes, stat->server_bytes, stat->query_time, avg.request_count, avg.client_bytes, avg.server_bytes, avg.query_time); } bool admin_database_stats(PgSocket *client, StatList *pool_list) { PgPool *pool; List *item; PgDatabase *cur_db = NULL; PgStats st_total, st_db, old_db, old_total; int rows = 0; PktBuf *buf; reset_stats(&st_total); reset_stats(&st_db); reset_stats(&old_db); reset_stats(&old_total); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sqqqqqqqq", "database", "total_requests", "total_received", "total_sent", "total_query_time", "avg_req", "avg_recv", "avg_sent", "avg_query"); statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); if (!cur_db) cur_db = pool->db; if (pool->db != cur_db) { write_stats(buf, &st_db, &old_db, cur_db->name); rows ++; cur_db = pool->db; stat_add(&st_total, &st_db); stat_add(&old_total, &old_db); reset_stats(&st_db); reset_stats(&old_db); } stat_add(&st_db, &pool->stats); stat_add(&old_db, &pool->older_stats); } if (cur_db) { write_stats(buf, &st_db, &old_db, cur_db->name); stat_add(&st_total, &st_db); stat_add(&old_total, &old_db); rows ++; } admin_flush(client, buf, "SHOW"); return true; } bool show_stat_totals(PgSocket *client, StatList *pool_list) { PgPool *pool; List *item; PgStats st_total, old_total, avg; PktBuf *buf; reset_stats(&st_total); reset_stats(&old_total); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); stat_add(&st_total, &pool->stats); stat_add(&old_total, &pool->older_stats); } calc_average(&avg, &st_total, &old_total); pktbuf_write_RowDescription(buf, "sq", "name", "value"); #define WTOTAL(name) pktbuf_write_DataRow(buf, "sq", "total_" #name, st_total.name) #define WAVG(name) pktbuf_write_DataRow(buf, "sq", "avg_" #name, avg.name) WTOTAL(request_count); WTOTAL(client_bytes); WTOTAL(server_bytes); WTOTAL(query_time); WAVG(request_count); WAVG(client_bytes); WAVG(server_bytes); WAVG(query_time); admin_flush(client, buf, "SHOW"); return true; } static void refresh_stats(int s, short flags, void *arg) { List *item; PgPool *pool; struct timeval period = { cf_stats_period, 0 }; PgStats old_total, cur_total, avg; reset_stats(&old_total); reset_stats(&cur_total); old_stamp = new_stamp; new_stamp = get_cached_time(); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); pool->older_stats = pool->newer_stats; pool->newer_stats = pool->stats; stat_add(&cur_total, &pool->stats); stat_add(&old_total, &pool->older_stats); } calc_average(&avg, &cur_total, &old_total); /* send totals to logfile */ log_info("Stats: %" PRIu64 " req/s," " in %" PRIu64 " b/s," " out %" PRIu64 " b/s," "query %" PRIu64 " us", avg.request_count, avg.client_bytes, avg.server_bytes, avg.query_time); safe_evtimer_add(&ev_stats, &period); } void stats_setup(void) { struct timeval period = { cf_stats_period, 0 }; new_stamp = get_cached_time(); old_stamp = new_stamp - USEC; /* launch stats */ evtimer_set(&ev_stats, refresh_stats, NULL); safe_evtimer_add(&ev_stats, &period); }