2424use Laudis \Neo4j \Databags \DriverConfiguration ;
2525use Laudis \Neo4j \Databags \SessionConfiguration ;
2626
27+ use Laudis \Neo4j \Exception \ConnectionPoolException ;
2728use function method_exists ;
2829use function microtime ;
2930
@@ -44,6 +45,7 @@ public function __construct(
4445 private readonly BoltFactory $ factory ,
4546 private readonly ConnectionRequestData $ data ,
4647 private readonly ?Neo4jLogger $ logger ,
48+ private readonly float $ acquireConnectionTimeout
4749 ) {
4850 }
4951
@@ -63,33 +65,39 @@ public static function create(
6365 $ conf ->getUserAgent (),
6466 $ conf ->getSslConfiguration ()
6567 ),
66- $ conf ->getLogger ()
68+ $ conf ->getLogger (),
69+ $ conf ->getAcquireConnectionTimeout ()
6770 );
6871 }
6972
7073 public function acquire (SessionConfiguration $ config ): Generator
7174 {
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+ }
7480
75- return ( function () use ( $ generator, $ start , $ config ) {
81+ $ generator = $ this -> semaphore -> wait ();
7682 // If the generator is valid, it means we are waiting to acquire a new connection.
7783 // This means we can use this time to check if we can reuse a connection or should throw a timeout exception.
7884 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+ }
8593
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 ) ;
8997 }
9098 }
9199
92- $ connection = $ this ->returnAnyAvailableConnection ($ config );
100+ $ connection = $ this ->reuseConnectionIfPossible ($ config );
93101 if ($ connection !== null ) {
94102 return $ connection ;
95103 }
@@ -119,53 +127,15 @@ public function getLogger(): ?Neo4jLogger
119127 return $ this ->logger ;
120128 }
121129
122- /**
123- * @return BoltConnection|null
124- */
125- private function returnAnyAvailableConnection (SessionConfiguration $ config ): ?ConnectionInterface
130+ private function reuseConnectionIfPossible (SessionConfiguration $ config ): ?ConnectionInterface
126131 {
127- $ streamingConnection = null ;
128- $ requiresReconnectConnection = null ;
129132 // Ensure random connection reuse before picking one.
130133 shuffle ($ this ->activeConnections );
131-
132134 foreach ($ this ->activeConnections as $ activeConnection ) {
133135 // 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 );
140138 }
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 );
169139 }
170140
171141 return null ;
0 commit comments