Skip to content

Commit 1aa1236

Browse files
committed
- work on collection
1 parent c9b5f56 commit 1aa1236

File tree

4 files changed

+158
-6
lines changed

4 files changed

+158
-6
lines changed

sqlalchemy_collectd/collector.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from sqlalchemy import event
2+
import threading
3+
4+
5+
class Collector(object):
6+
collectors = {}
7+
create_mutex = threading.Mutex()
8+
9+
def __init__(self, name):
10+
self.name = name
11+
12+
# all identifiers for known DBAPI connections
13+
self.connections = set()
14+
15+
# identifers for connections that have not been checked out
16+
# or were checked in
17+
self.checkedin = set()
18+
19+
# identifiers for connections where we've seen begin().
20+
# doesn't include DBAPI implicit transactions
21+
self.transactions = set()
22+
23+
# note these are prior to being closed and/or discarded
24+
self.invalidated = set()
25+
26+
# detached connections.
27+
self.detached = set()
28+
29+
@classmethod
30+
def collector_for_name(cls, name):
31+
cls.create_mutex.acquire()
32+
try:
33+
if name not in cls.collectors:
34+
cls.collectors[name] = collector = Collector(name)
35+
return collector
36+
else:
37+
return cls.collectors[name]
38+
finally:
39+
cls.create_mutex.release()
40+
41+
def conn_ident(self, dbapi_connection):
42+
return id(dbapi_connection)
43+
44+
def _connect_evt(self, dbapi_conn, connection_rec):
45+
id_ = self.conn_ident(dbapi_conn)
46+
self.connections.add(id_)
47+
self.checkedin.add(id_)
48+
49+
def _checkout_evt(self, dbapi_conn, connection_rec, connection_proxy):
50+
id_ = self.conn_ident(dbapi_conn)
51+
self.checkedin.remove(id_)
52+
53+
def _checkin_evt(self, dbapi_conn, connection_rec):
54+
id_ = self.conn_ident(dbapi_conn)
55+
self.checkedin.add(id_)
56+
57+
def _invalidate_evt(self, dbapi_conn, connection_rec):
58+
id_ = self.conn_ident(dbapi_conn)
59+
self.invalidated.add(id_)
60+
61+
def _reset_evt(self, dbapi_conn, connection_rec):
62+
id_ = self.conn_ident(dbapi_conn)
63+
# may or may not have been part of "transactions"
64+
self.transactions.discard(id_)
65+
66+
def _close_evt(self, dbapi_conn, connection_rec):
67+
id_ = self.conn_ident(dbapi_conn)
68+
self.transactions.discard(id_)
69+
self.invalidated.discard(id_)
70+
self.checkedin.discard(id_)
71+
72+
if not self.connections.discard(id_):
73+
self._warn_missing_connection(dbapi_conn)
74+
75+
# this shouldn't be there
76+
if self.detached.discard(id_):
77+
self._warn("shouldn't have detached")
78+
79+
def _detach_evt(self, dbapi_conn, connection_rec):
80+
id_ = self.conn_ident(dbapi_conn)
81+
self.detached.add(id_)
82+
83+
def _close_detached_evt(self, dbapi_conn):
84+
id_ = self.conn_ident(dbapi_conn)
85+
86+
if not self.connections.discard(id_):
87+
self._warn_missing_connection(dbapi_conn)
88+
89+
self.transactions.discard(id_)
90+
self.invalidated.discard(id_)
91+
self.checkedin.discard(id_)
92+
self.detached.discard(id_)
93+
94+
def add_engine(self, sqlalchemy_engine):
95+
eng = sqlalchemy_engine
96+
event.listen(eng, "connect", self._connect_evt)
97+
event.listen(eng, "checkout", self._checkout_evt)
98+
event.listen(eng, "checkin", self._checkin_evt)
99+
event.listen(eng, "invalidate", self._invalidate_evt)
100+
event.listen(eng, "soft_invalidate", self._invalidate_evt)
101+
event.listen(eng, "reset", self._reset_evt)
102+
event.listen(eng, "close", self._close_evt)
103+
event.listen(eng, "detach", self._detach_evt)
104+
event.listen(eng, "close_detached", self._close_detached_evt)
105+

