Skip to content

Commit aadd1dd

Browse files
committed
refactoring configuration of common database pipelines
1 parent 9553c5a commit aadd1dd

File tree

7 files changed

+282
-91
lines changed

7 files changed

+282
-91
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,8 @@ The database prefetching feature is used to speed up the initialization and refr
648648
```properties
649649
zonky.test.database.prefetching.thread-name-prefix=prefetching- # Prefix to use for the names of database prefetching threads.
650650
zonky.test.database.prefetching.concurrency=3 # Maximum number of concurrently running database prefetching threads.
651-
zonky.test.database.prefetching.pipeline-cache-size=3 # Maximum number of prepared databases per pipeline.
651+
zonky.test.database.prefetching.pipeline-cache-size=5 # Maximum number of prepared databases per pipeline.
652+
zonky.test.database.prefetching.max-prepared-templates=10 # Maximum number of prepared database templates.
652653
```
653654

654655
### Disabling auto-configuration
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package io.zonky.test.db.config;
2+
3+
import com.google.common.collect.ImmutableList;
4+
import io.zonky.test.db.provider.DatabaseProvider;
5+
import io.zonky.test.db.provider.TemplatableDatabaseProvider;
6+
import io.zonky.test.db.provider.common.OptimizingDatabaseProvider;
7+
import io.zonky.test.db.provider.common.PrefetchingDatabaseProvider;
8+
import io.zonky.test.db.provider.common.TemplatingDatabaseProvider;
9+
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
10+
import org.springframework.util.Assert;
11+
12+
import java.util.function.BiFunction;
13+
import java.util.function.Consumer;
14+
15+
public class DatabaseProviderFactory {
16+
17+
protected final AutowireCapableBeanFactory beanFactory;
18+
protected final BiFunction<PipelineBuilder, DatabaseProvider, DatabaseProvider> providerPreparer;
19+
protected final ImmutableList<Consumer<TemplatingDatabaseProvider.Config.Builder>> templatingCustomizers;
20+
protected final ImmutableList<Consumer<PrefetchingDatabaseProvider.Config.Builder>> prefetchingCustomizers;
21+
22+
public DatabaseProviderFactory(AutowireCapableBeanFactory beanFactory) {
23+
this.beanFactory = beanFactory;
24+
this.providerPreparer = (builder, provider) -> provider;
25+
this.templatingCustomizers = ImmutableList.of();
26+
this.prefetchingCustomizers = ImmutableList.of();
27+
}
28+
29+
protected DatabaseProviderFactory(AutowireCapableBeanFactory beanFactory,
30+
BiFunction<PipelineBuilder, DatabaseProvider, DatabaseProvider> providerPreparer,
31+
ImmutableList<Consumer<TemplatingDatabaseProvider.Config.Builder>> templatingCustomizers,
32+
ImmutableList<Consumer<PrefetchingDatabaseProvider.Config.Builder>> prefetchingCustomizers) {
33+
this.beanFactory = beanFactory;
34+
this.providerPreparer = providerPreparer;
35+
this.templatingCustomizers = templatingCustomizers;
36+
this.prefetchingCustomizers = prefetchingCustomizers;
37+
}
38+
39+
public DatabaseProvider createProvider(Class<? extends DatabaseProvider> providerType) {
40+
return providerPreparer.apply(new PipelineBuilder(), beanFactory.createBean(providerType));
41+
}
42+
43+
public DatabaseProviderFactory customizeTemplating(Consumer<TemplatingDatabaseProvider.Config.Builder> customizer) {
44+
ImmutableList<Consumer<TemplatingDatabaseProvider.Config.Builder>> templatingCustomizers =
45+
ImmutableList.<Consumer<TemplatingDatabaseProvider.Config.Builder>>builder()
46+
.addAll(this.templatingCustomizers).add(customizer).build();
47+
return new DatabaseProviderFactory(beanFactory, providerPreparer, templatingCustomizers, prefetchingCustomizers);
48+
}
49+
50+
public DatabaseProviderFactory customizePrefetching(Consumer<PrefetchingDatabaseProvider.Config.Builder> customizer) {
51+
ImmutableList<Consumer<PrefetchingDatabaseProvider.Config.Builder>> prefetchingCustomizers =
52+
ImmutableList.<Consumer<PrefetchingDatabaseProvider.Config.Builder>>builder()
53+
.addAll(this.prefetchingCustomizers).add(customizer).build();
54+
return new DatabaseProviderFactory(beanFactory, providerPreparer, templatingCustomizers, prefetchingCustomizers);
55+
}
56+
57+
public DatabaseProviderFactory customizeProvider(BiFunction<PipelineBuilder, DatabaseProvider, DatabaseProvider> customizer) {
58+
return new DatabaseProviderFactory(beanFactory, customizer, templatingCustomizers, prefetchingCustomizers);
59+
}
60+
61+
protected OptimizingDatabaseProvider optimizingProvider(DatabaseProvider provider) {
62+
return new OptimizingDatabaseProvider(provider);
63+
}
64+
65+
protected PrefetchingDatabaseProvider prefetchingProvider(DatabaseProvider provider) {
66+
return new PrefetchingDatabaseProvider(provider, prefetchingConfig());
67+
}
68+
69+
protected TemplatingDatabaseProvider templatingProvider(DatabaseProvider provider) {
70+
Assert.isAssignable(TemplatableDatabaseProvider.class, provider.getClass());
71+
return new TemplatingDatabaseProvider((TemplatableDatabaseProvider) provider, templatingConfig());
72+
}
73+
74+
protected PrefetchingDatabaseProvider.Config prefetchingConfig() {
75+
PrefetchingDatabaseProvider.Config.Builder builder = PrefetchingDatabaseProvider.Config.builder();
76+
prefetchingCustomizers.forEach(customizer -> customizer.accept(builder));
77+
return builder.build();
78+
}
79+
80+
protected TemplatingDatabaseProvider.Config templatingConfig() {
81+
TemplatingDatabaseProvider.Config.Builder builder = TemplatingDatabaseProvider.Config.builder();
82+
templatingCustomizers.forEach(customizer -> customizer.accept(builder));
83+
return builder.build();
84+
}
85+
86+
public class PipelineBuilder {
87+
88+
public OptimizingDatabaseProvider optimizingProvider(DatabaseProvider provider) {
89+
return DatabaseProviderFactory.this.optimizingProvider(provider);
90+
}
91+
92+
public PrefetchingDatabaseProvider prefetchingProvider(DatabaseProvider provider) {
93+
return DatabaseProviderFactory.this.prefetchingProvider(provider);
94+
}
95+
96+
public TemplatingDatabaseProvider templatingProvider(DatabaseProvider provider) {
97+
return DatabaseProviderFactory.this.templatingProvider(provider);
98+
}
99+
}
100+
}

