11from sqlalchemy import event
22import threading
3+ import weakref
4+ import logging
5+ from . import worker
36
47
5- class Collector (object ):
6- collectors = {}
7- create_mutex = threading .Mutex ()
8+ class CollectionTarget (object ):
9+ targets = {}
10+ create_mutex = threading .Lock ()
811
912 def __init__ (self , name ):
1013 self .name = name
1114
15+ self .collectors = weakref .WeakSet ()
16+
1217 # all identifiers for known DBAPI connections
1318 self .connections = set ()
1419
1520 # identifers for connections that have not been checked out
1621 # or were checked in
1722 self .checkedin = set ()
1823
19- # identifiers for connections where we've seen begin().
20- # doesn't include DBAPI implicit transactions
21- self .transactions = set ()
22-
2324 # note these are prior to being closed and/or discarded
2425 self .invalidated = set ()
2526
2627 # detached connections.
2728 self .detached = set ()
2829
30+ # identifiers for connections where we've seen begin().
31+ # doesn't include DBAPI implicit transactions
32+ self .transactions = set ()
33+
2934 @classmethod
30- def collector_for_name (cls , name ):
35+ def collection_for_name (cls , name ):
3136 cls .create_mutex .acquire ()
3237 try :
33- if name not in cls .collectors :
34- cls .collectors [name ] = collector = Collector (name )
35- return collector
38+ if name not in cls .targets :
39+ cls .targets [name ] = collection = CollectionTarget (name )
40+ return collection
3641 else :
37- return cls .collectors [name ]
42+ return cls .targets [name ]
3843 finally :
3944 cls .create_mutex .release ()
4045
46+ @property
47+ def num_pools (self ):
48+ return len (self .collectors )
49+
50+ @property
51+ def num_checkedout (self ):
52+ checkedout = self .connections .\
53+ difference (self .detached ).\
54+ difference (self .invalidated ).\
55+ difference (self .checkedin )
56+ return len (checkedout )
57+
58+ @property
59+ def num_checkedin (self ):
60+ return len (self .checkedin )
61+
62+ @property
63+ def num_detached (self ):
64+ return len (self .detached )
65+
66+ @property
67+ def num_invalidated (self ):
68+ return len (self .invalidated )
69+
70+ @property
71+ def num_connections (self ):
72+ return len (self .connections )
73+
74+ @property
75+ def num_transactions (self ):
76+ return len (self .transactions )
77+
78+
79+ class EngineCollector (object ):
80+
81+ def __init__ (self , collection_target , engine ):
82+ self .collection_target = collection_target
83+ self .engine = engine
84+ collection_target .collectors .add (self )
85+
86+ eng = engine
87+ event .listen (eng , "connect" , self ._connect_evt )
88+ event .listen (eng , "checkout" , self ._checkout_evt )
89+ event .listen (eng , "checkin" , self ._checkin_evt )
90+ event .listen (eng , "invalidate" , self ._invalidate_evt )
91+ event .listen (eng , "soft_invalidate" , self ._invalidate_evt )
92+ event .listen (eng , "reset" , self ._reset_evt )
93+ event .listen (eng , "close" , self ._close_evt )
94+ event .listen (eng , "detach" , self ._detach_evt )
95+ event .listen (eng , "close_detached" , self ._close_detached_evt )
96+
97+ self .connections = collection_target .connections
98+ self .checkedin = collection_target .checkedin
99+ self .transactions = collection_target .transactions
100+ self .invalidated = collection_target .invalidated
101+ self .detached = collection_target .detached
102+ self .logger = logging .getLogger ("%s.%s" % (__name__ , eng .logging_name ))
103+
41104 def conn_ident (self , dbapi_connection ):
42105 return id (dbapi_connection )
43106
44107 def _connect_evt (self , dbapi_conn , connection_rec ):
108+ worker ._check_threads_started ()
45109 id_ = self .conn_ident (dbapi_conn )
46110 self .connections .add (id_ )
47111 self .checkedin .add (id_ )
@@ -69,12 +133,23 @@ def _close_evt(self, dbapi_conn, connection_rec):
69133 self .invalidated .discard (id_ )
70134 self .checkedin .discard (id_ )
71135
72- if not self .connections .discard (id_ ):
136+ try :
137+ self .connections .remove (id_ )
138+ except KeyError :
73139 self ._warn_missing_connection (dbapi_conn )
74140
75141 # this shouldn't be there
76- if self .detached . discard ( id_ ) :
142+ if id_ in self .detached :
77143 self ._warn ("shouldn't have detached" )
144+ self .detached .discard (id_ )
145+
146+ def _warn_missing_connection (self , dbapi_conn ):
147+ self ._warn (
148+ "connection %s was closed but not part of "
149+ "total connections" % dbapi_conn )
150+
151+ def _warn (self , msg ):
152+ self .logger .warn (msg )
78153
79154 def _detach_evt (self , dbapi_conn , connection_rec ):
80155 id_ = self .conn_ident (dbapi_conn )
@@ -83,23 +158,15 @@ def _detach_evt(self, dbapi_conn, connection_rec):
83158 def _close_detached_evt (self , dbapi_conn ):
84159 id_ = self .conn_ident (dbapi_conn )
85160
86- if not self .connections .discard (id_ ):
87- self ._warn_missing_connection (dbapi_conn )
88-
89161 self .transactions .discard (id_ )
90162 self .invalidated .discard (id_ )
91163 self .checkedin .discard (id_ )
92164 self .detached .discard (id_ )
93165
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 )
166+ try :
167+ self .connections .remove (id_ )
168+ except KeyError :
169+ self ._warn_missing_connection (dbapi_conn )
170+
171+
105172
0 commit comments