-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add spring-boot-jdbc-postgresql-chaos example
- Loading branch information
1 parent
a1277db
commit 32f1ffd
Showing
10 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<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>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-parent</artifactId> | ||
<version>3.4.0-M3</version> | ||
<relativePath/> <!-- lookup parent from repository --> | ||
</parent> | ||
<groupId>com.example</groupId> | ||
<artifactId>spring-boot-jdbc-postgresql-chaos</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
<name>spring-boot-jdbc-postgresql-chaos</name> | ||
<description>Demo project for Spring Boot</description> | ||
<properties> | ||
<java.version>21</java.version> | ||
<testcontainers.version>1.20.1</testcontainers.version> | ||
</properties> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-jdbc</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.flywaydb</groupId> | ||
<artifactId>flyway-core</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.flywaydb</groupId> | ||
<artifactId>flyway-database-postgresql</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.github.resilience4j</groupId> | ||
<artifactId>resilience4j-retry</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.github.resilience4j</groupId> | ||
<artifactId>resilience4j-timelimiter</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.vavr</groupId> | ||
<artifactId>vavr</artifactId> | ||
<version>0.10.4</version> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.postgresql</groupId> | ||
<artifactId>postgresql</artifactId> | ||
<scope>runtime</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.testcontainers</groupId> | ||
<artifactId>junit-jupiter</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.testcontainers</groupId> | ||
<artifactId>postgresql</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.testcontainers</groupId> | ||
<artifactId>toxiproxy</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.assertj</groupId> | ||
<artifactId>assertj-vavr</artifactId> | ||
<version>0.4.3</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<dependencyManagement> | ||
<dependencies> | ||
<dependency> | ||
<groupId>io.github.resilience4j</groupId> | ||
<artifactId>resilience4j-bom</artifactId> | ||
<version>2.2.0</version> | ||
<type>pom</type> | ||
<scope>import</scope> | ||
</dependency> | ||
</dependencies> | ||
</dependencyManagement> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-surefire-plugin</artifactId> | ||
<configuration> | ||
<rerunFailingTestsCount>3</rerunFailingTestsCount> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
<repositories> | ||
<repository> | ||
<id>spring-milestones</id> | ||
<name>Spring Milestones</name> | ||
<url>https://repo.spring.io/milestone</url> | ||
<snapshots> | ||
<enabled>false</enabled> | ||
</snapshots> | ||
</repository> | ||
</repositories> | ||
<pluginRepositories> | ||
<pluginRepository> | ||
<id>spring-milestones</id> | ||
<name>Spring Milestones</name> | ||
<url>https://repo.spring.io/milestone</url> | ||
<snapshots> | ||
<enabled>false</enabled> | ||
</snapshots> | ||
</pluginRepository> | ||
</pluginRepositories> | ||
|
||
</project> |
4 changes: 4 additions & 0 deletions
4
...dbc-postgresql-chaos/src/main/java/com/example/springbootjdbcpostgresqlchaos/Profile.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.example.springbootjdbcpostgresqlchaos; | ||
|
||
public record Profile(Long id, String name) { | ||
} |
7 changes: 7 additions & 0 deletions
7
...esql-chaos/src/main/java/com/example/springbootjdbcpostgresqlchaos/ProfileRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.example.springbootjdbcpostgresqlchaos; | ||
|
||
import org.springframework.data.repository.CrudRepository; | ||
|
||
public interface ProfileRepository extends CrudRepository<Profile, Long> { | ||
|
||
} |
13 changes: 13 additions & 0 deletions
13
...a/com/example/springbootjdbcpostgresqlchaos/SpringBootJdbcPostgresqlChaosApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.example.springbootjdbcpostgresqlchaos; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
public class SpringBootJdbcPostgresqlChaosApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(SpringBootJdbcPostgresqlChaosApplication.class, args); | ||
} | ||
|
||
} |
1 change: 1 addition & 0 deletions
1
spring-boot-jdbc-postgresql-chaos/src/main/resources/application.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
6 changes: 6 additions & 0 deletions
6
...-boot-jdbc-postgresql-chaos/src/main/resources/db/migration/V1__create_populate_table.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
CREATE TABLE IF NOT EXISTS profile(id serial primary key, name varchar(255) not null); | ||
|
||
INSERT INTO profile (name) VALUES ('profile-1'); | ||
INSERT INTO profile (name) VALUES ('profile-2'); | ||
INSERT INTO profile (name) VALUES ('profile-3'); | ||
INSERT INTO profile (name) VALUES ('profile-4'); |
147 changes: 147 additions & 0 deletions
147
...springbootjdbcpostgresqlchaos/SpringBootJdbcPostgresqlChaosConfigCliApplicationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package com.example.springbootjdbcpostgresqlchaos; | ||
|
||
import io.github.resilience4j.core.IntervalFunction; | ||
import io.github.resilience4j.retry.Retry; | ||
import io.github.resilience4j.retry.RetryConfig; | ||
import io.github.resilience4j.timelimiter.TimeLimiter; | ||
import io.vavr.control.Try; | ||
import org.assertj.vavr.api.VavrAssertions; | ||
import org.junit.jupiter.api.Test; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest; | ||
import org.springframework.test.context.DynamicPropertyRegistry; | ||
import org.springframework.test.context.DynamicPropertySource; | ||
import org.testcontainers.containers.Container.ExecResult; | ||
import org.testcontainers.containers.Network; | ||
import org.testcontainers.containers.PostgreSQLContainer; | ||
import org.testcontainers.containers.ToxiproxyContainer; | ||
import org.testcontainers.junit.jupiter.Container; | ||
import org.testcontainers.junit.jupiter.Testcontainers; | ||
import org.testcontainers.utility.MountableFile; | ||
|
||
import java.time.Duration; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.TimeoutException; | ||
import java.util.function.Supplier; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@Testcontainers | ||
@DataJdbcTest | ||
class SpringBootJdbcPostgresqlChaosConfigCliApplicationTests { | ||
|
||
private static final Logger logger = LoggerFactory | ||
.getLogger(SpringBootJdbcPostgresqlChaosConfigCliApplicationTests.class); | ||
|
||
private static final Network network = Network.newNetwork(); | ||
|
||
@Container | ||
private static final PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine") | ||
.withNetwork(network) | ||
.withNetworkAliases("postgres"); | ||
|
||
@Container | ||
private static final ToxiproxyContainer toxiproxy = new ToxiproxyContainer("ghcr.io/shopify/toxiproxy:2.9.0") | ||
.withCopyFileToContainer(MountableFile.forClasspathResource("toxiproxy.json"), "/tmp/toxiproxy.json") | ||
.withCommand("-host=0.0.0.0", "-config=/tmp/toxiproxy.json") | ||
.withNetwork(network); | ||
|
||
@DynamicPropertySource | ||
static void sqlserverProperties(DynamicPropertyRegistry registry) throws Exception { | ||
registry.add("spring.datasource.url", () -> "jdbc:postgresql://%s:%d/%s".formatted(toxiproxy.getHost(), | ||
toxiproxy.getMappedPort(8666), postgres.getDatabaseName())); | ||
registry.add("spring.datasource.username", postgres::getUsername); | ||
registry.add("spring.datasource.password", postgres::getPassword); | ||
registry.add("spring.flyway.url", postgres::getJdbcUrl); | ||
registry.add("spring.flyway.user", postgres::getUsername); | ||
registry.add("spring.flyway.password", postgres::getPassword); | ||
} | ||
|
||
@Autowired | ||
private ProfileRepository profileRepository; | ||
|
||
@Test | ||
void normal() { | ||
assertThat(this.profileRepository.findAll()).hasSize(4); | ||
} | ||
|
||
@Test | ||
void withLatency() throws Exception { | ||
execute("./toxiproxy-cli toxic add -t latency --downstream -a latency=1600 -a jitter=100 -n latency_downstream postgresql"); | ||
|
||
assertThat(this.profileRepository.findAll()).hasSize(4); | ||
|
||
execute("./toxiproxy-cli toxic remove -n latency_downstream postgresql"); | ||
} | ||
|
||
@Test | ||
void withLatencyWithTimeout() throws Exception { | ||
execute("./toxiproxy-cli toxic add -t latency --downstream -a latency=1600 -a jitter=100 -n latency_downstream postgresql"); | ||
|
||
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofMillis(50)); | ||
Supplier<CompletableFuture<Iterable<Profile>>> completableFutureSupplier = () -> CompletableFuture | ||
.supplyAsync(() -> this.profileRepository.findAll()); | ||
Try<Iterable<Profile>> actual = Try.ofCallable(timeLimiter.decorateFutureSupplier(completableFutureSupplier)); | ||
VavrAssertions.assertThat(actual).isFailure().failBecauseOf(TimeoutException.class); | ||
|
||
execute("./toxiproxy-cli toxic remove -n latency_downstream postgresql"); | ||
} | ||
|
||
@Test | ||
void withLatencyWithRetries() throws Exception { | ||
var intervalFunction = IntervalFunction.of(Duration.ofMillis(500)); | ||
var retryConfig = RetryConfig.custom() | ||
.retryExceptions(TimeoutException.class) | ||
.maxAttempts(2) | ||
.failAfterMaxAttempts(true) | ||
.intervalFunction(intervalFunction) | ||
.build(); | ||
var jdbcRetry = Retry.of("jdbc", retryConfig); | ||
|
||
var timeLimiter = TimeLimiter.of(Duration.ofMillis(500)); | ||
|
||
execute("./toxiproxy-cli toxic add -t latency --downstream -a latency=1600 -a jitter=100 -n latency_downstream postgresql"); | ||
|
||
Supplier<CompletableFuture<Iterable<Profile>>> completableFutureSupplier = () -> CompletableFuture | ||
.supplyAsync(() -> { | ||
logger.info("Executing query"); | ||
return this.profileRepository.findAll(); | ||
}); | ||
Callable<Iterable<Profile>> iterableCallable = timeLimiter.decorateFutureSupplier(completableFutureSupplier); | ||
Callable<Iterable<Profile>> iterableCallable1 = Retry.decorateCallable(jdbcRetry, iterableCallable); | ||
VavrAssertions.assertThat(Try.ofCallable(iterableCallable1)).isFailure().failBecauseOf(TimeoutException.class); | ||
|
||
execute("./toxiproxy-cli toxic remove -n latency_downstream postgresql"); | ||
|
||
assertThat(this.profileRepository.findAll()).hasSize(4); | ||
} | ||
|
||
@Test | ||
void withToxiProxyConnectionDown() throws Exception { | ||
execute("./toxiproxy-cli toxic add -t bandwidth --downstream -a rate=0 -n bandwidth_downstream postgresql"); | ||
execute("./toxiproxy-cli toxic add -t bandwidth --upstream -a rate=0 -n bandwidth_upstream postgresql"); | ||
|
||
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofMillis(50)); | ||
Supplier<CompletableFuture<Iterable<Profile>>> completableFutureSupplier = () -> CompletableFuture | ||
.supplyAsync(() -> this.profileRepository.findAll()); | ||
Try<Iterable<Profile>> actual = Try.ofCallable(timeLimiter.decorateFutureSupplier(completableFutureSupplier)); | ||
VavrAssertions.assertThat(actual).isFailure().failBecauseOf(TimeoutException.class); | ||
|
||
execute("./toxiproxy-cli toxic remove -n bandwidth_downstream postgresql"); | ||
execute("./toxiproxy-cli toxic remove -n bandwidth_upstream postgresql"); | ||
|
||
assertThat(this.profileRepository.findAll()).hasSize(4); | ||
} | ||
|
||
private static void execute(String command) throws Exception { | ||
ExecResult result = toxiproxy.execInContainer(command.split(" ")); | ||
if (result.getExitCode() != 0) { | ||
throw new RuntimeException("Error executing command '%s' \nstderr: %s\nstdout: %s".formatted(command, | ||
result.getStderr(), result.getStdout())); | ||
} | ||
} | ||
|
||
} |
18 changes: 18 additions & 0 deletions
18
spring-boot-jdbc-postgresql-chaos/src/test/resources/logback-test.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<configuration> | ||
|
||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<!-- encoders are assigned the type | ||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="INFO"> | ||
<appender-ref ref="STDOUT"/> | ||
</root> | ||
|
||
<logger name="org.testcontainers" level="INFO"/> | ||
<logger name="com.github.dockerjava" level="WARN"/> | ||
|
||
</configuration> |
8 changes: 8 additions & 0 deletions
8
spring-boot-jdbc-postgresql-chaos/src/test/resources/toxiproxy.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[ | ||
{ | ||
"name": "postgresql", | ||
"listen": "0.0.0.0:8666", | ||
"upstream": "postgres:5432", | ||
"enabled": true | ||
} | ||
] |