1
1
from sqlalchemy import event
2
2
import threading
3
+ import weakref
4
+ import logging
5
+ from . import worker
3
6
4
7
5
- class Collector (object ):
6
- collectors = {}
7
- create_mutex = threading .Mutex ()
8
+ class CollectionTarget (object ):
9
+ targets = {}
10
+ create_mutex = threading .Lock ()
8
11
9
12
def __init__ (self , name ):
10
13
self .name = name
11
14
15
+ self .collectors = weakref .WeakSet ()
16
+
12
17
# all identifiers for known DBAPI connections
13
18
self .connections = set ()
14
19
15
20
# identifers for connections that have not been checked out
16
21
# or were checked in
17
22
self .checkedin = set ()
18
23
19
- # identifiers for connections where we've seen begin().
20
- # doesn't include DBAPI implicit transactions
21
- self .transactions = set ()
22
-
23
24
# note these are prior to being closed and/or discarded
24
25
self .invalidated = set ()
25
26
26
27
# detached connections.
27
28
self .detached = set ()
28
29
30
+ # identifiers for connections where we've seen begin().
31
+ # doesn't include DBAPI implicit transactions
32
+ self .transactions = set ()
33
+
29
34
@classmethod
30
- def collector_for_name (cls , name ):
35
+ def collection_for_name (cls , name ):
31
36
cls .create_mutex .acquire ()
32
37
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
36
41
else :
37
- return cls .collectors [name ]
42
+ return cls .targets [name ]
38
43
finally :
39
44
cls .create_mutex .release ()
40
45
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
+
41
104
def conn_ident (self , dbapi_connection ):
42
105
return id (dbapi_connection )
43
106
44
107
def _connect_evt (self , dbapi_conn , connection_rec ):
108
+ worker ._check_threads_started ()
45
109
id_ = self .conn_ident (dbapi_conn )
46
110
self .connections .add (id_ )
47
111
self .checkedin .add (id_ )
@@ -69,12 +133,23 @@ def _close_evt(self, dbapi_conn, connection_rec):
69
133
self .invalidated .discard (id_ )
70
134
self .checkedin .discard (id_ )
71
135
72
- if not self .connections .discard (id_ ):
136
+ try :
137
+ self .connections .remove (id_ )
138
+ except KeyError :
73
139
self ._warn_missing_connection (dbapi_conn )
74
140
75
141
# this shouldn't be there
76
- if self .detached . discard ( id_ ) :
142
+ if id_ in self .detached :
77
143
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 )
78
153
79
154
def _detach_evt (self , dbapi_conn , connection_rec ):
80
155
id_ = self .conn_ident (dbapi_conn )
@@ -83,23 +158,15 @@ def _detach_evt(self, dbapi_conn, connection_rec):
83
158
def _close_detached_evt (self , dbapi_conn ):
84
159
id_ = self .conn_ident (dbapi_conn )
85
160
86
- if not self .connections .discard (id_ ):
87
- self ._warn_missing_connection (dbapi_conn )
88
-
89
161
self .transactions .discard (id_ )
90
162
self .invalidated .discard (id_ )
91
163
self .checkedin .discard (id_ )
92
164
self .detached .discard (id_ )
93
165
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
+
105
172
0 commit comments