Skip to content

Commit

Permalink
Added ydb-token-app as example of Spring+Hibernate v5
Browse files Browse the repository at this point in the history
  • Loading branch information
alex268 committed Nov 20, 2024
1 parent 7e5d50d commit b935418
Show file tree
Hide file tree
Showing 13 changed files with 756 additions and 0 deletions.
1 change: 1 addition & 0 deletions jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<module>basic-example</module>
<module>failsafe-example</module>
<module>spring-data-jpa-v5</module>
<module>ydb-token-app</module>
</modules>

<dependencyManagement>
Expand Down
1 change: 1 addition & 0 deletions jdbc/spring-jooq/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.3</version>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.jooq</groupId>-->
Expand Down
62 changes: 62 additions & 0 deletions jdbc/ydb-token-app/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>tech.ydb.jdbc.examples</groupId>
<artifactId>ydb-jdbc-examples</artifactId>
<version>1.1.0-SNAPSHOT</version>
</parent>

<groupId>tech.ydb.apps</groupId>
<artifactId>ydb-token-app</artifactId>

<name>YDB Token application</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>

<spring.boot.version>2.7.18</spring.boot.version>
<ydb.hibernate.dialect.version>0.9.3</ydb.hibernate.dialect.version>

<exec.mainClass>tech.ydb.apps.Application</exec.mainClass>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>

<dependency>
<groupId>tech.ydb.jdbc</groupId>
<artifactId>ydb-jdbc-driver</artifactId>
</dependency>
<dependency>
<groupId>tech.ydb.dialects</groupId>
<artifactId>hibernate-ydb-dialect-v5</artifactId>
<version>${ydb.hibernate.dialect.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
</plugins>
</build>
</project>
217 changes: 217 additions & 0 deletions jdbc/ydb-token-app/src/main/java/tech/ydb/apps/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package tech.ydb.apps;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.annotation.EnableRetry;

import tech.ydb.apps.service.SchemeService;
import tech.ydb.apps.service.TokenService;
import tech.ydb.jdbc.YdbTracer;

/**
*
* @author Aleksandr Gorshenin
*/
@EnableRetry
@SpringBootApplication
public class Application implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(Application.class);

private static final int THREADS_COUNT = 32;
private static final int RECORDS_COUNT = 1_000_000;
private static final int LOAD_BATCH_SIZE = 1000;

private static final int WORKLOAD_DURATION_SECS = 60; // 60 seconds

public static void main(String[] args) {
SpringApplication.run(Application.class, args).close();
}

private final Ticker ticker = new Ticker(logger);

private final SchemeService schemeService;
private final TokenService tokenService;

private final ExecutorService executor;
private final AtomicInteger threadCounter = new AtomicInteger(0);
private final AtomicInteger executionsCount = new AtomicInteger(0);
private final AtomicInteger retriesCount = new AtomicInteger(0);

public Application(SchemeService schemeService, TokenService tokenService) {
this.schemeService = schemeService;
this.tokenService = tokenService;

this.executor = Executors.newFixedThreadPool(THREADS_COUNT, this::threadFactory);
}

@PreDestroy
public void close() throws Exception {
logger.info("CLI app is waiting for finishing");

executor.shutdown();
executor.awaitTermination(5, TimeUnit.MINUTES);

ticker.printTotal();
ticker.close();

logger.info("Executed {} transactions with {} retries", executionsCount.get(), retriesCount.get());
logger.info("CLI app has finished");
}

@Bean
public RetryListener retryListener() {
return new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext ctx, RetryCallback<T, E> callback) {
executionsCount.incrementAndGet();
return true;
}

@Override
public <T, E extends Throwable> void onError(RetryContext ctx, RetryCallback<T, E> callback, Throwable th) {
logger.debug("Retry operation with error {} ", printSqlException(th));
retriesCount.incrementAndGet();
}
};
}

private String printSqlException(Throwable th) {
Throwable ex = th;
while (ex != null) {
if (ex instanceof SQLException) {
return ex.getMessage();
}
ex = ex.getCause();
}
return th.getMessage();
}

@Override
public void run(String... args) {
logger.info("CLI app has started");

for (String arg : args) {
logger.info("execute {} step", arg);

if ("clean".equalsIgnoreCase(arg)) {
schemeService.executeClean();
}

if ("init".equalsIgnoreCase(arg)) {
schemeService.executeInit();
}

if ("load".equalsIgnoreCase(arg)) {
ticker.runWithMonitor(this::loadData);
}

if ("run".equalsIgnoreCase(arg)) {
ticker.runWithMonitor(this::runWorkloads);
}

if ("test".equalsIgnoreCase(arg)) {
ticker.runWithMonitor(this::test);
}
}
}

private Thread threadFactory(Runnable runnable) {
return new Thread(runnable, "app-thread-" + threadCounter.incrementAndGet());
}

private void loadData() {
List<CompletableFuture<?>> futures = new ArrayList<>();
int id = 0;
while (id < RECORDS_COUNT) {
final int first = id;
id += LOAD_BATCH_SIZE;
final int last = id < RECORDS_COUNT ? id : RECORDS_COUNT;

futures.add(CompletableFuture.runAsync(() -> {
try (Ticker.Measure measure = ticker.getLoad().newCall()) {
tokenService.insertBatch(first, last);
logger.info("inserted tokens [{}, {})", first, last);
measure.inc();
}
}, executor));
}

futures.forEach(CompletableFuture::join);
}

private void test() {
YdbTracer.current().markToPrint("test");

final Random rnd = new Random();
List<Integer> randomIds = IntStream.range(0, 100)
.mapToObj(idx -> rnd.nextInt(RECORDS_COUNT))
.collect(Collectors.toList());

tokenService.updateBatch(randomIds);
}

private void runWorkloads() {
long finishAt = System.currentTimeMillis() + WORKLOAD_DURATION_SECS * 1000;
List<CompletableFuture<?>> futures = new ArrayList<>();
for (int i = 0; i < THREADS_COUNT; i++) {
futures.add(CompletableFuture.runAsync(() -> this.workload(finishAt), executor));
}

futures.forEach(CompletableFuture::join);
}

private void workload(long finishAt) {
final Random rnd = new Random();
while (System.currentTimeMillis() < finishAt) {
int mode = rnd.nextInt(10);

try {
if (mode < 2) {
try (Ticker.Measure measure = ticker.getBatchUpdate().newCall()) {
List<Integer> randomIds = IntStream.range(0, 100)
.mapToObj(idx -> rnd.nextInt(RECORDS_COUNT))
.collect(Collectors.toList());
tokenService.updateBatch(randomIds);
measure.inc();
}

} else if (mode < 6) {
int id = rnd.nextInt(RECORDS_COUNT);
try (Ticker.Measure measure = ticker.getFetch().newCall()) {
tokenService.fetchToken(id);
measure.inc();
}
} else {
int id = rnd.nextInt(RECORDS_COUNT);
try (Ticker.Measure measure = ticker.getUpdate().newCall()) {
tokenService.updateToken(id);
measure.inc();
}
}
} catch (RuntimeException ex) {
logger.debug("got exception {}", ex.getMessage());
}
}
}
}
Loading

0 comments on commit b935418

Please sign in to comment.