embedded-database-spring-test/src/main/java/io/zonky/test/db/config/EmbeddedDatabaseAutoConfiguration.java

Lines changed: 59 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@
2121
import io.zonky.test.db.liquibase.LiquibaseDatabaseExtension;
2222
import io.zonky.test.db.liquibase.LiquibasePropertiesPostProcessor;
2323
import io.zonky.test.db.provider.DatabaseProvider;
24-
import io.zonky.test.db.provider.TemplatableDatabaseProvider;
25-
import io.zonky.test.db.provider.common.OptimizingDatabaseProvider;
26-
import io.zonky.test.db.provider.common.PrefetchingDatabaseProvider;
27-
import io.zonky.test.db.provider.common.TemplatingDatabaseProvider;
2824
import io.zonky.test.db.provider.mariadb.DockerMariaDBDatabaseProvider;
2925
import io.zonky.test.db.provider.mssql.DockerMSSQLDatabaseProvider;
3026
import io.zonky.test.db.provider.mysql.DockerMySQLDatabaseProvider;
@@ -36,134 +32,142 @@
3632
import io.zonky.test.db.support.DefaultProviderResolver;
3733
import io.zonky.test.db.support.ProviderResolver;
3834
import org.springframework.beans.factory.BeanClassLoaderAware;
39-
import org.springframework.beans.factory.BeanFactory;
40-
import org.springframework.beans.factory.BeanFactoryAware;
4135
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
4236
import org.springframework.beans.factory.config.BeanDefinition;
4337
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
44-
import org.springframework.context.EnvironmentAware;
4538
import org.springframework.context.annotation.Bean;
4639
import org.springframework.context.annotation.Configuration;
4740
import org.springframework.context.annotation.Role;
48-
import org.springframework.context.annotation.Scope;
4941
import org.springframework.core.env.Environment;
5042
import org.springframework.util.ClassUtils;
5143