sqlalchemy_collectd/plugin.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from sqlalchemy.engine import CreateEnginePlugin
2+
3+
4+
class Plugin(CreateEnginePlugin):
5+
def __init__(self, url, kwargs):
6+
self.url = url
7+
8+
def handle_dialect_kwargs(self, dialect_cls, dialect_args):
9+
"""parse and modify dialect kwargs"""
10+
11+
def handle_pool_kwargs(self, pool_cls, pool_args):
12+
"""parse and modify pool kwargs"""
13+
14+
def engine_created(self, engine):
15+
"""Receive the :class:`.Engine` object when it is fully constructed.
16+
17+
The plugin may make additional changes to the engine, such as
18+
registering engine or connection pool events.
19+
20+
"""
21+
22+

sqlalchemy_collectd/tests/test_collectd.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ def test_encode_type_values(self):
1414
)
1515

1616
self.assertEqual(
17-
b'\x00\x06\x00\x18\x00\x02\x01\x02\xc9v\xbe\x9f\x1a\xcf9'
18-
b'@\x00\x00\x00\x00\x00\x00\x01\xc2',
17+
b'\x00\x06' # TYPE_VALUES
18+
b'\x00\x18' # part length
19+
b'\x00\x02' # number of values
20+
b'\x01\x02' # dstype codes GAUGE, DERIVE
21+
b'\xc9v\xbe\x9f\x1a\xcf9@' # 8 bytes for 25.809
22+
b'\x00\x00\x00\x00\x00\x00\x01\xc2', # 8 bytes for 450
1923
type_.encode_values(25.809, 450)
2024
)
2125

@@ -36,9 +40,23 @@ def test_message_construct(self):
3640
sender.send(connection, 1517607042.95968, 25.809, 450)
3741

3842
self.assertEqual(
39-
[
40-
mock.call(b'\x00\x00\x00\rsomehost\x00\x00\x01\x00\x0c\x00\x00\x00\x00Zt\xd8\x82\x00\x02\x00\x0fsomeplugin\x00\x00\x03\x00\x17someplugininstance\x00\x00\x04\x00\x0cmy_type\x00\x00\x07\x00\x0c\x00\x00\x00\x00\x00\x00\x00\n\x00\x05\x00\x15sometypeinstance\x00\x00\x06\x00\x18\x00\x02\x01\x02\xc9v\xbe\x9f\x1a\xcf9@\x00\x00\x00\x00\x00\x00\x01\xc2')
41-
],
43+
[mock.call(
44+
b'\x00\x00\x00\rsomehost\x00' # TYPE_HOST
45+
b'\x00\x01\x00\x0c\x00\x00\x00\x00Zt\xd8\x82' # TYPE_TIME
46+
b'\x00\x02\x00\x0fsomeplugin\x00' # TYPE_PLUGIN
47+
# TYPE_PLUGIN_INSTANCE
48+
b'\x00\x03\x00\x17someplugininstance\x00'
49+
b'\x00\x04\x00\x0cmy_type\x00' # TYPE_TYPE
50+
# TYPE_TIMESTAMP
51+
b'\x00\x07\x00\x0c\x00\x00\x00\x00\x00\x00\x00\n'
52+
b'\x00\x05\x00\x15sometypeinstance\x00' # TYPE_TYPE_INSTANCE
53+
b'\x00\x06' # TYPE_VALUES
54+
b'\x00\x18' # part length
55+
b'\x00\x02' # number of values
56+
b'\x01\x02' # dstype codes GAUGE, DERIVE
57+
b'\xc9v\xbe\x9f\x1a\xcf9@' # 8 bytes for 25.809
58+
b'\x00\x00\x00\x00\x00\x00\x01\xc2' # 8 bytes for 450
59+
)],
4260
connection.send.mock_calls
4361
)
4462

types.db

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
1-
sqlalchemy_pool numpools:GAUGE:0:U, checkedout:GAUGE:0:U, checkedin:GAUGE:0:U, detached:GAUGE:0:U, total:GAUGE:0:U
1+
sqlalchemy_pool numpools:GAUGE:0:U, checkedout:GAUGE:0:U, checkedin:GAUGE:0:U, detached:GAUGE:0:U, invalidated:GAUGE:0:U, total:GAUGE:0:U
2+
sqlalchemy_checkouts count:DERIVE:0:U
3+
sqlalchemy_commits count:DERIVE:0:U
4+
sqlalchemy_rollbacks count:DERIVE:0:U
5+
sqlalchemy_invalidated count:DERIVE:0:U
6+
sqlalchemy_transactions count:GAUGE:0:U
7+
8+
29

0 commit comments

Comments
 (0)