Skip to content

Commit 729263b

Browse files
committed
Update tests to prepare for concurrent execution
This readies tests for concurrent execution. It makes a few changes: * The FDBDatabaseExtension is now scoped to the test instance instead of static. This allows us to create a new `FDBDatabaseFactory` and `FDBDatabase` for each test execution, which in turn means that we can execute tests that modify the factory's state concurrently, and we can have each database check for open contexts. This did mean that this needed to add a new method to create an `FDBDatabaseFactory` for testing (only) that accepted an already initialized FDB. * One test introduced a write conflict range on the range `\x00` to `\xff`, so it would cause random other tests to fail if they were executed at the same time. I've narrowed the range of the conflict range so that it is now on a unique keyspace for that test. * I introduced a few new test extensions that can be applied to every test. In particular, the `TestMdcExtension` updates the logger MDC with the test class, method name, and displey name so that test logs contain more information that can be used to correlate information. Meanwhile, the `ExceptionLoggingDetailsExtension` adds an exception handler for loggable exceptions that will log the logging details if a test fails with, say, a `RecordCoreException`, we have a way of understanding the underlying details.
1 parent dad489e commit 729263b

File tree

75 files changed

+797
-451
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+797
-451
lines changed

.idea/compiler.xml

+3-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fdb-extensions/fdb-extensions.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ dependencies {
3535
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
3636
testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}" // binding
3737
testImplementation "org.apache.logging.log4j:log4j-core:${log4jVersion}" // library
38+
testCompileOnly "com.google.auto.service:auto-service:undefined"
39+
testAnnotationProcessor "com.google.auto.service:auto-service:undefined"
3840
}
3941

