4
4
Optional ,
5
5
List ,
6
6
)
7
+ import threading
8
+ import queue
7
9
8
10
from .session import (
9
11
QuerySessionSync ,
12
14
RetrySettings ,
13
15
retry_operation_sync ,
14
16
)
17
+ from .. import issues
15
18
from .. import convert
16
19
from .._grpc .grpcwrapper import common_utils
17
20
22
25
class QuerySessionPool :
23
26
"""QuerySessionPool is an object to simplify operations with sessions of Query Service."""
24
27
25
- def __init__ (self , driver : common_utils .SupportedDriverType ):
28
+ def __init__ (self , driver : common_utils .SupportedDriverType , size : int = 10 ):
26
29
"""
27
30
:param driver: A driver instance
28
31
"""
29
32
30
33
logger .warning ("QuerySessionPool is an experimental API, which could be changed." )
31
34
self ._driver = driver
32
-
33
- def checkout (self ) -> "SimpleQuerySessionCheckout" :
35
+ self ._queue = queue .PriorityQueue ()
36
+ self ._current_size = 0
37
+ self ._size = size
38
+ self ._should_stop = threading .Event ()
39
+ self ._lock = threading .RLock ()
40
+
41
+ def _create_new_session (self ):
42
+ session = QuerySessionSync (self ._driver )
43
+ session .create ()
44
+ logger .debug (f"New session was created for pool. Session id: { session ._state .session_id } " )
45
+ return session
46
+
47
+ def acquire (self , timeout : float ) -> QuerySessionSync :
48
+ with self ._lock :
49
+ if self ._should_stop .is_set ():
50
+ logger .error ("An attempt to take session from closed session pool." )
51
+ raise RuntimeError ("An attempt to take session from closed session pool." )
52
+
53
+ try :
54
+ _ , session = self ._queue .get_nowait ()
55
+ logger .debug (f"Acquired active session from queue: { session ._state .session_id } " )
56
+ return session if session ._state .attached else self ._create_new_session ()
57
+ except queue .Empty :
58
+ pass
59
+
60
+ if self ._current_size < self ._size :
61
+ logger .debug (f"Session pool is not large enough: { self ._current_size } < { self ._size } , will create new one." )
62
+ session = self ._create_new_session ()
63
+ self ._current_size += 1
64
+ return session
65
+
66
+ try :
67
+ _ , session = self ._queue .get (block = True , timeout = timeout )
68
+ return session if session ._state .attached else self ._create_new_session ()
69
+ except queue .Empty :
70
+ raise issues .SessionPoolEmpty ("Timeout on acquire session" )
71
+
72
+ def release (self , session : QuerySessionSync ) -> None :
73
+ with self ._lock :
74
+ self ._queue .put_nowait ((1 , session ))
75
+ logger .debug ("Session returned to queue: %s" , session ._state .session_id )
76
+
77
+ def checkout (self , timeout : float = 10 ) -> "SimpleQuerySessionCheckout" :
34
78
"""WARNING: This API is experimental and could be changed.
35
79
Return a Session context manager, that opens session on enter and closes session on exit.
36
80
"""
37
81
38
- return SimpleQuerySessionCheckout (self )
82
+ return SimpleQuerySessionCheckout (self , timeout )
39
83
40
84
def retry_operation_sync (self , callee : Callable , retry_settings : Optional [RetrySettings ] = None , * args , ** kwargs ):
41
85
"""WARNING: This API is experimental and could be changed.
@@ -85,7 +129,17 @@ def wrapped_callee():
85
129
return retry_operation_sync (wrapped_callee , retry_settings )
86
130
87
131
def stop (self , timeout = None ):
88
- pass # TODO: implement
132
+ with self ._lock :
133
+ self ._should_stop .set ()
134
+ while True :
135
+ try :
136
+ _ , session = self ._queue .get_nowait ()
137
+ session .delete ()
138
+ except queue .Empty :
139
+ break
140
+
141
+ logger .debug ("All session were deleted." )
142
+
89
143
90
144
def __enter__ (self ):
91
145
return self
@@ -95,13 +149,14 @@ def __exit__(self, exc_type, exc_val, exc_tb):
95
149
96
150
97
151
class SimpleQuerySessionCheckout :
98
- def __init__ (self , pool : QuerySessionPool ):
152
+ def __init__ (self , pool : QuerySessionPool , timeout : float ):
99
153
self ._pool = pool
100
- self ._session = QuerySessionSync (pool ._driver )
154
+ self ._timeout = timeout
155
+ self ._session = None
101
156
102
157
def __enter__ (self ) -> QuerySessionSync :
103
- self ._session . create ( )
158
+ self ._session = self . _pool . acquire ( self . _timeout )
104
159
return self ._session
105
160
106
161
def __exit__ (self , exc_type , exc_val , exc_tb ):
107
- self ._session . delete ( )
162
+ self ._pool . release ( self . _session )
0 commit comments