Skip to content

Commit a161230

Browse files
committed
fix caching of prepared databases
1 parent adb4ff8 commit a161230

File tree

3 files changed

+126
-15
lines changed

3 files changed

+126
-15
lines changed

src/main/java/io/zonky/test/db/postgres/embedded/EmbeddedPostgres.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
import java.util.UUID;
5252
import java.util.concurrent.TimeUnit;
5353
import java.util.concurrent.atomic.AtomicBoolean;
54-
import java.util.concurrent.atomic.AtomicReference;
5554
import java.util.concurrent.locks.Lock;
5655
import java.util.concurrent.locks.ReentrantLock;
5756
import java.util.stream.Collectors;
@@ -571,6 +570,33 @@ public EmbeddedPostgres start() throws IOException {
571570
}
572571
return new EmbeddedPostgres(parentDirectory, builderDataDirectory, builderCleanDataDirectory, config, localeConfig, builderPort, connectConfig, pgBinaryResolver, errRedirector, outRedirector, pgStartupWait);
573572
}
573+
574+
@Override
575+
public boolean equals(Object o) {
576+
if (this == o) {
577+
return true;
578+
}
579+
if (o == null || getClass() != o.getClass()) {
580+
return false;
581+
}
582+
Builder builder = (Builder) o;
583+
return builderCleanDataDirectory == builder.builderCleanDataDirectory &&
584+
builderPort == builder.builderPort &&
585+
Objects.equals(parentDirectory, builder.parentDirectory) &&
586+
Objects.equals(builderDataDirectory, builder.builderDataDirectory) &&
587+
Objects.equals(config, builder.config) &&
588+
Objects.equals(localeConfig, builder.localeConfig) &&
589+
Objects.equals(connectConfig, builder.connectConfig) &&
590+
Objects.equals(pgBinaryResolver, builder.pgBinaryResolver) &&
591+
Objects.equals(pgStartupWait, builder.pgStartupWait) &&
592+
Objects.equals(errRedirector, builder.errRedirector) &&
593+
Objects.equals(outRedirector, builder.outRedirector);
594+
}
595+
596+
@Override
597+
public int hashCode() {
598+
return Objects.hash(parentDirectory, builderDataDirectory, config, localeConfig, builderCleanDataDirectory, builderPort, connectConfig, pgBinaryResolver, pgStartupWait, errRedirector, outRedirector);
599+
}
574600
}
575601

576602
private static List<String> system(String... command)
@@ -601,8 +627,8 @@ private static void mkdirs(File dir)
601627
}
602628
}
603629

604-
private static final AtomicReference<File> BINARY_DIR = new AtomicReference<>();
605630
private static final Lock PREPARE_BINARIES_LOCK = new ReentrantLock();
631+
private static final Map<PgBinaryResolver, File> PREPARE_BINARIES = new HashMap<>();
606632

607633
/**
608634
* Get current operating system string. The string is used in the appropriate postgres binary name.
@@ -685,8 +711,8 @@ private static File prepareBinaries(PgBinaryResolver pgBinaryResolver)
685711
{
686712
PREPARE_BINARIES_LOCK.lock();
687713
try {
688-
if(BINARY_DIR.get() != null) {
689-
return BINARY_DIR.get();
714+
if (PREPARE_BINARIES.containsKey(pgBinaryResolver)) {
715+
return PREPARE_BINARIES.get(pgBinaryResolver);
690716
}
691717

692718
final String system = getOS();
@@ -765,7 +791,7 @@ private static File prepareBinaries(PgBinaryResolver pgBinaryResolver)
765791
LOG.warn("could not delete {}", pgTbz);
766792
}
767793
}
768-
BINARY_DIR.set(pgDir);
794+
PREPARE_BINARIES.put(pgBinaryResolver, pgDir);
769795
LOG.info("Postgres binaries at {}", pgDir);
770796
return pgDir;
771797
} finally {

src/main/java/io/zonky/test/db/postgres/embedded/PreparedDbProvider.java

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.HashMap;
2222
import java.util.Locale;
2323
import java.util.Map;
24+
import java.util.Objects;
2425
import java.util.concurrent.ExecutorService;
2526
import java.util.concurrent.Executors;
2627
import java.util.concurrent.SynchronousQueue;
@@ -41,11 +42,9 @@ public class PreparedDbProvider
4142
* loaded so that the databases may be cloned.
4243
*/
4344
// @GuardedBy("PreparedDbProvider.class")
44-
private static final Map<DatabasePreparer, PrepPipeline> CLUSTERS = new HashMap<>();
45+
private static final Map<ClusterKey, PrepPipeline> CLUSTERS = new HashMap<>();
4546

4647
private final PrepPipeline dbPreparer;
47-
private final Iterable<Consumer<Builder>> customizers;
48-
4948

