1313
1414namespace Laudis \Neo4j ;
1515
16+ use Bolt \error \ConnectException ;
1617use Laudis \Neo4j \Common \DriverSetupManager ;
1718use Laudis \Neo4j \Contracts \ClientInterface ;
1819use Laudis \Neo4j \Contracts \DriverInterface ;
2324use Laudis \Neo4j \Databags \Statement ;
2425use Laudis \Neo4j \Databags \SummarizedResult ;
2526use Laudis \Neo4j \Databags \TransactionConfiguration ;
26- use Laudis \Neo4j \Enum \ AccessMode ;
27+ use Laudis \Neo4j \Exception \ ConnectionPoolException ;
2728use Laudis \Neo4j \Types \CypherList ;
2829
2930/**
@@ -100,22 +101,91 @@ private function getSession(?string $alias = null): SessionInterface
100101 return $ this ->boundSessions [$ alias ] = $ this ->startSession ($ alias , $ this ->defaultSessionConfiguration );
101102 }
102103
104+ /**
105+ * Executes an operation with automatic retry on alternative drivers when connection exceptions occur.
106+ *
107+ * @template T
108+ *
109+ * @param callable(SessionInterface): T $operation The operation to execute
110+ * @param string|null $alias The driver alias to use
111+ *
112+ * @throws ConnectionPoolException When all available drivers have been exhausted
113+ *
114+ * @return T The result of the operation
115+ */
116+ private function executeWithRetry (callable $ operation , ?string $ alias = null , ?int $ maxTries = 3 )
117+ {
118+ $ alias ??= $ this ->driverSetups ->getDefaultAlias ();
119+ $ attemptedDrivers = [];
120+ $ lastException = null ;
121+
122+ $ tries = $ maxTries ;
123+
124+ while ($ tries > 0 ) {
125+ try {
126+ $ driver = $ this ->driverSetups ->getDriver ($ this ->defaultSessionConfiguration , $ alias );
127+
128+ $ driverHash = spl_object_hash ($ driver );
129+ if (in_array ($ driverHash , $ attemptedDrivers , true )) {
130+ throw $ lastException ?? new ConnectionPoolException ('No available drivers ' );
131+ }
132+ $ attemptedDrivers [] = $ driverHash ;
133+
134+ $ session = $ driver ->createSession ($ this ->defaultSessionConfiguration );
135+
136+ return $ operation ($ session );
137+ } catch (ConnectionPoolException |ConnectException $ e ) {
138+ $ lastException = $ e ;
139+ $ this ->driverSetups ->resetDriver ($ alias );
140+
141+ --$ tries ;
142+ }
143+ }
144+
145+ throw $ lastException ?? new ConnectionPoolException ('No available drivers ' );
146+ }
147+
103148 public function runStatements (iterable $ statements , ?string $ alias = null ): CypherList
104149 {
105- $ runner = $ this ->getRunner ($ alias );
106- if ($ runner instanceof SessionInterface) {
107- return $ runner ->runStatements ($ statements , $ this ->defaultTransactionConfiguration );
150+ $ alias ??= $ this ->driverSetups ->getDefaultAlias ();
151+
152+ if (array_key_exists ($ alias , $ this ->boundTransactions )
153+ && count ($ this ->boundTransactions [$ alias ]) > 0 ) {
154+ $ runner = $ this ->getRunner ($ alias );
155+ if ($ runner instanceof TransactionInterface) {
156+ return $ runner ->runStatements ($ statements );
157+ }
158+ }
159+
160+ if (array_key_exists ($ alias , $ this ->boundSessions )) {
161+ $ session = $ this ->boundSessions [$ alias ];
162+
163+ return $ session ->runStatements ($ statements , $ this ->defaultTransactionConfiguration );
108164 }
109165
110- return $ runner ->runStatements ($ statements );
166+ return $ this ->executeWithRetry (
167+ function (SessionInterface $ session ) use ($ statements ) {
168+ return $ session ->runStatements ($ statements , $ this ->defaultTransactionConfiguration );
169+ },
170+ $ alias
171+ );
111172 }
112173
113174 public function beginTransaction (?iterable $ statements = null , ?string $ alias = null , ?TransactionConfiguration $ config = null ): UnmanagedTransactionInterface
114175 {
115- $ session = $ this ->getSession ( $ alias );
176+ $ alias ?? = $ this ->driverSetups -> getDefaultAlias ( );
116177 $ config = $ this ->getTsxConfig ($ config );
117178
118- return $ session ->beginTransaction ($ statements , $ config );
179+ if (array_key_exists ($ alias , $ this ->boundSessions )) {
180+ return $ this ->boundSessions [$ alias ]->beginTransaction ($ statements , $ config );
181+ }
182+
183+ return $ this ->executeWithRetry (
184+ function (SessionInterface $ session ) use ($ statements , $ config ) {
185+ return $ session ->beginTransaction ($ statements , $ config );
186+ },
187+ $ alias
188+ );
119189 }
120190
121191 public function getDriver (?string $ alias ): DriverInterface
@@ -130,27 +200,36 @@ private function startSession(?string $alias, SessionConfiguration $configuratio
130200
131201 public function writeTransaction (callable $ tsxHandler , ?string $ alias = null , ?TransactionConfiguration $ config = null )
132202 {
133- $ accessMode = $ this ->defaultSessionConfiguration ->getAccessMode ();
134- if ($ accessMode === null || $ accessMode === AccessMode::WRITE ()) {
135- $ session = $ this ->getSession ($ alias );
136- } else {
137- $ sessionConfig = $ this ->defaultSessionConfiguration ->withAccessMode (AccessMode::WRITE ());
138- $ session = $ this ->startSession ($ alias , $ sessionConfig );
203+ $ alias ??= $ this ->driverSetups ->getDefaultAlias ();
204+ $ config = $ this ->getTsxConfig ($ config );
205+
206+ if (array_key_exists ($ alias , $ this ->boundSessions )) {
207+ return $ this ->boundSessions [$ alias ]->writeTransaction ($ tsxHandler , $ config );
139208 }
140209
141- return $ session ->writeTransaction ($ tsxHandler , $ this ->getTsxConfig ($ config ));
210+ return $ this ->executeWithRetry (
211+ function (SessionInterface $ session ) use ($ tsxHandler , $ config ) {
212+ return $ session ->writeTransaction ($ tsxHandler , $ config );
213+ },
214+ $ alias
215+ );
142216 }
143217
144218 public function readTransaction (callable $ tsxHandler , ?string $ alias = null , ?TransactionConfiguration $ config = null )
145219 {
146- if ( $ this ->defaultSessionConfiguration -> getAccessMode () === AccessMode:: READ ()) {
147- $ session = $ this ->getSession ( $ alias );
148- } else {
149- $ sessionConfig = $ this ->defaultSessionConfiguration -> withAccessMode (AccessMode:: WRITE ());
150- $ session = $ this ->startSession ( $ alias , $ sessionConfig );
220+ $ alias ??= $ this ->driverSetups -> getDefaultAlias ();
221+ $ config = $ this ->getTsxConfig ( $ config );
222+
223+ if ( array_key_exists ( $ alias , $ this ->boundSessions )) {
224+ return $ this ->boundSessions [ $ alias]-> readTransaction ( $ tsxHandler , $ config );
151225 }
152226
153- return $ session ->readTransaction ($ tsxHandler , $ this ->getTsxConfig ($ config ));
227+ return $ this ->executeWithRetry (
228+ function (SessionInterface $ session ) use ($ tsxHandler , $ config ) {
229+ return $ session ->readTransaction ($ tsxHandler , $ config );
230+ },
231+ $ alias
232+ );
154233 }
155234
156235 public function transaction (callable $ tsxHandler , ?string $ alias = null , ?TransactionConfiguration $ config = null )
0 commit comments