Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pgbouncer] Fix stats deprecated in pgbouncer 1.8 #1016

Merged
merged 7 commits into from
Jan 19, 2018
Merged
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
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ env:
- TRAVIS_FLAVOR=nginx FLAVOR_VERSION=1.10.2
- TRAVIS_FLAVOR=openstack
# - TRAVIS_FLAVOR=oracle FLAVOR_VERSION=latest
- TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=debian-jessie # pgbouncer-1.5.4
- TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=debian-stretch # pgbouncer-1.7.2
- TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=ubuntu-xenial # pgbouncer-1.7
- TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=1.5
- TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=1.7
- TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=1.8
- TRAVIS_FLAVOR=php_fpm FLAVOR_VERSION=5.5
- TRAVIS_FLAVOR=postgres FLAVOR_VERSION=9.6
- TRAVIS_FLAVOR=powerdns_recursor FLAVOR_VERSION=3.7.3
Expand Down
9 changes: 9 additions & 0 deletions pgbouncer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# CHANGELOG - pgbouncer

1.1.0 / Unreleased
==================

### Changes

* [FEATURE] Added new metrics for PGBouncer 1.8 `SHOW STATS` and `SHOW POOLS` See [#1016][]


1.0.2 / [2017-11-21]
==================

Expand All @@ -24,3 +32,4 @@
<!--- The following link definition list is generated by PimpMyChangelog --->
[#295]: https://github.com/DataDog/integrations-core/issues/295
[#735]: https://github.com/DataDog/integrations-core/issues/735
[#1016]: https://github.com/DataDog/integrations-core/issues/1016
2 changes: 2 additions & 0 deletions pgbouncer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ The PgBouncer check is compatible with all major platforms.
### Metrics
See [metadata.csv](https://github.com/DataDog/integrations-core/blob/master/pgbouncer/metadata.csv) for a list of metrics provided by this check.

Note: Not all metrics are available with all versions of PGBouncer.

### Events
The PGboucer check does not include any event at this time.

Expand Down
2 changes: 1 addition & 1 deletion pgbouncer/datadog_checks/pgbouncer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

PgBouncer = pgbouncer.PgBouncer

__version__ = "1.0.2"
__version__ = "1.1.0"

__all__ = ['pgbouncer']
76 changes: 37 additions & 39 deletions pgbouncer/datadog_checks/pgbouncer/pgbouncer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

# 3p
import psycopg2 as pg
import psycopg2.extras as pgextras

# project
from checks import AgentCheck, CheckException
Expand All @@ -33,14 +34,21 @@ class PgBouncer(AgentCheck):
('database', 'db'),
],
'metrics': [
('total_requests', ('pgbouncer.stats.requests_per_second', RATE)),
('total_requests', ('pgbouncer.stats.requests_per_second', RATE)), # < 1.8
('total_xact_count', ('pgbouncer.stats.transactions_per_second', RATE)), # >= 1.8
('total_query_count', ('pgbouncer.stats.queries_per_second', RATE)), # >= 1.8
('total_received', ('pgbouncer.stats.bytes_received_per_second', RATE)),
('total_sent', ('pgbouncer.stats.bytes_sent_per_second', RATE)),
('total_query_time', ('pgbouncer.stats.total_query_time', GAUGE)),
('avg_req', ('pgbouncer.stats.avg_req', GAUGE)),
('total_query_time', ('pgbouncer.stats.total_query_time', RATE)),
('total_xact_time', ('pgbouncer.stats.total_transaction_time', RATE)), # >= 1.8
('avg_req', ('pgbouncer.stats.avg_req', GAUGE)), # < 1.8
('avg_xact_count', ('pgbouncer.stats.avg_transaction_count', GAUGE)), # >= 1.8
('avg_query_count', ('pgbouncer.stats.avg_query_count', GAUGE)), # >= 1.8
('avg_recv', ('pgbouncer.stats.avg_recv', GAUGE)),
('avg_sent', ('pgbouncer.stats.avg_sent', GAUGE)),
('avg_query', ('pgbouncer.stats.avg_query', GAUGE)),
('avg_query', ('pgbouncer.stats.avg_query', GAUGE)), # < 1.8
('avg_xact_time', ('pgbouncer.stats.avg_transaction_time', GAUGE)), # >= 1.8
('avg_query_time', ('pgbouncer.stats.avg_query_time', GAUGE)), # >= 1.8
],
'query': """SHOW STATS""",
}
Expand Down Expand Up @@ -87,51 +95,41 @@ def _collect_stats(self, db, instance_tags):
metric_scope = [self.STATS_METRICS, self.POOLS_METRICS]

try:
cursor = db.cursor()
results = None

for scope in metric_scope:

metrics = scope['metrics']
cols = [m[0] for m in metrics]

try:
with db.cursor(cursor_factory=pgextras.DictCursor) as cursor:
for scope in metric_scope:
descriptors = scope['descriptors']
metrics = scope['metrics']
query = scope['query']
self.log.debug("Running query: %s" % query)
cursor.execute(query)

results = cursor.fetchall()
try:
self.log.debug("Running query: %s", query)
cursor.execute(query)

except pg.Error as e:
self.log.warning("Not all metrics may be available: %s" % str(e))
continue
rows = cursor.fetchall()

for row in results:
if row[0] == self.DB_NAME:
continue
except pg.Error:
self.log.exception("Not all metrics may be available")

desc = scope['descriptors']
else:
for row in rows:
self.log.debug("Processing row: %r", row)

# Some versions of pgbouncer have extra fields at the end of SHOW POOLS
if len(row) == len(cols) + len(desc) + 1:
row = row[:-1]
elif len(row) == len(cols) + len(desc) + 2:
row = row[:-2]
# Skip the "pgbouncer" database
if row['database'] == self.DB_NAME:
continue

assert len(row) == len(cols) + len(desc)
tags = list(instance_tags)
tags += ["%s:%s" % (tag, row[column]) for (column, tag) in descriptors if column in row]
for (column, (name, reporter)) in metrics:
if column in row:
reporter(self, name, row[column], tags)

tags = list(instance_tags)
tags += ["%s:%s" % (d[0][1], d[1]) for d in zip(desc, row[:len(desc)])]
for i, (key_name, (mname, mtype)) in enumerate(metrics):
value = row[i + len(desc)]
mtype(self, mname, value, tags)
if not rows:
self.log.warning("No results were found for query: %s", query)

if not results:
self.warning('No results were found for query: "%s"' % query)
except pg.Error:
self.log.exception("Connection error")

cursor.close()
except pg.Error as e:
self.log.error("Connection error: %s" % str(e))
raise ShouldRestartException

def _get_connect_kwargs(self, host, port, user, password, database_url):
Expand Down
2 changes: 1 addition & 1 deletion pgbouncer/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"mac_os"
],
"package_deps": false,
"version": "1.0.2",
"version": "1.1.0",
"guid": "51386802-4502-4991-b592-27eff1ca111c",
"public_title": "Datadog-PGBouncer Integration",
"categories":["data store"],
Expand Down
15 changes: 11 additions & 4 deletions pgbouncer/metadata.csv
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name
pgbouncer.stats.requests_per_second,gauge,,request,second,The request rate,0,pgbouncer,requests
pgbouncer.stats.bytes_received_per_second,gauge,,byte,second,The total network traffic received,0,pgbouncer,bytes received
pgbouncer.stats.bytes_sent_per_second,gauge,,byte,second,The total network traffic sent,0,pgbouncer,bytes sent
pgbouncer.stats.total_query_time,gauge,,microsecond,,Time spent by pgbouncer actively querying PostgreSQL,0,pgbouncer,query time
pgbouncer.stats.requests_per_second,rate,,request,second,The request rate,0,pgbouncer,requests
pgbouncer.stats.queries_per_second,rate,,query,second,The query rate,0,pgbouncer,queries
pgbouncer.stats.transactions_per_second,rate,,transaction,second,The transaction rate,0,pgbouncer,transactions
pgbouncer.stats.bytes_received_per_second,rate,,byte,second,The total network traffic received,0,pgbouncer,bytes received
pgbouncer.stats.bytes_sent_per_second,rate,,byte,second,The total network traffic sent,0,pgbouncer,bytes sent
pgbouncer.stats.total_query_time,rate,,microsecond,,Time spent by pgbouncer actively querying PostgreSQL,0,pgbouncer,query time
pgbouncer.stats.total_transaction_time,rate,,microsecond,,Time spent by pgbouncer in transactions,0,pgbouncer,transaction time
pgbouncer.stats.avg_req,gauge,,request,second,The average number of requests per second in last stat period,0,pgbouncer,requests per second
pgbouncer.stats.avg_query_count,gauge,,query,second,The average number of queries per second in last stat period,0,pgbouncer,queries per second
pgbouncer.stats.avg_transaction_count,gauge,,transaction,second,The average number of transactions per second in last stat period,0,pgbouncer,transactions per second
pgbouncer.stats.avg_recv,gauge,,byte,second,The client network traffic received,0,pgbouncer,client bytes received
pgbouncer.stats.avg_sent,gauge,,byte,second,The client network traffic sent,0,pgbouncer,client bytes sent
pgbouncer.stats.avg_query,gauge,,microsecond,,The average query duration,-1,pgbouncer,query duration
pgbouncer.stats.avg_query_time,gauge,,microsecond,,The average query duration,-1,pgbouncer,query duration
pgbouncer.stats.avg_transaction_time,gauge,,microsecond,,The average transaction duration,-1,pgbouncer,transaction duration
pgbouncer.pools.cl_active,gauge,,connection,,Client connections linked to server connection and able to process queries,0,pgbouncer,active conns
pgbouncer.pools.cl_waiting,gauge,,connection,,Client connections waiting on a server connection,-1,pgbouncer,waiting conns
pgbouncer.pools.sv_active,gauge,,connection,,Server connections linked to a client connection,0,pgbouncer,server conns
Expand Down
12 changes: 11 additions & 1 deletion pgbouncer/test/ci/pgbouncer.rake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ pgbname = 'dd-test-pgbouncer'
pg_resources_path = (ENV['SDK_HOME']).to_s + '/pgbouncer/test/ci/resources/pg'
pgb_resources_path = (ENV['SDK_HOME']).to_s + '/pgbouncer/test/ci/resources/pgb'

pgbouncer_images = {
# pgbouncer-1.5.4 on debian jessie
'1.5' => 'kotaimen/pgbouncer:debian-jessie',
# pgbouncer-1.7.2 on debian stretch
'1.7' => 'kotaimen/pgbouncer:debian-stretch',
# pgbouncer-1.8.1 on debian jessie
'1.8' => 'inonit/pgbouncer:latest',
'latest' => 'inonit/pgbouncer:latest',
}

namespace :ci do
namespace :pgbouncer do |flavor|
task before_install: ['ci:common:before_install'] do
Expand All @@ -32,7 +42,7 @@ namespace :ci do
end
puts 'Postgres is running, installing PgBouncer'
sh %(docker run -d --name #{pgbname} --link #{pgname}:postgres -v #{pgb_resources_path}:/etc/pgbouncer:ro -p \
16432:6432 kotaimen/pgbouncer:#{pgbouncer_version})
16432:6432 #{pgbouncer_images.fetch(pgbouncer_version)})
sleep_for 10
end

Expand Down
25 changes: 21 additions & 4 deletions pgbouncer/test/test_pgbouncer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Licensed under Simplified BSD License (see LICENSE)

# stdlib
import os
import time
from types import ListType

Expand All @@ -22,6 +23,9 @@ class TestPgbouncer(AgentCheckTest):
CHECK_NAME = 'pgbouncer'

def test_checks(self):
pgbouncer_version = os.environ.get('FLAVOR_VERSION', 'latest')
pgbouncer_pre18 = pgbouncer_version in ('1.5', '1.7')

config = {
'init_config': {},
'instances': [
Expand Down Expand Up @@ -60,11 +64,18 @@ def test_checks(self):
self.assertMetric('pgbouncer.pools.sv_login')
self.assertMetric('pgbouncer.pools.maxwait')

self.assertMetric('pgbouncer.stats.total_query_time')
self.assertMetric('pgbouncer.stats.avg_req')
self.assertMetric('pgbouncer.stats.avg_recv')
self.assertMetric('pgbouncer.stats.avg_sent')
self.assertMetric('pgbouncer.stats.avg_query')

if pgbouncer_pre18:
self.assertMetric('pgbouncer.stats.avg_req')
self.assertMetric('pgbouncer.stats.avg_query')
else:
self.assertMetric('pgbouncer.stats.avg_transaction_time')
self.assertMetric('pgbouncer.stats.avg_query_time')
self.assertMetric('pgbouncer.stats.avg_transaction_count')
self.assertMetric('pgbouncer.stats.avg_query_count')

# Rate metrics, need 2 collection rounds
try:
connection = pg.connect(
Expand All @@ -80,7 +91,13 @@ def test_checks(self):
pass
time.sleep(1)
self.run_check(config)
self.assertMetric('pgbouncer.stats.requests_per_second')
if pgbouncer_pre18:
self.assertMetric('pgbouncer.stats.requests_per_second')
else:
self.assertMetric('pgbouncer.stats.queries_per_second')
self.assertMetric('pgbouncer.stats.transactions_per_second')
self.assertMetric('pgbouncer.stats.total_transaction_time')
self.assertMetric('pgbouncer.stats.total_query_time')
self.assertMetric('pgbouncer.stats.bytes_received_per_second')
self.assertMetric('pgbouncer.stats.bytes_sent_per_second')

Expand Down