Skip to content

Commit c9fe9d3

Browse files
committed
added callInReadTxWithRetry
1 parent 5ea7cbb commit c9fe9d3

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

objectbox-java/src/main/java/io/objectbox/BoxStore.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,13 +596,48 @@ public void runInReadTx(Runnable runnable) {
596596
}
597597
}
598598

599+
@Experimental
600+
public <T> T callInReadTxWithRetry(Callable<T> callable, int attempts, int initialBackOffInMs, boolean logAndHeal) {
601+
if (attempts == 1) {
602+
return callInReadTx(callable);
603+
}
604+
long backoffInMs = initialBackOffInMs;
605+
DbException lastException = null;
606+
for (int attempt = 1; attempt <= attempts; attempt++) {
607+
try {
608+
return callInReadTx(callable);
609+
} catch (DbException e) {
610+
lastException = e;
611+
if (logAndHeal) {
612+
System.err.println(attempt + ". of " + attempts + " attempts of calling a read TX failed:");
613+
e.printStackTrace();
614+
System.err.println(diagnose());
615+
System.err.flush();
616+
617+
System.gc();
618+
System.runFinalization();
619+
cleanStaleReadTransactions();
620+
}
621+
try {
622+
Thread.sleep(backoffInMs);
623+
} catch (InterruptedException ie) {
624+
ie.printStackTrace();
625+
throw lastException;
626+
}
627+
backoffInMs *= 2;
628+
}
629+
}
630+
throw lastException;
631+
}
632+
599633
/**
600634
* Calls the given callable inside a read(-only) transaction. Multiple read transactions can occur at the same time.
601635
* This allows multiple read operations (gets) using a single consistent state of data.
602636
* Also, for a high number of read operations (thousands, e.g. in loops),
603637
* it is advised to run them in a single read transaction for efficiency reasons.
604638
* Note that any exception thrown by the given Callable will be wrapped in a RuntimeException.
605639
*/
640+
606641
public <T> T callInReadTx(Callable<T> callable) {
607642
Transaction tx = this.activeTx.get();
608643
// Only if not already set, allowing to call it recursively with first (outer) TX
@@ -611,6 +646,8 @@ public <T> T callInReadTx(Callable<T> callable) {
611646
activeTx.set(tx);
612647
try {
613648
return callable.call();
649+
} catch (DbException e) {
650+
throw e;
614651
} catch (Exception e) {
615652
throw new RuntimeException("Callable threw exception", e);
616653
} finally {

tests/objectbox-java-test/src/main/java/io/objectbox/BoxStoreTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.junit.Test;
2020

2121
import java.io.File;
22+
import java.util.concurrent.Callable;
2223

2324
import io.objectbox.exception.DbException;
2425

@@ -144,4 +145,31 @@ private void closeStoreForTest() {
144145
assertFalse(boxStoreDir.exists());
145146
}
146147

148+
@Test
149+
public void testCallInReadTxWithRetry() {
150+
final int[] countHolder = {0};
151+
String value = store.callInReadTxWithRetry(createTestCallable(countHolder), 5, 0, true);
152+
assertEquals("42", value);
153+
assertEquals(5, countHolder[0]);
154+
}
155+
156+
@Test(expected = DbException.class)
157+
public void testCallInReadTxWithRetry_fail() {
158+
final int[] countHolder = {0};
159+
store.callInReadTxWithRetry(createTestCallable(countHolder), 4, 0, true);
160+
}
161+
162+
private Callable<String> createTestCallable(final int[] countHolder) {
163+
return new Callable<String>() {
164+
@Override
165+
public String call() throws Exception {
166+
int count = ++countHolder[0];
167+
if (count < 5) {
168+
throw new DbException("Count: " + count);
169+
}
170+
return "42";
171+
}
172+
};
173+
}
174+
147175
}

0 commit comments

Comments
 (0)