File tree Expand file tree Collapse file tree 1 file changed +37
-0
lines changed
lib/public/AppFramework/Db Expand file tree Collapse file tree 1 file changed +37
-0
lines changed Original file line number Diff line number Diff line change 2525
2626namespace OCP \AppFramework \Db ;
2727
28+ use OC \DB \Exceptions \DbalException ;
2829use OCP \DB \Exception ;
2930use OCP \IDBConnection ;
3031use Throwable ;
32+ use function OCP \Log \logger ;
3133
3234/**
3335 * Helper trait for transactional operations
@@ -66,4 +68,39 @@ protected function atomic(callable $fn, IDBConnection $db) {
6668 throw $ e ;
6769 }
6870 }
71+
72+ /**
73+ * Wrapper around atomic() to retry after a retryable exception occurred
74+ *
75+ * Certain transactions might need to be retried. This is especially useful
76+ * in highly concurrent requests where a deadlocks is thrown by the database
77+ * without waiting for the lock to be freed (e.g. due to MySQL/MariaDB deadlock
78+ * detection)
79+ *
80+ * @template T
81+ * @param callable $fn
82+ * @psalm-param callable():T $fn
83+ * @param IDBConnection $db
84+ * @param int $maxRetires
85+ *
86+ * @return mixed the result of the passed callable
87+ * @psalm-return T
88+ *
89+ * @throws Exception for possible errors of commit or rollback or the custom operations within the closure
90+ * @throws Throwable any other error caused by the closure
91+ *
92+ * @since 24.0.0
93+ */
94+ protected function atomicRetry (callable $ fn , IDBConnection $ db , int $ maxRetires = 3 ): mixed {
95+ for ($ i = 0 ; $ i < $ maxRetires ; $ i ++) {
96+ try {
97+ return $ this ->atomic ($ fn , $ db );
98+ } catch (DbalException $ e ) {
99+ if (!$ e ->isRetryable () || $ i === ($ maxRetires - 1 )) {
100+ throw $ e ;
101+ }
102+ logger ('core ' )->warning ('Retrying operation after retryable exception. ' , [ 'exception ' => $ e ]);
103+ }
104+ }
105+ }
69106}
You can’t perform that action at this time.
0 commit comments