4042
publishing {

fdb-extensions/src/test/java/com/apple/foundationdb/async/MoreAsyncUtilTest.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020

2121
package com.apple.foundationdb.async;
2222

23+
import com.apple.foundationdb.test.TestExecutors;
2324
import org.junit.jupiter.api.Test;
2425

2526
import java.util.concurrent.CompletableFuture;
2627
import java.util.concurrent.Executor;
27-
import java.util.concurrent.ForkJoinPool;
2828
import java.util.concurrent.TimeUnit;
2929

3030
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -35,8 +35,7 @@
3535
* Tests for {@link MoreAsyncUtil}.
3636
*/
3737
public class MoreAsyncUtilTest {
38-
39-
static final Executor EXECUTOR = ForkJoinPool.commonPool();
38+
static final Executor EXECUTOR = TestExecutors.defaultThreadPool();
4039

4140
int count;
4241

fdb-extensions/src/test/java/com/apple/foundationdb/async/RangeSetTest.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import org.junit.jupiter.api.Tag;
3535
import org.junit.jupiter.api.Test;
3636
import org.junit.jupiter.api.extension.RegisterExtension;
37+
import org.junit.jupiter.api.parallel.Execution;
38+
import org.junit.jupiter.api.parallel.ExecutionMode;
3739

3840
import javax.annotation.Nonnull;
3941
import java.util.ArrayList;
@@ -58,6 +60,7 @@
5860
* Tests for {@link RangeSet}.
5961
*/
6062
@Tag(Tags.RequiresFDB)
63+
@Execution(ExecutionMode.CONCURRENT)
6164
public class RangeSetTest {
6265
@RegisterExtension
6366
static final TestDatabaseExtension dbExtension = new TestDatabaseExtension();
@@ -116,7 +119,7 @@ private void checkIncreasing() {
116119
}
117120

118121
@BeforeEach
119-
void setUp() throws Exception {
122+
void setUp() {
120123
db = dbExtension.getDatabase();
121124
rsSubspace = rsSubspaceExtension.getSubspace();
122125
rs = new RangeSet(rsSubspace);

fdb-extensions/src/test/java/com/apple/foundationdb/async/RankedSetTest.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.apple.foundationdb.TransactionContext;
2828
import com.apple.foundationdb.subspace.Subspace;
2929
import com.apple.foundationdb.test.TestDatabaseExtension;
30+
import com.apple.foundationdb.test.TestExecutors;
3031
import com.apple.foundationdb.test.TestSubspaceExtension;
3132
import com.apple.foundationdb.tuple.ByteArrayUtil;
3233
import com.apple.foundationdb.tuple.Tuple;
@@ -35,12 +36,13 @@
3536
import org.junit.jupiter.api.Tag;
3637
import org.junit.jupiter.api.Test;
3738
import org.junit.jupiter.api.extension.RegisterExtension;
39+
import org.junit.jupiter.api.parallel.Execution;
40+
import org.junit.jupiter.api.parallel.ExecutionMode;
3841

3942
import java.util.ArrayList;
4043
import java.util.Collections;
4144
import java.util.List;
4245
import java.util.concurrent.CompletionException;
43-
import java.util.concurrent.ForkJoinPool;
4446
import java.util.concurrent.ThreadLocalRandom;
4547

4648
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -54,6 +56,7 @@
5456
* Tests for {@link RankedSet}.
5557
*/
5658
@Tag(Tags.RequiresFDB)
59+
@Execution(ExecutionMode.CONCURRENT)
5760
public class RankedSetTest {
5861
@RegisterExtension
5962
static final TestDatabaseExtension dbExtension = new TestDatabaseExtension();
@@ -288,7 +291,7 @@ public void rankAsThoughPresent() {
288291
//
289292

290293
private RankedSet newRankedSet() {
291-
RankedSet result = new RankedSet(rsSubspace, ForkJoinPool.commonPool(), config);
294+
RankedSet result = new RankedSet(rsSubspace, TestExecutors.defaultThreadPool(), config);
292295
result.init(db).join();
293296
return result;
294297
}

fdb-extensions/src/test/java/com/apple/foundationdb/async/rtree/RTreeModificationTest.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.apple.foundationdb.async.AsyncUtil;
2727
import com.apple.foundationdb.subspace.Subspace;
2828
import com.apple.foundationdb.test.TestDatabaseExtension;
29+
import com.apple.foundationdb.test.TestExecutors;
2930
import com.apple.foundationdb.test.TestSubspaceExtension;
3031
import com.apple.foundationdb.tuple.Tuple;
3132
import com.apple.test.Tags;
@@ -35,6 +36,8 @@
3536
import org.junit.jupiter.api.Tag;
3637
import org.junit.jupiter.api.Test;
3738
import org.junit.jupiter.api.extension.RegisterExtension;
39+
import org.junit.jupiter.api.parallel.Execution;
40+
import org.junit.jupiter.api.parallel.ExecutionMode;
3841
import org.junit.jupiter.params.ParameterizedTest;
3942
import org.junit.jupiter.params.provider.Arguments;
4043
import org.junit.jupiter.params.provider.MethodSource;
@@ -45,7 +48,6 @@
4548
import java.util.List;
4649
import java.util.Random;
4750
import java.util.concurrent.CompletableFuture;
48-
import java.util.concurrent.ForkJoinPool;
4951
import java.util.concurrent.atomic.AtomicLong;
5052
import java.util.stream.Stream;
5153

@@ -54,15 +56,14 @@
5456
*/
5557
@Tag(Tags.RequiresFDB)
5658
@Tag(Tags.Slow)
59+
@Execution(ExecutionMode.CONCURRENT)
5760
public class RTreeModificationTest {
5861
private static final Logger logger = LoggerFactory.getLogger(RTreeModificationTest.class);
59-
private static final boolean TRACE = false;
60-
6162
private static final int NUM_TEST_RUNS = 5;
6263
private static final int NUM_SAMPLES = 10_000;
6364

6465
@RegisterExtension
65-
static final TestDatabaseExtension dbExtension = new TestDatabaseExtension(TRACE);
66+
static final TestDatabaseExtension dbExtension = new TestDatabaseExtension();
6667
@RegisterExtension
6768
TestSubspaceExtension rtSubspace = new TestSubspaceExtension(dbExtension);
6869
@RegisterExtension
@@ -81,7 +82,7 @@ public void testAllDeleted(final RTree.Config config, final long seed, final int
8182
final RTreeScanTest.OnWriteCounters onWriteCounters = new RTreeScanTest.OnWriteCounters();
8283
final RTreeScanTest.OnReadCounters onReadCounters = new RTreeScanTest.OnReadCounters();
8384

84-
final RTree rTree = new RTree(rtSubspace.getSubspace(), rtSecondarySubspace.getSubspace(), ForkJoinPool.commonPool(), config,
85+
final RTree rTree = new RTree(rtSubspace.getSubspace(), rtSecondarySubspace.getSubspace(), TestExecutors.defaultThreadPool(), config,
8586
RTreeHilbertCurveHelpers::hilbertValue, NodeHelpers::newSequentialNodeId, onWriteCounters,
8687
onReadCounters);
8788
final long startTs = System.nanoTime();
@@ -139,7 +140,7 @@ public void testAllDeleted(final RTree.Config config, final long seed, final int
139140
@MethodSource("numSamplesAndNumDeletes")
140141
public void testRandomDeletes(@Nonnull final RTree.Config config, final long seed, final int numSamples, final int numDeletes) {
141142
final RTreeScanTest.OnReadCounters onReadCounters = new RTreeScanTest.OnReadCounters();
142-
final RTree rTree = new RTree(rtSubspace.getSubspace(), rtSecondarySubspace.getSubspace(), ForkJoinPool.commonPool(), config,
143+
final RTree rTree = new RTree(rtSubspace.getSubspace(), rtSecondarySubspace.getSubspace(), TestExecutors.defaultThreadPool(), config,
143144
RTreeHilbertCurveHelpers::hilbertValue, NodeHelpers::newSequentialNodeId, OnWriteListener.NOOP,
144145
onReadCounters);
145146
final Item[] items = randomInserts(db, rTree, seed, numSamples);
@@ -201,7 +202,7 @@ public <T extends Node> CompletableFuture<T> onAsyncRead(@Nonnull final Completa
201202
}
202203
};
203204

204-
final RTree rTree = new RTree(rtSubspace.getSubspace(), rtSecondarySubspace.getSubspace(), ForkJoinPool.commonPool(),
205+
final RTree rTree = new RTree(rtSubspace.getSubspace(), rtSecondarySubspace.getSubspace(), TestExecutors.defaultThreadPool(),
205206
new RTree.ConfigBuilder().build(),
206207
RTreeHilbertCurveHelpers::hilbertValue, NodeHelpers::newSequentialNodeId, OnWriteListener.NOOP,
207208
onReadListener);

fdb-extensions/src/test/java/com/apple/foundationdb/async/rtree/RTreeScanTest.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.apple.foundationdb.async.rtree.RTreeModificationTest.Item;
2828
import com.apple.foundationdb.subspace.Subspace;
2929
import com.apple.foundationdb.test.TestDatabaseExtension;
30+
import com.apple.foundationdb.test.TestExecutors;
3031
import com.apple.foundationdb.test.TestSubspaceExtension;
3132
import com.apple.foundationdb.tuple.Tuple;
3233
import com.apple.test.Tags;
@@ -40,6 +41,8 @@
4041
import org.junit.jupiter.api.BeforeAll;
4142
import org.junit.jupiter.api.Tag;
4243
import org.junit.jupiter.api.extension.RegisterExtension;
44+
import org.junit.jupiter.api.parallel.Execution;
45+
import org.junit.jupiter.api.parallel.ExecutionMode;
4346
import org.junit.jupiter.params.ParameterizedTest;
4447
import org.junit.jupiter.params.provider.Arguments;
4548
import org.junit.jupiter.params.provider.MethodSource;
@@ -54,7 +57,6 @@
5457
import java.util.Queue;
5558
import java.util.Random;
5659
import java.util.concurrent.CompletableFuture;
57-
import java.util.concurrent.ForkJoinPool;
5860
import java.util.concurrent.atomic.AtomicLong;
5961
import java.util.function.Predicate;
6062
import java.util.stream.Stream;
@@ -64,14 +66,14 @@
6466
*/
6567
@Tag(Tags.RequiresFDB)
6668
@Tag(Tags.Slow)
69+
@Execution(ExecutionMode.CONCURRENT)
6770
public class RTreeScanTest {
6871
private static final Logger logger = LoggerFactory.getLogger(RTreeScanTest.class);
69-
private static final boolean TRACE = false;
7072

7173
private static final int NUM_SAMPLES = 10_000;
7274
private static final int NUM_QUERIES = 100;
7375
@RegisterExtension
74-
static TestDatabaseExtension dbExtension = new TestDatabaseExtension(TRACE);
76+
static TestDatabaseExtension dbExtension = new TestDatabaseExtension();
7577

7678
private static Database db;
7779
private static Subspace rtSubspace;
@@ -84,7 +86,7 @@ public static void setUpDb() {
8486
db = dbExtension.getDatabase();
8587
rtSubspace = new TestSubspaceExtension(dbExtension).getSubspace();
8688
rtSecondarySubspace = new TestSubspaceExtension(dbExtension).getSubspace();
87-
final RTree rTree = new RTree(rtSubspace, rtSecondarySubspace, ForkJoinPool.commonPool(), RTree.DEFAULT_CONFIG,
89+
final RTree rTree = new RTree(rtSubspace, rtSecondarySubspace, TestExecutors.defaultThreadPool(), RTree.DEFAULT_CONFIG,
8890
RTreeHilbertCurveHelpers::hilbertValue, NodeHelpers::newSequentialNodeId, OnWriteListener.NOOP,
8991
OnReadListener.NOOP);
9092
final Item[] items1 = RTreeModificationTest.randomInsertsWithNulls(db, rTree, 0L, NUM_SAMPLES / 2);
@@ -158,7 +160,7 @@ public void queryWithFilters(@Nonnull final RTree.Rectangle query) {
158160

159161
final OnReadCounters onReadCounters = new OnReadCounters();
160162

161-
final RTree rt = new RTree(rtSubspace, rtSecondarySubspace, ForkJoinPool.commonPool(), RTree.DEFAULT_CONFIG,
163+
final RTree rt = new RTree(rtSubspace, rtSecondarySubspace, TestExecutors.defaultThreadPool(), RTree.DEFAULT_CONFIG,
162164
RTreeHilbertCurveHelpers::hilbertValue, NodeHelpers::newSequentialNodeId, OnWriteListener.NOOP,
163165
onReadCounters);
164166

@@ -206,7 +208,7 @@ public void queryTopNWithFilters(@Nonnull final RTree.Rectangle query) {
206208
}
207209
}
208210
final OnReadCounters onReadCounters = new OnReadCounters();
209-
final RTree rt = new RTree(rtSubspace, rtSecondarySubspace, ForkJoinPool.commonPool(), RTree.DEFAULT_CONFIG,
211+
final RTree rt = new RTree(rtSubspace, rtSecondarySubspace, TestExecutors.defaultThreadPool(), RTree.DEFAULT_CONFIG,
210212
RTreeHilbertCurveHelpers::hilbertValue, NodeHelpers::newSequentialNodeId, OnWriteListener.NOOP,
211213
onReadCounters);
212214
final AtomicLong nresults = new AtomicLong(0L);

fdb-extensions/src/test/java/com/apple/foundationdb/test/TestDatabaseExtension.java

+26-15
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.junit.jupiter.api.extension.ExtensionContext;
2929

3030
import javax.annotation.Nonnull;
31+
import javax.annotation.Nullable;
32+
import java.util.Objects;
3133

3234
/**
3335
* Test extension to use to get a connection to an FDB {@link Database}. This handles setting up the FDB
@@ -48,16 +50,14 @@ public class TestDatabaseExtension implements BeforeAllCallback, AfterAllCallbac
4850
private static final int MIN_API_VERSION = 630;
4951
private static final int MAX_API_VERSION = 710;
5052
private static final String API_VERSION_PROPERTY = "com.apple.foundationdb.apiVersion";
53+
private static final boolean TRACE = false;
54+
55+
@Nullable
56+
private static volatile FDB fdb;
5157

52-
private final boolean trace;
5358
private Database db;
5459

5560
public TestDatabaseExtension() {
56-
this(false);
57-
}
58-
59-
public TestDatabaseExtension(boolean trace) {
60-
this.trace = trace;
6161
}
6262

6363
public static int getAPIVersion() {
@@ -69,17 +69,28 @@ public static int getAPIVersion() {
6969
return apiVersion;
7070
}
7171

72-
@Override
73-
public void beforeAll(final ExtensionContext extensionContext) {
74-
if (!FDB.isAPIVersionSelected()) {
75-
FDB fdb = FDB.selectAPIVersion(getAPIVersion());
76-
if (trace) {
77-
NetworkOptions options = fdb.options();
78-
options.setTraceEnable("/tmp");
79-
options.setTraceLogGroup("fdb_extensions_tests");
72+
@Nonnull
73+
private static FDB getFDB() {
74+
if (fdb == null) {
75+
synchronized (TestDatabaseExtension.class) {
76+
if (fdb == null) {
77+
FDB inst = FDB.selectAPIVersion(getAPIVersion());
78+
if (TRACE) {
79+
NetworkOptions options = inst.options();
80+
options.setTraceEnable("/tmp");
81+
options.setTraceLogGroup("fdb_extensions_tests");
82+
}
83+
inst.setUnclosedWarning(true);
84+
fdb = inst;
85+
}
8086
}
81-
fdb.setUnclosedWarning(true);
8287
}
88+
return Objects.requireNonNull(fdb);
89+
}
90+
91+
@Override
92+
public void beforeAll(final ExtensionContext extensionContext) {
93+
getFDB();
8394
}
8495

8596
@Nonnull
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* TestExecutors.java
3+
*
4+
* This source file is part of the FoundationDB open source project
5+
*
6+
* Copyright 2015-2024 Apple Inc. and the FoundationDB project authors
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package com.apple.foundationdb.test;
22+
23+
import javax.annotation.Nonnull;
24+
import java.util.concurrent.Executor;
25+
import java.util.concurrent.Executors;
26+
import java.util.concurrent.ThreadFactory;
27+
import java.util.concurrent.atomic.AtomicInteger;
28+
29+
/**
30+
* Executors to use during testing.
31+
*/
32+
public class TestExecutors {
33+
/**
34+
* Thread factory for creating threads used by test thread pools.
35+
*/
36+
public static class TestThreadFactory implements ThreadFactory {
37+
private final String namePrefix;
38+
private final AtomicInteger count;
39+
40+
public TestThreadFactory(@Nonnull String namePrefix) {
41+
this.namePrefix = namePrefix;
42+
this.count = new AtomicInteger();
43+
}
44+
45+
@Override
46+
public Thread newThread(final Runnable r) {
47+
Thread t = new Thread(r);
48+
t.setDaemon(true);
49+
t.setName(namePrefix + "-" + count.incrementAndGet());
50+
return t;
51+
}
52+
}
53+
54+
@Nonnull
55+
private static final Executor DEFAULT_THREAD_POOL = newThreadPool("fdb-unit-test");
56+
57+
public static Executor newThreadPool(@Nonnull String namePrefix) {
58+
return Executors.newCachedThreadPool(new TestThreadFactory(namePrefix));
59+
}
60+
61+
@Nonnull
62+
public static Executor defaultThreadPool() {
63+
return DEFAULT_THREAD_POOL;
64+
}
65+
}

0 commit comments

Comments
 (0)