Skip to content

Commit ab3eaf1

Browse files
author
Alexander Lavrukov
committed
new-tx-i: new Tx interface pattern
1 parent 51c3007 commit ab3eaf1

File tree

5 files changed

+71
-18
lines changed

5 files changed

+71
-18
lines changed

Diff for: repository-test/src/main/java/tech/ydb/yoj/repository/test/RepositoryTest.java

+38-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import com.google.common.collect.ImmutableSet;
44
import com.google.common.collect.Iterators;
5+
import lombok.AccessLevel;
6+
import lombok.AllArgsConstructor;
7+
import lombok.RequiredArgsConstructor;
58
import org.assertj.core.api.Assertions;
69
import org.junit.Assert;
710
import org.junit.Test;
@@ -288,13 +291,45 @@ public void deferFinallyRollbackRetryable() {
288291

289292
@Test
290293
public void deferFinallyNotInTxContext() {
291-
db.tx(() -> Tx.Current.get().deferFinally(() -> assertFalse(Tx.Current.exists())));
294+
db.txC(tx -> tx.deferFinally(() -> assertFalse(Tx.Current.exists())));
295+
}
296+
297+
@Test
298+
public void myTest() {
299+
db.txC(tx -> {
300+
// new. Usefull because you see that table depends on tx;
301+
Db1.project(tx).findAll();
302+
303+
// new. Usefull because you can initialize db one time in tx and use call table without pass tx any time
304+
Db2.of(tx).project().findAll();
305+
306+
// old
307+
db.projects().findAll();
308+
});
309+
}
310+
311+
// Examples of new DB patterns. User can use one or both;
312+
313+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
314+
final static class Db1 {
315+
public static TestEntityOperations.ProjectTable project(Tx tx) {
316+
return new TestEntityOperations.ProjectTable(tx.table(Project.class));
317+
}
318+
}
319+
320+
@RequiredArgsConstructor(staticName = "of")
321+
final static class Db2 {
322+
private final Tx tx;
323+
324+
TestEntityOperations.ProjectTable project() {
325+
return new TestEntityOperations.ProjectTable(tx.table(Project.class));
326+
}
292327
}
293328

294329
@Test
295330
public void deferFinallyRollbackNotInTxContext() {
296-
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> db.tx(() -> {
297-
Tx.Current.get().deferFinally(() -> assertFalse(Tx.Current.exists()));
331+
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> db.txC(tx -> {
332+
tx.deferFinally(() -> assertFalse(Tx.Current.exists()));
298333
throw new RuntimeException();
299334
}));
300335
}

Diff for: repository/src/main/java/tech/ydb/yoj/repository/db/StdTxManager.java

+13-10
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.time.Duration;
2323
import java.util.Set;
2424
import java.util.concurrent.atomic.AtomicLong;
25+
import java.util.function.Function;
2526
import java.util.function.Supplier;
2627
import java.util.regex.Pattern;
2728

@@ -181,23 +182,25 @@ public ScanBuilder scan() {
181182

182183
@Override
183184
public void tx(Runnable runnable) {
184-
tx(() -> {
185-
runnable.run();
186-
return null;
187-
});
185+
txC(__ -> runnable.run());
188186
}
189187

190188
@Override
191189
public <T> T tx(Supplier<T> supplier) {
190+
return tx(__ -> supplier.get());
191+
}
192+
193+
@Override
194+
public <T> T tx(Function<Tx, T> func) {
192195
if (name == null) {
193-
return withGeneratedNameAndLine().tx(supplier);
196+
return withGeneratedNameAndLine().tx(func);
194197
}
195198

196199
checkSeparatePolicy(separatePolicy, name);
197-
return txImpl(supplier);
200+
return txImpl(func);
198201
}
199202

200-
private <T> T txImpl(Supplier<T> supplier) {
203+
private <T> T txImpl(Function<Tx, T> func) {
201204
RetryableException lastRetryableException = null;
202205
TxImpl lastTx = null;
203206
try (Timer ignored = totalDuration.labels(name).startTimer()) {
@@ -207,7 +210,7 @@ private <T> T txImpl(Supplier<T> supplier) {
207210
T result;
208211
try (var ignored1 = attemptDuration.labels(name).startTimer()) {
209212
lastTx = new TxImpl(name, repository.startTransaction(options), options);
210-
result = runAttempt(supplier, lastTx);
213+
result = runAttempt(lastTx, func);
211214
}
212215

213216
if (options.isDryRun()) {
@@ -258,11 +261,11 @@ private String getExceptionNameForMetric(RetryableException e) {
258261
return Strings.removeSuffix(e.getClass().getSimpleName(), "Exception");
259262
}
260263

261-
private <T> T runAttempt(Supplier<T> supplier, TxImpl tx) {
264+
private <T> T runAttempt(TxImpl tx, Function<Tx, T> func) {
262265
try (var ignored2 = MDC.putCloseable("tx", formatTx());
263266
var ignored3 = MDC.putCloseable("tx-id", formatTxId());
264267
var ignored4 = MDC.putCloseable("tx-name", formatTxName(false))) {
265-
return tx.run(supplier);
268+
return tx.run(func);
266269
}
267270
}
268271

Diff for: repository/src/main/java/tech/ydb/yoj/repository/db/Tx.java

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import java.util.function.Supplier;
77

88
public interface Tx {
9+
default <T extends Entity<T>> Table<T> table(Class<T> cls) {
10+
return getRepositoryTransaction().table(cls);
11+
}
12+
913
void defer(Runnable runnable);
1014

1115
void deferFinally(Runnable runnable);

Diff for: repository/src/main/java/tech/ydb/yoj/repository/db/TxImpl.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import java.util.ArrayList;
1111
import java.util.List;
12-
import java.util.function.Supplier;
12+
import java.util.function.Function;
1313

1414
final class TxImpl implements Tx {
1515
private static final Logger log = LoggerFactory.getLogger(TxImpl.class);
@@ -32,10 +32,10 @@ public TxImpl(String name, RepositoryTransaction repositoryTransaction, TxOption
3232
this.logStatementOnSuccess = options.isLogStatementOnSuccess();
3333
}
3434

35-
<R> R run(Supplier<R> supplier) {
35+
<R> R run(Function<Tx, R> func) {
3636
R value;
3737
try {
38-
value = Current.runInTx(this, () -> runImpl(supplier));
38+
value = Current.runInTx(this, () -> runImpl(func));
3939
} catch (Exception e) {
4040
if (Interrupts.isInterruptException(e)) {
4141
Thread.currentThread().interrupt();
@@ -72,11 +72,11 @@ public void deferBeforeCommit(Runnable runnable) {
7272
deferredBeforeCommit.add(runnable);
7373
}
7474

75-
private <R> R runImpl(Supplier<R> supplier) {
75+
private <R> R runImpl(Function<Tx, R> func) {
7676
Stopwatch sw = Stopwatch.createStarted();
7777
R res;
7878
try {
79-
res = supplier.get();
79+
res = func.apply(this);
8080
deferredBeforeCommit.forEach(Runnable::run);
8181
} catch (Throwable t) {
8282
doRollback(isBusinessException(t),

Diff for: repository/src/main/java/tech/ydb/yoj/repository/db/TxManager.java

+11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import tech.ydb.yoj.repository.db.exception.RetryableException;
88

99
import java.time.Duration;
10+
import java.util.function.Consumer;
11+
import java.util.function.Function;
1012
import java.util.function.Supplier;
1113

1214
public interface TxManager {
@@ -148,6 +150,15 @@ default TxManager noLogging() {
148150
*/
149151
void tx(Runnable runnable);
150152

153+
<T> T tx(Function<Tx, T> supplier);
154+
155+
default void txC(Consumer<Tx> consumer) {
156+
tx(tx -> {
157+
consumer.accept(tx);
158+
return null;
159+
});
160+
}
161+
151162
/**
152163
* Start a transaction-like session of read-only statements. Each statement will be executed <em>separately</em>,
153164
* with the specified isolation level (online consistent read-only, by default).

0 commit comments

Comments
 (0)