5244
@Configuration
53-
public class EmbeddedDatabaseAutoConfiguration implements EnvironmentAware, BeanClassLoaderAware, BeanFactoryAware {
45+
public class EmbeddedDatabaseAutoConfiguration implements BeanClassLoaderAware {
5446

55-
private Environment environment;
5647
private ClassLoader classLoader;
57-
private AutowireCapableBeanFactory beanFactory;
58-
59-
@Override
60-
public void setEnvironment(Environment environment) {
61-
this.environment = environment;
62-
}
6348

6449
@Override
6550
public void setBeanClassLoader(ClassLoader classLoader) {
6651
this.classLoader = classLoader;
6752
}
6853

69-
@Override
70-
public void setBeanFactory(BeanFactory beanFactory) {
71-
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
72-
}
73-
7454
@Bean
7555
@Provider(type = "docker", database = "postgres")
7656
@ConditionalOnMissingBean(name = "dockerPostgresDatabaseProvider")
77-
public DatabaseProvider dockerPostgresDatabaseProvider() {
57+
public DatabaseProvider dockerPostgresDatabaseProvider(DatabaseProviderFactory postgresDatabaseProviderFactory) {
7858
checkDependency("org.testcontainers", "postgresql", "org.testcontainers.containers.PostgreSQLContainer");
7959
checkDependency("org.postgresql", "postgresql", "org.postgresql.ds.PGSimpleDataSource");
80-
TemplatableDatabaseProvider provider = beanFactory.createBean(DockerPostgresDatabaseProvider.class);
81-
return optimizingDatabaseProvider(prefetchingDatabaseProvider(templatingDatabaseProvider(provider)));
60+
return postgresDatabaseProviderFactory.createProvider(DockerPostgresDatabaseProvider.class);
8261
}
8362

8463
@Bean
8564
@Provider(type = "zonky", database = "postgres")
8665
@ConditionalOnMissingBean(name = "zonkyPostgresDatabaseProvider")
87-
public DatabaseProvider zonkyPostgresDatabaseProvider() {
66+
public DatabaseProvider zonkyPostgresDatabaseProvider(DatabaseProviderFactory postgresDatabaseProviderFactory) {
8867
checkDependency("io.zonky.test", "embedded-postgres", "io.zonky.test.db.postgres.embedded.EmbeddedPostgres");
8968
checkDependency("org.postgresql", "postgresql", "org.postgresql.ds.PGSimpleDataSource");
90-
TemplatableDatabaseProvider provider = beanFactory.createBean(ZonkyPostgresDatabaseProvider.class);
91-
return optimizingDatabaseProvider(prefetchingDatabaseProvider(templatingDatabaseProvider(provider)));
69+
return postgresDatabaseProviderFactory.createProvider(ZonkyPostgresDatabaseProvider.class);
9270
}
9371

9472
@Bean
9573
@Provider(type = "opentable", database = "postgres")
9674
@ConditionalOnMissingBean(name = "openTablePostgresDatabaseProvider")
97-
public DatabaseProvider openTablePostgresDatabaseProvider() {
75+
public DatabaseProvider openTablePostgresDatabaseProvider(DatabaseProviderFactory postgresDatabaseProviderFactory) {
9876
checkDependency("com.opentable.components", "otj-pg-embedded", "com.opentable.db.postgres.embedded.EmbeddedPostgres");
9977
checkDependency("org.postgresql", "postgresql", "org.postgresql.ds.PGSimpleDataSource");
100-
TemplatableDatabaseProvider provider = beanFactory.createBean(OpenTablePostgresDatabaseProvider.class);
101-
return optimizingDatabaseProvider(prefetchingDatabaseProvider(templatingDatabaseProvider(provider)));
78+
return postgresDatabaseProviderFactory.createProvider(OpenTablePostgresDatabaseProvider.class);
10279
}
10380

10481
@Bean
10582
@Provider(type = "yandex", database = "postgres")
10683
@ConditionalOnMissingBean(name = "yandexPostgresDatabaseProvider")
107-
public DatabaseProvider yandexPostgresDatabaseProvider() {
84+
public DatabaseProvider yandexPostgresDatabaseProvider(DatabaseProviderFactory postgresDatabaseProviderFactory) {
10885
checkDependency("ru.yandex.qatools.embed", "postgresql-embedded", "ru.yandex.qatools.embed.postgresql.EmbeddedPostgres");
10986
checkDependency("org.postgresql", "postgresql", "org.postgresql.ds.PGSimpleDataSource");
110-
TemplatableDatabaseProvider provider = beanFactory.createBean(YandexPostgresDatabaseProvider.class);
111-
return optimizingDatabaseProvider(prefetchingDatabaseProvider(templatingDatabaseProvider(provider)));
87+
return postgresDatabaseProviderFactory.createProvider(YandexPostgresDatabaseProvider.class);
11288
}
11389

11490
@Bean
11591
@Provider(type = "docker", database = "mssql")
11692
@ConditionalOnMissingBean(name = "dockerMsSqlDatabaseProvider")
117-
public DatabaseProvider dockerMsSqlDatabaseProvider() {
93+
public DatabaseProvider dockerMsSqlDatabaseProvider(DatabaseProviderFactory msSqlDatabaseProviderFactory) {
11894
checkDependency("org.testcontainers", "mssqlserver", "org.testcontainers.containers.MSSQLServerContainer");
11995
checkDependency("com.microsoft.sqlserver", "mssql-jdbc", "com.microsoft.sqlserver.jdbc.SQLServerDataSource");
120-
DockerMSSQLDatabaseProvider provider = beanFactory.createBean(DockerMSSQLDatabaseProvider.class);
121-
return optimizingDatabaseProvider(prefetchingDatabaseProvider(templatingDatabaseProvider(provider)));
96+
return msSqlDatabaseProviderFactory.createProvider(DockerMSSQLDatabaseProvider.class);
12297
}
12398

12499
@Bean
125100
@Provider(type = "docker", database = "mysql")
126101
@ConditionalOnMissingBean(name = "dockerMySqlDatabaseProvider")
127-
public DatabaseProvider dockerMySqlDatabaseProvider() {
102+
public DatabaseProvider dockerMySqlDatabaseProvider(DatabaseProviderFactory mySqlDatabaseProviderFactory) {
128103
checkDependency("org.testcontainers", "mysql", "org.testcontainers.containers.MySQLContainer");
129104
checkDependency("mysql", "mysql-connector-java", "com.mysql.cj.jdbc.MysqlDataSource");
130-
DockerMySQLDatabaseProvider provider = beanFactory.createBean(DockerMySQLDatabaseProvider.class);
131-
return optimizingDatabaseProvider(provider);
105+
return mySqlDatabaseProviderFactory.createProvider(DockerMySQLDatabaseProvider.class);
132106
}
133107

134108
@Bean
135109
@Provider(type = "docker", database = "mariadb")
136110
@ConditionalOnMissingBean(name = "dockerMariaDbDatabaseProvider")
137-
public DatabaseProvider dockerMariaDbDatabaseProvider() {
111+
public DatabaseProvider dockerMariaDbDatabaseProvider(DatabaseProviderFactory mariaDbDatabaseProviderFactory) {
138112
checkDependency("org.testcontainers", "mariadb", "org.testcontainers.containers.MariaDBContainer");
139113
checkDependency("org.mariadb.jdbc", "mariadb-java-client", "org.mariadb.jdbc.MariaDbDataSource");
140-
DockerMariaDBDatabaseProvider provider = beanFactory.createBean(DockerMariaDBDatabaseProvider.class);
141-
return optimizingDatabaseProvider(provider);
114+
return mariaDbDatabaseProviderFactory.createProvider(DockerMariaDBDatabaseProvider.class);
115+
}
116+
117+
@Bean
118+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
119+
@ConditionalOnMissingBean(name = "postgresDatabaseProviderFactory")
120+
public DatabaseProviderFactory postgresDatabaseProviderFactory(DatabaseProviderFactory defaultDatabaseProviderFactory) {
121+
return defaultDatabaseProviderFactory.customizeProvider((builder, provider) ->
122+
builder.optimizingProvider(
123+
builder.prefetchingProvider(
124+
builder.templatingProvider(provider))));
125+
}
126+
127+
@Bean
128+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
129+
@ConditionalOnMissingBean(name = "msSqlDatabaseProviderFactory")
130+
public DatabaseProviderFactory msSqlDatabaseProviderFactory(DatabaseProviderFactory defaultDatabaseProviderFactory) {
131+
return defaultDatabaseProviderFactory.customizeProvider((builder, provider) ->
132+
builder.optimizingProvider(
133+
builder.prefetchingProvider(
134+
builder.templatingProvider(provider))));
142135
}
143136

144137
@Bean
145-
@Scope("prototype")
146138
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
147-
@ConditionalOnMissingBean(name = "optimizingDatabaseProvider")
148-
public OptimizingDatabaseProvider optimizingDatabaseProvider(DatabaseProvider provider) {
149-
return new OptimizingDatabaseProvider(provider);
139+
@ConditionalOnMissingBean(name = "mySqlDatabaseProviderFactory")
140+
public DatabaseProviderFactory mySqlDatabaseProviderFactory(DatabaseProviderFactory defaultDatabaseProviderFactory) {
141+
return defaultDatabaseProviderFactory.customizeProvider((builder, provider) ->
142+
builder.optimizingProvider(provider));
150143
}
151144

152-
// TODO: consider using a factory bean instead (also consider using pipeline factories aimed for specific provider types)
153145
@Bean
154-
@Scope("prototype")
155146
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
156-
@ConditionalOnMissingBean(name = "prefetchingDatabaseProvider")
157-
public PrefetchingDatabaseProvider prefetchingDatabaseProvider(DatabaseProvider provider) {
158-
return new PrefetchingDatabaseProvider(provider, environment);
147+
@ConditionalOnMissingBean(name = "mariaDbDatabaseProviderFactory")
148+
public DatabaseProviderFactory mariaDbDatabaseProviderFactory(DatabaseProviderFactory defaultDatabaseProviderFactory) {
149+
return defaultDatabaseProviderFactory.customizeProvider((builder, provider) ->
150+
builder.optimizingProvider(provider));
159151
}
160152

161153
@Bean
162-
@Scope("prototype")
163154
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
164-
@ConditionalOnMissingBean(name = "templatingDatabaseProvider")
165-
public TemplatingDatabaseProvider templatingDatabaseProvider(TemplatableDatabaseProvider provider) {
166-
return new TemplatingDatabaseProvider(provider);
155+
@ConditionalOnMissingBean(name = "defaultDatabaseProviderFactory")
156+
public DatabaseProviderFactory defaultDatabaseProviderFactory(AutowireCapableBeanFactory beanFactory, Environment environment) {
157+
String threadNamePrefix = environment.getProperty("zonky.test.database.prefetching.thread-name-prefix", "prefetching-");
158+
int concurrency = environment.getProperty("zonky.test.database.prefetching.concurrency", int.class, 3);
159+
int pipelineCacheSize = environment.getProperty("zonky.test.database.prefetching.pipeline-cache-size", int.class, 5);
160+
int maxPreparedTemplates = environment.getProperty("zonky.test.database.prefetching.max-prepared-templates", int.class, 10);
161+
int maxPreparedDatabases = (maxPreparedTemplates * 2/3 * 2) + pipelineCacheSize;
162+
163+
return new DatabaseProviderFactory(beanFactory)
164+
.customizeTemplating(builder -> builder
165+
.withMaxTemplateCount(maxPreparedTemplates))
166+
.customizePrefetching(builder -> builder
167+
.withThreadNamePrefix(threadNamePrefix)
168+
.withConcurrency(concurrency)
169+
.withPipelineMaxCacheSize(pipelineCacheSize)
170+
.withMaxPreparedDatabases(maxPreparedDatabases));
167171
}
168172

169173
@Bean

0 commit comments

Comments
 (0)