24
24
use Laudis \Neo4j \Databags \DriverConfiguration ;
25
25
use Laudis \Neo4j \Databags \SessionConfiguration ;
26
26
27
+ use Laudis \Neo4j \Exception \ConnectionPoolException ;
27
28
use function method_exists ;
28
29
use function microtime ;
29
30
@@ -44,6 +45,7 @@ public function __construct(
44
45
private readonly BoltFactory $ factory ,
45
46
private readonly ConnectionRequestData $ data ,
46
47
private readonly ?Neo4jLogger $ logger ,
48
+ private readonly float $ acquireConnectionTimeout
47
49
) {
48
50
}
49
51
@@ -63,33 +65,39 @@ public static function create(
63
65
$ conf ->getUserAgent (),
64
66
$ conf ->getSslConfiguration ()
65
67
),
66
- $ conf ->getLogger ()
68
+ $ conf ->getLogger (),
69
+ $ conf ->getAcquireConnectionTimeout ()
67
70
);
68
71
}
69
72
70
73
public function acquire (SessionConfiguration $ config ): Generator
71
74
{
72
- $ generator = $ this ->semaphore ->wait ();
73
- $ start = microtime (true );
75
+ return (function () use ($ config ) {
76
+ $ connection = $ this ->reuseConnectionIfPossible ($ config );
77
+ if ($ connection !== null ) {
78
+ return $ connection ;
79
+ }
74
80
75
- return ( function () use ( $ generator, $ start , $ config ) {
81
+ $ generator = $ this -> semaphore -> wait ();
76
82
// If the generator is valid, it means we are waiting to acquire a new connection.
77
83
// This means we can use this time to check if we can reuse a connection or should throw a timeout exception.
78
84
while ($ generator ->valid ()) {
79
- /** @var bool $continue */
80
- $ continue = yield microtime (true ) - $ start ;
81
- $ generator ->send ($ continue );
82
- if ($ continue === false ) {
83
- return null ;
84
- }
85
+ $ waitTime = $ generator ->current ();
86
+ if ($ waitTime <= $ this ->acquireConnectionTimeout ) {
87
+ yield $ waitTime ;
88
+
89
+ $ connection = $ this ->reuseConnectionIfPossible ($ config );
90
+ if ($ connection !== null ) {
91
+ return $ connection ;
92
+ }
85
93
86
- $ connection = $ this -> returnAnyAvailableConnection ( $ config );
87
- if ( $ connection !== null ) {
88
- return $ connection ;
94
+ $ generator -> next ( );
95
+ } else {
96
+ throw new ConnectionPoolException ( ' Connection acquire timeout reached: ' . $ waitTime ) ;
89
97
}
90
98
}
91
99
92
- $ connection = $ this ->returnAnyAvailableConnection ($ config );
100
+ $ connection = $ this ->reuseConnectionIfPossible ($ config );
93
101
if ($ connection !== null ) {
94
102
return $ connection ;
95
103
}
@@ -119,53 +127,15 @@ public function getLogger(): ?Neo4jLogger
119
127
return $ this ->logger ;
120
128
}
121
129
122
- /**
123
- * @return BoltConnection|null
124
- */
125
- private function returnAnyAvailableConnection (SessionConfiguration $ config ): ?ConnectionInterface
130
+ private function reuseConnectionIfPossible (SessionConfiguration $ config ): ?ConnectionInterface
126
131
{
127
- $ streamingConnection = null ;
128
- $ requiresReconnectConnection = null ;
129
132
// Ensure random connection reuse before picking one.
130
133
shuffle ($ this ->activeConnections );
131
-
132
134
foreach ($ this ->activeConnections as $ activeConnection ) {
133
135
// We prefer a connection that is just ready
134
- if ($ activeConnection ->getServerState () === 'READY ' ) {
135
- if ($ this ->factory ->canReuseConnection ($ activeConnection , $ this ->data , $ config )) {
136
- return $ this ->factory ->reuseConnection ($ activeConnection , $ config );
137
- } else {
138
- $ requiresReconnectConnection = $ activeConnection ;
139
- }
136
+ if ($ activeConnection ->getServerState () === 'READY ' && $ this ->factory ->canReuseConnection ($ activeConnection , $ config )) {
137
+ return $ this ->factory ->reuseConnection ($ activeConnection , $ config );
140
138
}
141
-
142
- // We will store any streaming connections, so we can use that one
143
- // as we can force the subscribed result sets to consume the results
144
- // and become ready again.
145
- // This code will make sure we never get stuck if the user has many
146
- // results open that aren't consumed yet.
147
- // https://github.com/neo4j-php/neo4j-php-client/issues/146
148
- // NOTE: we cannot work with TX_STREAMING as we cannot force the transaction to implicitly close.
149
- if ($ streamingConnection === null && $ activeConnection ->getServerState () === 'STREAMING ' ) {
150
- if ($ this ->factory ->canReuseConnection ($ activeConnection , $ this ->data , $ config )) {
151
- $ streamingConnection = $ activeConnection ;
152
- if (method_exists ($ streamingConnection , 'consumeResults ' )) {
153
- $ streamingConnection ->consumeResults (); // State should now be ready
154
- }
155
- } else {
156
- $ requiresReconnectConnection = $ activeConnection ;
157
- }
158
- }
159
- }
160
-
161
- if ($ streamingConnection ) {
162
- return $ this ->factory ->reuseConnection ($ streamingConnection , $ config );
163
- }
164
-
165
- if ($ requiresReconnectConnection ) {
166
- $ this ->release ($ requiresReconnectConnection );
167
-
168
- return $ this ->factory ->createConnection ($ this ->data , $ config );
169
139
}
170
140
171
141
return null ;
0 commit comments