Skip to content

Commit

Permalink
new-tx-i: new Tx interface pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Lavrukov committed Feb 15, 2024
1 parent 51c3007 commit 17976c0
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Test;
Expand Down Expand Up @@ -288,13 +291,45 @@ public void deferFinallyRollbackRetryable() {

@Test
public void deferFinallyNotInTxContext() {
db.tx(() -> Tx.Current.get().deferFinally(() -> assertFalse(Tx.Current.exists())));
db.txC(tx -> tx.deferFinally(() -> assertFalse(Tx.Current.exists())));
}

@Test
public void myTest() {
db.txC(tx -> {
// new. Usefull because you see that table depends on tx;
Db1.project(tx).findAll();

// new. Usefull because you can initialize db one time in tx and use call table without pass tx any time
Db2.of(tx).project().findAll();

// old
db.projects().findAll();
});
}

// Examples of new DB patterns. User can use one or both;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
final static class Db1 {
public static TestEntityOperations.ProjectTable project(Tx tx) {
return new TestEntityOperations.ProjectTable(tx.table(Project.class));
}
}

@RequiredArgsConstructor(staticName = "of")
final static class Db2 {
private final Tx tx;

TestEntityOperations.ProjectTable project() {
return new TestEntityOperations.ProjectTable(tx.table(Project.class));
}
}

@Test
public void deferFinallyRollbackNotInTxContext() {
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> db.tx(() -> {
Tx.Current.get().deferFinally(() -> assertFalse(Tx.Current.exists()));
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> db.txC(tx -> {
tx.deferFinally(() -> assertFalse(Tx.Current.exists()));
throw new RuntimeException();
}));
}
Expand Down
4 changes: 4 additions & 0 deletions repository/src/main/java/tech/ydb/yoj/repository/db/Tx.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import java.util.function.Supplier;

public interface Tx {
default <T extends Entity<T>> Table<T> table(Class<T> cls) {
return getRepositoryTransaction().table(cls);
}

void defer(Runnable runnable);

void deferFinally(Runnable runnable);
Expand Down
19 changes: 17 additions & 2 deletions repository/src/main/java/tech/ydb/yoj/repository/db/TxManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import tech.ydb.yoj.repository.db.exception.RetryableException;

import java.time.Duration;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public interface TxManager {
Expand Down Expand Up @@ -137,7 +139,9 @@ default TxManager noLogging() {
* @param supplier action to perform
* @return action result
*/
<T> T tx(Supplier<T> supplier);
default <T> T tx(Supplier<T> supplier) {
return txF(tx -> supplier.get());
}

/**
* Performs the specified action inside a transaction. The action must be idempotent, because it might be executed
Expand All @@ -146,7 +150,18 @@ default TxManager noLogging() {
*
* @param runnable action to perform
*/
void tx(Runnable runnable);
default void tx(Runnable runnable) {
txC(tx -> runnable.run());
}

<T> T txF(Function<Tx, T> supplier);

default void txC(Consumer<Tx> consumer) {
txF(tx -> {
consumer.accept(tx);
return null;
});
}

/**
* Start a transaction-like session of read-only statements. Each statement will be executed <em>separately</em>,
Expand Down

0 comments on commit 17976c0

Please sign in to comment.