5049
public static PreparedDbProvider forPreparer(DatabasePreparer preparer) {
5150
return forPreparer(preparer, Collections.emptyList());
@@ -55,11 +54,9 @@ public static PreparedDbProvider forPreparer(DatabasePreparer preparer, Iterable
5554
return new PreparedDbProvider(preparer, customizers);
5655
}
5756

58-
private PreparedDbProvider(DatabasePreparer preparer, Iterable<Consumer<Builder>> customizers)
59-
{
60-
this.customizers = customizers;
57+
private PreparedDbProvider(DatabasePreparer preparer, Iterable<Consumer<Builder>> customizers) {
6158
try {
62-
dbPreparer = createOrFindPreparer(preparer);
59+
dbPreparer = createOrFindPreparer(preparer, customizers);
6360
} catch (final IOException | SQLException e) {
6461
throw new RuntimeException(e);
6562
}
@@ -69,9 +66,10 @@ private PreparedDbProvider(DatabasePreparer preparer, Iterable<Consumer<Builder>
6966
* Each schema set has its own database cluster. The template1 database has the schema preloaded so that
7067
* each test case need only create a new database and not re-invoke your preparer.
7168
*/
72-
private synchronized PrepPipeline createOrFindPreparer(DatabasePreparer preparer) throws IOException, SQLException
69+
private static synchronized PrepPipeline createOrFindPreparer(DatabasePreparer preparer, Iterable<Consumer<Builder>> customizers) throws IOException, SQLException
7370
{
74-
PrepPipeline result = CLUSTERS.get(preparer);
71+
final ClusterKey key = new ClusterKey(preparer, customizers);
72+
PrepPipeline result = CLUSTERS.get(key);
7573
if (result != null) {
7674
return result;
7775
}
@@ -82,7 +80,7 @@ private synchronized PrepPipeline createOrFindPreparer(DatabasePreparer preparer
8280
preparer.prepare(pg.getTemplateDatabase());
8381

8482
result = new PrepPipeline(pg).start();
85-
CLUSTERS.put(preparer, result);
83+
CLUSTERS.put(key, result);
8684
return result;
8785
}
8886

@@ -233,6 +231,36 @@ private static void create(final DataSource connectDb, final String dbName, fina
233231
}
234232
}
235233

234+
private static class ClusterKey {
235+
236+
private final DatabasePreparer preparer;
237+
private final Builder builder;
238+
239+
ClusterKey(DatabasePreparer preparer, Iterable<Consumer<Builder>> customizers) {
240+
this.preparer = preparer;
241+
this.builder = EmbeddedPostgres.builder();
242+
customizers.forEach(c -> c.accept(this.builder));
243+
}
244+
245+
@Override
246+
public boolean equals(Object o) {
247+
if (this == o) {
248+
return true;
249+
}
250+
if (o == null || getClass() != o.getClass()) {
251+
return false;
252+
}
253+
ClusterKey that = (ClusterKey) o;
254+
return Objects.equals(preparer, that.preparer) &&
255+
Objects.equals(builder, that.builder);
256+
}
257+
258+
@Override
259+
public int hashCode() {
260+
return Objects.hash(preparer, builder);
261+
}
262+
}
263+
236264
public static class DbInfo
237265
{
238266
public static DbInfo ok(final String dbName, final int port, final String user) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.zonky.test.db.postgres.embedded;
15+
16+
import io.zonky.test.db.postgres.junit.EmbeddedPostgresRules;
17+
import io.zonky.test.db.postgres.junit.PreparedDbRule;
18+
import org.junit.Rule;
19+
import org.junit.Test;
20+
21+
import java.time.Duration;
22+
23+
import static org.junit.Assert.assertEquals;
24+
import static org.junit.Assert.assertNotEquals;
25+
26+
public class PreparedDbCustomizerTest {
27+
28+
private static final DatabasePreparer EMPTY_PREPARER = ds -> {};
29+
30+
@Rule
31+
public PreparedDbRule dbA1 = EmbeddedPostgresRules.preparedDatabase(EMPTY_PREPARER);
32+
@Rule
33+
public PreparedDbRule dbA2 = EmbeddedPostgresRules.preparedDatabase(EMPTY_PREPARER).customize(builder -> {});
34+
@Rule
35+
public PreparedDbRule dbA3 = EmbeddedPostgresRules.preparedDatabase(EMPTY_PREPARER).customize(builder -> builder.setPGStartupWait(Duration.ofSeconds(10)));
36+
@Rule
37+
public PreparedDbRule dbB1 = EmbeddedPostgresRules.preparedDatabase(EMPTY_PREPARER).customize(builder -> builder.setPGStartupWait(Duration.ofSeconds(11)));
38+
@Rule
39+
public PreparedDbRule dbB2 = EmbeddedPostgresRules.preparedDatabase(EMPTY_PREPARER).customize(builder -> builder.setPGStartupWait(Duration.ofSeconds(11)));
40+
41+
@Test
42+
public void testCustomizers() {
43+
int dbA1Port = dbA1.getConnectionInfo().getPort();
44+
int dbA2Port = dbA2.getConnectionInfo().getPort();
45+
int dbA3Port = dbA3.getConnectionInfo().getPort();
46+
47+
assertEquals(dbA1Port, dbA2Port);
48+
assertEquals(dbA1Port, dbA3Port);
49+
50+
int dbB1Port = dbB1.getConnectionInfo().getPort();
51+
int dbB2Port = dbB2.getConnectionInfo().getPort();
52+
53+
assertEquals(dbB1Port, dbB2Port);
54+
55+
assertNotEquals(dbA1Port, dbB2Port);
56+
}
57+
}

0 commit comments

Comments
 (0)