Skip to content

Commit 6e515e8

Browse files
authored
Merge pull request #46 from alex268/master
Producation ready example of Spring JPA application
2 parents 4be1703 + 6d579ca commit 6e515e8

File tree

13 files changed

+757
-1
lines changed

13 files changed

+757
-1
lines changed

jdbc/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<packaging>pom</packaging>
1616

1717
<properties>
18-
<ydb.jdbc.version>2.2.3</ydb.jdbc.version>
18+
<ydb.jdbc.version>2.3.5</ydb.jdbc.version>
1919
<slf4j.version>1.7.36</slf4j.version>
2020
</properties>
2121

@@ -53,6 +53,7 @@
5353
<module>shedlock</module>
5454
<module>spring-data-jdbc</module>
5555
<module>spring-jooq</module>
56+
<module>ydb-token-app</module>
5657
</modules>
5758
</profile>
5859
</profiles>

jdbc/spring-jooq/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
<plugin>
114114
<groupId>org.springframework.boot</groupId>
115115
<artifactId>spring-boot-maven-plugin</artifactId>
116+
<version>3.3.3</version>
116117
</plugin>
117118
<!-- <plugin>-->
118119
<!-- <groupId>org.jooq</groupId>-->

jdbc/ydb-token-app/pom.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<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">
2+
<modelVersion>4.0.0</modelVersion>
3+
4+
<parent>
5+
<groupId>tech.ydb.jdbc.examples</groupId>
6+
<artifactId>ydb-jdbc-examples</artifactId>
7+
<version>1.1.0-SNAPSHOT</version>
8+
</parent>
9+
10+
<groupId>tech.ydb.apps</groupId>
11+
<artifactId>ydb-token-app</artifactId>
12+
13+
<name>YDB Token application</name>
14+
15+
<properties>
16+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17+
<maven.compiler.release>17</maven.compiler.release>
18+
19+
<spring.boot.version>2.7.18</spring.boot.version>
20+
<ydb.hibernate.dialect.version>0.9.3</ydb.hibernate.dialect.version>
21+
22+
<exec.mainClass>tech.ydb.apps.Application</exec.mainClass>
23+
</properties>
24+
25+
<dependencies>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-data-jpa</artifactId>
29+
<version>${spring.boot.version}</version>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.springframework.retry</groupId>
33+
<artifactId>spring-retry</artifactId>
34+
<version>2.0.7</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>jakarta.xml.bind</groupId>
38+
<artifactId>jakarta.xml.bind-api</artifactId>
39+
<version>2.3.2</version>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>tech.ydb.jdbc</groupId>
44+
<artifactId>ydb-jdbc-driver</artifactId>
45+
</dependency>
46+
<dependency>
47+
<groupId>tech.ydb.dialects</groupId>
48+
<artifactId>hibernate-ydb-dialect-v5</artifactId>
49+
<version>${ydb.hibernate.dialect.version}</version>
50+
</dependency>
51+
</dependencies>
52+
53+
<build>
54+
<plugins>
55+
<plugin>
56+
<groupId>org.springframework.boot</groupId>
57+
<artifactId>spring-boot-maven-plugin</artifactId>
58+
<version>${spring.boot.version}</version>
59+
</plugin>
60+
</plugins>
61+
</build>
62+
</project>
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package tech.ydb.apps;
2+
3+
import java.sql.SQLException;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.Random;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.concurrent.ExecutorService;
9+
import java.util.concurrent.Executors;
10+
import java.util.concurrent.TimeUnit;
11+
import java.util.concurrent.atomic.AtomicInteger;
12+
import java.util.stream.Collectors;
13+
import java.util.stream.IntStream;
14+
15+
import javax.annotation.PreDestroy;
16+
17+
import org.slf4j.Logger;
18+
import org.slf4j.LoggerFactory;
19+
import org.springframework.boot.CommandLineRunner;
20+
import org.springframework.boot.SpringApplication;
21+
import org.springframework.boot.autoconfigure.SpringBootApplication;
22+
import org.springframework.context.annotation.Bean;
23+
import org.springframework.retry.RetryCallback;
24+
import org.springframework.retry.RetryContext;
25+
import org.springframework.retry.RetryListener;
26+
import org.springframework.retry.annotation.EnableRetry;
27+
28+
import tech.ydb.apps.service.SchemeService;
29+
import tech.ydb.apps.service.TokenService;
30+
import tech.ydb.jdbc.YdbTracer;
31+
32+
/**
33+
*
34+
* @author Aleksandr Gorshenin
35+
*/
36+
@EnableRetry
37+
@SpringBootApplication
38+
public class Application implements CommandLineRunner {
39+
private static final Logger logger = LoggerFactory.getLogger(Application.class);
40+
41+
private static final int THREADS_COUNT = 32;
42+
private static final int RECORDS_COUNT = 1_000_000;
43+
private static final int LOAD_BATCH_SIZE = 1000;
44+
45+
private static final int WORKLOAD_DURATION_SECS = 60; // 60 seconds
46+
47+
public static void main(String[] args) {
48+
SpringApplication.run(Application.class, args).close();
49+
}
50+
51+
private final Ticker ticker = new Ticker(logger);
52+
53+
private final SchemeService schemeService;
54+
private final TokenService tokenService;
55+
56+
private final ExecutorService executor;
57+
private final AtomicInteger threadCounter = new AtomicInteger(0);
58+
private final AtomicInteger executionsCount = new AtomicInteger(0);
59+
private final AtomicInteger retriesCount = new AtomicInteger(0);
60+
61+
public Application(SchemeService schemeService, TokenService tokenService) {
62+
this.schemeService = schemeService;
63+
this.tokenService = tokenService;
64+
65+
this.executor = Executors.newFixedThreadPool(THREADS_COUNT, this::threadFactory);
66+
}
67+
68+
@PreDestroy
69+
public void close() throws Exception {
70+
logger.info("CLI app is waiting for finishing");
71+
72+
executor.shutdown();
73+
executor.awaitTermination(5, TimeUnit.MINUTES);
74+
75+
ticker.printTotal();
76+
ticker.close();
77+
78+
logger.info("Executed {} transactions with {} retries", executionsCount.get(), retriesCount.get());
79+
logger.info("CLI app has finished");
80+
}
81+
82+
@Bean
83+
public RetryListener retryListener() {
84+
return new RetryListener() {
85+
@Override
86+
public <T, E extends Throwable> boolean open(RetryContext ctx, RetryCallback<T, E> callback) {
87+
executionsCount.incrementAndGet();
88+
return true;
89+
}
90+
91+
@Override
92+
public <T, E extends Throwable> void onError(RetryContext ctx, RetryCallback<T, E> callback, Throwable th) {
93+
logger.debug("Retry operation with error {} ", printSqlException(th));
94+
retriesCount.incrementAndGet();
95+
}
96+
};
97+
}
98+
99+
private String printSqlException(Throwable th) {
100+
Throwable ex = th;
101+
while (ex != null) {
102+
if (ex instanceof SQLException) {
103+
return ex.getMessage();
104+
}
105+
ex = ex.getCause();
106+
}
107+
return th.getMessage();
108+
}
109+
110+
@Override
111+
public void run(String... args) {
112+
logger.info("CLI app has started");
113+
114+
for (String arg : args) {
115+
logger.info("execute {} step", arg);
116+
117+
if ("clean".equalsIgnoreCase(arg)) {
118+
schemeService.executeClean();
119+
}
120+
121+
if ("init".equalsIgnoreCase(arg)) {
122+
schemeService.executeInit();
123+
}
124+
125+
if ("load".equalsIgnoreCase(arg)) {
126+
ticker.runWithMonitor(this::loadData);
127+
}
128+
129+
if ("run".equalsIgnoreCase(arg)) {
130+
ticker.runWithMonitor(this::runWorkloads);
131+
}
132+
133+
if ("test".equalsIgnoreCase(arg)) {
134+
ticker.runWithMonitor(this::test);
135+
}
136+
}
137+
}
138+
139+
private Thread threadFactory(Runnable runnable) {
140+
return new Thread(runnable, "app-thread-" + threadCounter.incrementAndGet());
141+
}
142+
143+
private void loadData() {
144+
List<CompletableFuture<?>> futures = new ArrayList<>();
145+
int id = 0;
146+
while (id < RECORDS_COUNT) {
147+
final int first = id;
148+
id += LOAD_BATCH_SIZE;
149+
final int last = id < RECORDS_COUNT ? id : RECORDS_COUNT;
150+
151+
futures.add(CompletableFuture.runAsync(() -> {
152+
try (Ticker.Measure measure = ticker.getLoad().newCall()) {
153+
tokenService.insertBatch(first, last);
154+
logger.info("inserted tokens [{}, {})", first, last);
155+
measure.inc();
156+
}
157+
}, executor));
158+
}
159+
160+
futures.forEach(CompletableFuture::join);
161+
}
162+
163+
private void test() {
164+
YdbTracer.current().markToPrint("test");
165+
166+
final Random rnd = new Random();
167+
List<Integer> randomIds = IntStream.range(0, 100)
168+
.mapToObj(idx -> rnd.nextInt(RECORDS_COUNT))
169+
.collect(Collectors.toList());
170+
171+
tokenService.updateBatch(randomIds);
172+
}
173+
174+
private void runWorkloads() {
175+
long finishAt = System.currentTimeMillis() + WORKLOAD_DURATION_SECS * 1000;
176+
List<CompletableFuture<?>> futures = new ArrayList<>();
177+
for (int i = 0; i < THREADS_COUNT; i++) {
178+
futures.add(CompletableFuture.runAsync(() -> this.workload(finishAt), executor));
179+
}
180+
181+
futures.forEach(CompletableFuture::join);
182+
}
183+
184+
private void workload(long finishAt) {
185+
final Random rnd = new Random();
186+
while (System.currentTimeMillis() < finishAt) {
187+
int mode = rnd.nextInt(10);
188+
189+
try {
190+
if (mode < 2) {
191+
try (Ticker.Measure measure = ticker.getBatchUpdate().newCall()) {
192+
List<Integer> randomIds = IntStream.range(0, 100)
193+
.mapToObj(idx -> rnd.nextInt(RECORDS_COUNT))
194+
.collect(Collectors.toList());
195+
tokenService.updateBatch(randomIds);
196+
measure.inc();
197+
}
198+
199+
} else if (mode < 6) {
200+
int id = rnd.nextInt(RECORDS_COUNT);
201+
try (Ticker.Measure measure = ticker.getFetch().newCall()) {
202+
tokenService.fetchToken(id);
203+
measure.inc();
204+
}
205+
} else {
206+
int id = rnd.nextInt(RECORDS_COUNT);
207+
try (Ticker.Measure measure = ticker.getUpdate().newCall()) {
208+
tokenService.updateToken(id);
209+
measure.inc();
210+
}
211+
}
212+
} catch (RuntimeException ex) {
213+
logger.debug("got exception {}", ex.getMessage());
214+
}
215+
}
216+
}
217+
}

0 commit comments

Comments
 (0)