diff --git a/.gitignore b/.gitignore index 345009a61f..45e33f9ed3 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,7 @@ protogen/ # Local FDB settings fdb-environment.properties +fdb-environment.yaml # Docker local support run/ diff --git a/actions/setup-fdb/action.yml b/actions/setup-fdb/action.yml index e6b4c9054e..a2a438ce88 100644 --- a/actions/setup-fdb/action.yml +++ b/actions/setup-fdb/action.yml @@ -31,12 +31,57 @@ runs: - name: Install FDB Server shell: bash run: sudo dpkg -i ~/.fdb-cache/${{ steps.fdb_filenames.outputs.client_deb }} ~/.fdb-cache/${{ steps.fdb_filenames.outputs.server_deb }} - - name: Fix FDB Network Addresses + - name: Stop default fdb shell: bash - run: sudo sed -i -e "s/public_address = auto:\$ID/public_address = 127.0.0.1:\$ID/g" -e "s/listen_address = public/listen_address = 0.0.0.0:\$ID/g" /etc/foundationdb/foundationdb.conf - - name: Start FDB Server + run: sudo service foundationdb stop + + - name: Create cluster1 config + shell: bash + run: | + sudo cp /etc/foundationdb/foundationdb.conf /etc/foundationdb/foundationdb1.conf + + sudo sed -i -e "s/\/etc\/foundationdb\/fdb.cluster/\/etc\/foundationdb\/fdb1.cluster/g" \ + -e "s/\/var\/log\/foundationdb/\/var\/log\/foundationdb1/" \ + -e "s/fdbserver.4500/fdbserver.4600/g" \ + /etc/foundationdb/foundationdb1.conf + + sudo bash -c "echo 'fdb1:$(mktemp -u XXXXXXXX)@127.0.0.1:4600' > /etc/foundationdb/fdb1.cluster" + - name: Create cluster2 config shell: bash - run: sudo /usr/lib/foundationdb/fdbmonitor /etc/foundationdb/foundationdb.conf --daemonize - - name: Switch FDB to SSD + run: | + sudo cp /etc/foundationdb/foundationdb.conf /etc/foundationdb/foundationdb2.conf + + sudo sed -i -e "s/\/etc\/foundationdb\/fdb.cluster/\/etc\/foundationdb\/fdb2.cluster/g" \ + -e "s/\/var\/log\/foundationdb/\/var\/log\/foundationdb2/" \ + -e "s/fdbserver.4500/fdbserver.4700/g" \ + /etc/foundationdb/foundationdb2.conf + + sudo bash -c "echo 'fdb2:$(mktemp -u XXXXXXXX)@127.0.0.1:4700' > /etc/foundationdb/fdb2.cluster" + + - name: create dirs & set permissions shell: bash - run: fdbcli --exec "configure single ssd storage_migration_type=aggressive; status" + run: | + sudo mkdir /var/log/foundationdb1 /var/log/foundationdb2 + sudo chown foundationdb:foundationdb /etc/foundationdb/fdb1.cluster /etc/foundationdb/fdb2.cluster /var/log/foundationdb1 /var/log/foundationdb2 + sudo chmod 664 /etc/foundationdb/fdb1.cluster /etc/foundationdb/fdb2.cluster + sudo chmod 700 /var/log/foundationdb1 /var/log/foundationdb2 + + - name: Start FDB Server 1 + shell: bash + run: sudo /usr/lib/foundationdb/fdbmonitor --conffile /etc/foundationdb/foundationdb1.conf --lockfile /var/run/fdbmonitor1.pid --loggroup fdb1 --daemonize + - name: Start FDB Server 2 + shell: bash + run: sudo /usr/lib/foundationdb/fdbmonitor --conffile /etc/foundationdb/foundationdb2.conf --lockfile /var/run/fdbmonitor2.pid --loggroup fdb2 --daemonize + + - name: Switch FDB 1 to SSD + shell: bash + run: fdbcli -C /etc/foundationdb/fdb1.cluster --exec "configure new single ssd storage_migration_type=aggressive; status" + - name: Switch FDB 2 to SSD + shell: bash + run: fdbcli -C /etc/foundationdb/fdb2.cluster --exec "configure new single ssd storage_migration_type=aggressive; status" + - name: Create fdb-environment.yaml + shell: bash + run: | + echo "clusterFiles: " >> fdb-environment.yaml + echo " - /etc/foundationdb/fdb1.cluster" >> fdb-environment.yaml + echo " - /etc/foundationdb/fdb2.cluster" >> fdb-environment.yaml diff --git a/build.gradle b/build.gradle index 100972cd17..d4dc5c72ea 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,10 @@ * limitations under the License. */ +import org.yaml.snakeyaml.Yaml + +import org.apache.tools.ant.taskdefs.condition.Os + buildscript { repositories { if (Boolean.parseBoolean(mavenLocalEnabled)) { @@ -29,6 +33,7 @@ buildscript { dependencies { classpath 'org.jboss.tattletale:tattletale:1.2.0.Beta2' + classpath libs.snakeyaml } } @@ -341,6 +346,8 @@ if (!JavaVersion.current().isJava8Compatible()) { throw new Exception("Java 8 is required to build fdb-record-layer") } +// fdb-environment.properties is the old way we configured the library path and cluster file, it does not scale well +// to multiple cluster files, so is being replaced with a yaml file. def fdbEnvironmentFile = new File("${rootProject.projectDir}/fdb-environment.properties") if (fdbEnvironmentFile.exists()) { fdbEnvironmentFile.eachLine { line -> @@ -356,3 +363,15 @@ if (!ext.fdbEnvironment.isEmpty()) { } } } + +def fdbEnvironmentYamlFile = new File("${rootProject.projectDir}/fdb-environment.yaml") +if (fdbEnvironmentYamlFile.exists()) { + def libraryPath = new Yaml().loadAll(fdbEnvironmentYamlFile.newInputStream()).first().libraryPath + def libraryPathVariable = Os.isFamily(Os.FAMILY_MAC) ? "DYLD_LIBRARY_PATH" : "LD_LIBRARY_PATH" + allprojects { + tasks.withType(Test) { task -> + task.environment([(libraryPathVariable): libraryPath]) + task.environment(FDB_ENVIRONMENT_YAML: fdbEnvironmentYamlFile.absolutePath) + } + } +} diff --git a/docker-local/start.sh b/docker-local/start.sh index bc7e87c4f9..82823d2483 100755 --- a/docker-local/start.sh +++ b/docker-local/start.sh @@ -95,6 +95,13 @@ FDB_CLUSTER_FILE=${FDB_CLUSTER_FILE} ${LIBRARY_PATH}=${RUNDIR} EOF +cat >${ROOTDIR}/fdb-environment.yaml < clusterFiles; + + static { + final String fdbEnvironment = System.getenv("FDB_ENVIRONMENT_YAML"); + + if (fdbEnvironment != null && !fdbEnvironment.isEmpty()) { + clusterFiles = List.copyOf(parseFDBEnvironmentYaml(fdbEnvironment)); + } else { + clusterFiles = Collections.singletonList(null); // List.of does not allow null + } + } + + @SuppressWarnings("unchecked") + private static List parseFDBEnvironmentYaml(final String fdbEnvironment) { + Yaml yaml = new Yaml(); + try (FileInputStream yamlInput = new FileInputStream(fdbEnvironment)) { + Object fdbConfig = yaml.load(yamlInput); + return (List)((Map)fdbConfig).get("clusterFiles"); + } catch (IOException e) { + throw new IllegalStateException("Could not read fdb-environment.yaml", e); + } catch (ClassCastException e) { + throw new IllegalStateException("Could not parse fdb environment file " + fdbEnvironment, e); + } + } + + @Nullable + public static String getClusterFile(int i) { + return clusterFiles.get(i); + } + + public static List allClusterFiles() { + return clusterFiles; + } + + public static String randomClusterFile() { + return clusterFiles.get(ThreadLocalRandom.current().nextInt(clusterFiles.size())); + } + + /** + * Marks the current test as skipped if there are not the desired number of clusters available to test against. + * @param desiredCount the number of clusters to test against + */ + public static void assumeClusterCount(final int desiredCount) { + if (desiredCount > 1) { + Assumptions.assumeThat(clusterFiles.size()).as("Cluster file count").isGreaterThanOrEqualTo(desiredCount); + } + } +} diff --git a/fdb-extensions/src/testFixtures/java/com/apple/foundationdb/test/TestDatabaseExtension.java b/fdb-extensions/src/testFixtures/java/com/apple/foundationdb/test/TestDatabaseExtension.java index e64b12e890..ed7bf340b6 100644 --- a/fdb-extensions/src/testFixtures/java/com/apple/foundationdb/test/TestDatabaseExtension.java +++ b/fdb-extensions/src/testFixtures/java/com/apple/foundationdb/test/TestDatabaseExtension.java @@ -95,7 +95,7 @@ public void beforeAll(final ExtensionContext extensionContext) { @Nonnull public Database getDatabase() { if (db == null) { - db = FDB.instance().open(); + db = FDB.instance().open(FDBTestEnvironment.randomClusterFile()); } return db; } diff --git a/fdb-record-layer-core/fdb-record-layer-core.gradle b/fdb-record-layer-core/fdb-record-layer-core.gradle index 463f2d581d..7662d12bcf 100644 --- a/fdb-record-layer-core/fdb-record-layer-core.gradle +++ b/fdb-record-layer-core/fdb-record-layer-core.gradle @@ -44,6 +44,7 @@ dependencies { testImplementation(libs.junit.platform) testImplementation(libs.protobuf.util) testCompileOnly(libs.spotbugs.annotations) + testImplementation(libs.snakeyaml) testAnnotationProcessor(libs.autoService) } diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/BlockingInAsyncDetectionTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/BlockingInAsyncDetectionTest.java index deb86e7aa3..a05141b7c6 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/BlockingInAsyncDetectionTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/BlockingInAsyncDetectionTest.java @@ -24,6 +24,7 @@ import com.apple.foundationdb.record.RecordCoreException; import com.apple.foundationdb.record.TestHelpers; import com.apple.foundationdb.record.test.FDBDatabaseExtension; +import com.apple.foundationdb.test.FDBTestEnvironment; import com.apple.test.Tags; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -57,7 +58,7 @@ void testBlockingInAsyncException() { // Make sure that we aren't holding on to previously created databases factory.clear(); - FDBDatabase database = factory.getDatabase(); + FDBDatabase database = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); assertEquals(BlockingInAsyncDetection.IGNORE_COMPLETE_EXCEPTION_BLOCKING, database.getBlockingInAsyncDetection()); assertThrows(BlockingInAsyncException.class, () -> callAsyncBlocking(database)); } @@ -68,7 +69,7 @@ void testBlockingInAsyncWarning() { factory.setBlockingInAsyncDetection(BlockingInAsyncDetection.IGNORE_COMPLETE_WARN_BLOCKING); factory.clear(); - FDBDatabase database = factory.getDatabase(); + FDBDatabase database = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); TestHelpers.assertLogs(FDBDatabase.class, FDBDatabase.BLOCKING_IN_ASYNC_CONTEXT_MESSAGE, () -> { callAsyncBlocking(database, true); @@ -82,7 +83,7 @@ void testCompletedBlockingInAsyncWarning() { factory.setBlockingInAsyncDetection(BlockingInAsyncDetection.WARN_COMPLETE_EXCEPTION_BLOCKING); factory.clear(); - FDBDatabase database = factory.getDatabase(); + FDBDatabase database = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); TestHelpers.assertLogs(FDBDatabase.class, FDBDatabase.BLOCKING_IN_ASYNC_CONTEXT_MESSAGE, () -> database.asyncToSync(new FDBStoreTimer(), FDBStoreTimer.Waits.WAIT_ERROR_CHECK, CompletableFuture.supplyAsync(() -> @@ -95,7 +96,7 @@ void testBlockingCreatingAsyncDetection() { factory.setBlockingInAsyncDetection(BlockingInAsyncDetection.WARN_COMPLETE_EXCEPTION_BLOCKING); factory.clear(); - FDBDatabase database = factory.getDatabase(); + FDBDatabase database = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); TestHelpers.assertLogs(FDBDatabase.class, FDBDatabase.BLOCKING_RETURNING_ASYNC_MESSAGE, () -> returnAnAsync(database, MoreAsyncUtil.delayedFuture(200L, TimeUnit.MILLISECONDS, database.getScheduledExecutor()))); } @@ -106,7 +107,7 @@ void testCompletedBlockingCreatingAsyncDetection() { factory.setBlockingInAsyncDetection(BlockingInAsyncDetection.WARN_COMPLETE_EXCEPTION_BLOCKING); factory.clear(); - FDBDatabase database = factory.getDatabase(); + FDBDatabase database = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); TestHelpers.assertDidNotLog(FDBDatabase.class, FDBDatabase.BLOCKING_RETURNING_ASYNC_MESSAGE, () -> returnAnAsync(database, CompletableFuture.completedFuture(10L))); } diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBDatabaseTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBDatabaseTest.java index fd684a78d2..72ad1e4e65 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBDatabaseTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBDatabaseTest.java @@ -36,6 +36,7 @@ import com.apple.foundationdb.record.test.TestKeySpace; import com.apple.foundationdb.record.test.TestKeySpacePathManagerExtension; import com.apple.foundationdb.subspace.Subspace; +import com.apple.foundationdb.test.FDBTestEnvironment; import com.apple.foundationdb.tuple.Tuple; import com.apple.test.BooleanSource; import com.apple.test.Tags; @@ -54,6 +55,7 @@ import javax.annotation.Nonnull; import java.io.IOException; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -66,6 +68,7 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -240,7 +243,7 @@ void testJoinNowOnNonCompletedFuture(BlockingInAsyncDetection behavior) { if (behavior.throwExceptionOnBlocking()) { assertThrows(BlockingInAsyncException.class, () -> database.joinNow(new CompletableFuture<>())); } else { - FDBDatabase database2 = factory.getDatabase(); + FDBDatabase database2 = factory.getDatabase(database.getClusterFile()); TestHelpers.assertLogs(FDBDatabase.class, FDBDatabase.BLOCKING_FOR_FUTURE_MESSAGE, () -> { long val = database2.joinNow(MoreAsyncUtil.delayedFuture(100, TimeUnit.MILLISECONDS, database2.getScheduledExecutor()) .thenApply(vignore -> 1066L)); @@ -494,4 +497,31 @@ void cannotChangeAPIVersionAfterInit() { assertEquals(initApiVersion, database.getAPIVersion()); } } + + @Test + void canAccessMultipleClusters() { + FDBTestEnvironment.assumeClusterCount(2); + final FDBDatabase database0 = dbExtension.getDatabase(0); + final FDBDatabase database1 = dbExtension.getDatabase(1); + final byte[] key = Tuple.from(UUID.randomUUID()).pack(); + final byte[] value0 = Tuple.from("cluster0").pack(); + final byte[] value1 = Tuple.from("cluster1").pack(); + try (FDBRecordContext context0 = database0.openContext()) { + context0.ensureActive().set(key, value0); + context0.commit(); + } + try (FDBRecordContext context1 = database1.openContext()) { + assertNull(context1.ensureActive().get(key).join()); + context1.ensureActive().set(key, value1); + context1.commit(); + } + try (FDBRecordContext context0 = database0.openContext()) { + assertArrayEquals(value0, context0.ensureActive().get(key).join()); + context0.commit(); + } + try (FDBRecordContext context1 = database1.openContext()) { + assertArrayEquals(value1, context1.ensureActive().get(key).join()); + context1.commit(); + } + } } diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordContextTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordContextTest.java index f517382b6a..64c905dee8 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordContextTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordContextTest.java @@ -34,6 +34,7 @@ import com.apple.foundationdb.record.test.TestKeySpacePathManagerExtension; import com.apple.foundationdb.record.util.pair.Pair; import com.apple.foundationdb.subspace.Subspace; +import com.apple.foundationdb.test.FDBTestEnvironment; import com.apple.foundationdb.tuple.ByteArrayUtil; import com.apple.foundationdb.tuple.ByteArrayUtil2; import com.apple.foundationdb.tuple.Tuple; @@ -433,7 +434,7 @@ public void logWithoutSettingId() { FDBDatabaseFactory factory = fdb.getFactory(); factory.setTransactionIsTracedSupplier(() -> false); factory.clear(); - fdb = factory.getDatabase(); + fdb = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); } try (FDBRecordContext context = fdb.openContext()) { RecordCoreException err = assertThrows(RecordCoreException.class, context::logTransaction); @@ -665,7 +666,8 @@ public void contextExecutor() { factory.setContextExecutor(exec -> new ThreadIdRestoringExecutor(exec, myThreadId)); factory.clear(); - FDBDatabase database = factory.getDatabase(); + + FDBDatabase database = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); try (FDBRecordContext context = database.openContext()) { context.ensureActive().get(new byte[] { 0 }).thenAccept( value -> { assertEquals(myThreadId, ThreadId.get()); diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStorePerformanceTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStorePerformanceTest.java index fab42eb76d..acd969cb26 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStorePerformanceTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStorePerformanceTest.java @@ -38,6 +38,7 @@ import com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpacePath; import com.apple.foundationdb.record.test.FDBDatabaseExtension; import com.apple.foundationdb.record.test.TestKeySpace; +import com.apple.foundationdb.test.FDBTestEnvironment; import com.apple.foundationdb.tuple.Tuple; import com.apple.foundationdb.util.StringUtils; import com.apple.test.Tags; @@ -164,7 +165,7 @@ public void setup() { public void createMetaData() { FDBDatabaseFactory factory = dbExtension.getDatabaseFactory(); factory.setDirectoryCacheSize(databaseParameters.pathCache); - fdb = factory.getDatabase(); + fdb = factory.getDatabase(FDBTestEnvironment.randomClusterFile()); RecordMetaDataBuilder metaDataBuilder = RecordMetaData.newBuilder().setRecords(TestRecords1Proto.getDescriptor()); metaDataBuilder.setSplitLongRecords(databaseParameters.splitRecords); diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBReverseDirectoryCacheTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBReverseDirectoryCacheTest.java index 6aaafbeacb..5f8e48964a 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBReverseDirectoryCacheTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/FDBReverseDirectoryCacheTest.java @@ -34,6 +34,7 @@ import com.apple.foundationdb.record.test.FDBDatabaseExtension; import com.apple.foundationdb.record.test.TestKeySpace; import com.apple.foundationdb.record.test.TestKeySpacePathManagerExtension; +import com.apple.foundationdb.test.FDBTestEnvironment; import com.apple.foundationdb.record.util.pair.Pair; import com.apple.foundationdb.tuple.Tuple; import com.apple.test.Tags; @@ -387,8 +388,9 @@ public void testMapPathKeysConflict() throws Exception { public void testMapPathKeysConflictMultipleDatabaseObjects() throws Exception { // this is to simulate multiple VMs final int parallelism = 20; + final String clusterFile = FDBTestEnvironment.randomClusterFile(); testParallelReverseDirectoryCache(parallelism, true, - () -> new FDBDatabase(dbExtension.getDatabaseFactory(), null)); + () -> new FDBDatabase(dbExtension.getDatabaseFactory(), clusterFile)); } @Test @@ -404,8 +406,9 @@ public void testCacheInitConflict() throws Exception { public void testCacheInitConflictMultipleDatabaseObjects() throws Exception { // this is to simulate multiple VMs final int parallelism = 20; + final String clusterFile = FDBTestEnvironment.randomClusterFile(); testParallelReverseDirectoryCache(parallelism, false, - () -> new FDBDatabase(dbExtension.getDatabaseFactory(), null)); + () -> new FDBDatabase(dbExtension.getDatabaseFactory(), clusterFile)); } private void testParallelReverseDirectoryCache(int parallelism, boolean preInitReverseDirectoryCache, @@ -458,8 +461,8 @@ private void runParallelCodeOnEmptyDB(int parallelism, final Supplier blockingInAsyncDetectionSupplier = factory.getBlockingInAsyncDetectionSupplier(); try { factory.setBlockingInAsyncDetection(BlockingInAsyncDetection.DISABLED); - // Get a fresh new one - fdb = factory.getDatabase(); + // Get a fresh new one (but the same cluster as was wiped) + fdb = factory.getDatabase(fdb.getClusterFile()); final Executor executor = new ForkJoinPool(parallelism + 1); final Semaphore lock = new Semaphore(parallelism); @@ -590,7 +593,7 @@ public void testUniqueCachePerDatabase() throws Exception { factory.clear(); // Get a fresh new one - fdb = factory.getDatabase(); + fdb = factory.getDatabase(fdb.getClusterFile()); cache = fdb.getReverseDirectoryCache(); // In the hopes to ensure that re-creating the entries that we previously created diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java index 539cda3427..418bbd8777 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java @@ -93,7 +93,7 @@ import java.util.List; import java.util.Random; import java.util.Set; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletionException; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -135,6 +135,7 @@ import static com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScan; import static com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScanType; import static com.apple.foundationdb.record.query.plan.match.PlanMatchers.scoreForRank; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anyOf; @@ -745,34 +746,27 @@ void containsNullScore() throws Exception { } @DualPlannerTest - void writeOnlyRankQuery() { - assertThrows(RecordCoreException.class, () -> { - fdb = dbExtension.getDatabase(); - try (FDBRecordContext context = openContext()) { - openRecordStore(context); - recordStore.markIndexWriteOnly("rank_by_gender").join(); - - // Re-open to reload state. - openRecordStore(context); - final QueryComponent filter = - Query.rank(field("score").groupBy(field("gender"))).equalsValue(1L); - RecordQuery query = RecordQuery.newBuilder() - .setRecordType("BasicRankedRecord") - .setFilter(filter) - .build(); - RecordQueryPlan plan = recordStore.planQuery(query); - assertMatchesExactly(plan, - filterPlan( - typeFilterPlan( - scanPlan().where(scanComparisons(unbounded()))) - .where(recordTypes(containsAll(ImmutableSet.of("BasicRankedRecord"))))) - .where(queryComponents(exactly(equalsObject(filter))))); - recordStore.executeQuery(plan) - .map(rec -> TestRecordsRankProto.BasicRankedRecord.newBuilder().mergeFrom(rec.getRecord()).build()).asList().get(); - } catch (ExecutionException e) { - throw e.getCause(); - } - }); + void writeOnlyRankQuery() throws Exception { + try (FDBRecordContext context = openContext()) { + openRecordStore(context); + recordStore.markIndexWriteOnly("rank_by_gender").join(); + final QueryComponent filter = + Query.rank(field("score").groupBy(field("gender"))).equalsValue(1L); + RecordQuery query = RecordQuery.newBuilder() + .setRecordType("BasicRankedRecord") + .setFilter(filter) + .build(); + RecordQueryPlan plan = recordStore.planQuery(query); + assertMatchesExactly(plan, + filterPlan( + typeFilterPlan( + scanPlan().where(scanComparisons(unbounded()))) + .where(recordTypes(containsAll(ImmutableSet.of("BasicRankedRecord"))))) + .where(queryComponents(exactly(equalsObject(filter))))); + assertThatThrownBy(() -> recordStore.executeQuery(plan).asList().join(), "Plan: " + plan + " " ) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(RecordCoreException.class); + } } @Test diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/LocatableResolverTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/LocatableResolverTest.java index 0e87869918..1a0ca50288 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/LocatableResolverTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/LocatableResolverTest.java @@ -224,7 +224,7 @@ void testDirectoryCache() { FDBStoreTimer timer = new FDBStoreTimer(); - FDBDatabase fdb = factory.getDatabase(); + FDBDatabase fdb = factory.getDatabase(database.getClusterFile()); fdb.close(); // Make sure cache is fresh. String key = "world"; Long value; @@ -246,7 +246,7 @@ void testDirectoryCache() { @Test void testDirectoryCacheWithUncommittedContext() { - FDBDatabase fdb = dbExtension.getDatabase(); + FDBDatabase fdb = database; fdb.clearCaches(); // In the scoped directory layer test, this can conflict with initializing the reverse directory layer @@ -300,7 +300,7 @@ void testDirectoryCacheWithUncommittedContext() { @Test void testCachesWinnerOfConflict() { - FDBDatabase fdb = dbExtension.getDatabase(); + FDBDatabase fdb = database; fdb.clearCaches(); // In the scoped directory layer test, this can conflict with initializing the reverse directory layer @@ -350,7 +350,7 @@ void testCachesWinnerOfConflict() { */ @Test void testDoesNotCacheValueReadFromReadYourWritesCache() { - FDBDatabase fdb = dbExtension.getDatabase(); + FDBDatabase fdb = dbExtension.getDatabase(database.getClusterFile()); fdb.clearCaches(); final String key = "hello " + UUID.randomUUID(); @@ -397,7 +397,7 @@ void testResolveUseCacheCommits() { FDBStoreTimer timer = new FDBStoreTimer(); String key = "hello " + UUID.randomUUID(); - FDBDatabase fdb = factory.getDatabase(); + FDBDatabase fdb = factory.getDatabase(database.getClusterFile()); assertEquals(0, timer.getCount(FDBStoreTimer.Events.COMMIT)); try (FDBRecordContext context = fdb.openContext(null, timer)) { @@ -435,7 +435,7 @@ void testResolveCommitsWhenCacheEnabled() { Long baseline = database.getDirectoryCacheStats().hitCount(); Long reverseCacheBaseline = database.getReverseDirectoryInMemoryCache().stats().hitCount(); database.close(); - database = dbExtension.getDatabase(); + database = dbExtension.getDatabaseFactory().getDatabase(database.getClusterFile()); try (FDBRecordContext context = database.openContext()) { for (Map.Entry entry : mappings.entrySet()) { Long value = globalScope.resolve(context.getTimer(), entry.getKey()).join(); @@ -716,7 +716,7 @@ void testWriteLockCaching() { final FDBDatabaseFactory factory = dbExtension.getDatabaseFactory(); factory.clear(); - FDBDatabase newDatabase = factory.getDatabase(); + FDBDatabase newDatabase = factory.getDatabase(database.getClusterFile()); FDBStoreTimer timer2 = new FDBStoreTimer(); try (FDBRecordContext context = newDatabase.openContext(null, timer2)) { globalScope.resolve(context.getTimer(), "something").join(); @@ -896,7 +896,8 @@ void testParallelDbAndScopeGetVersion() { final FDBDatabaseFactory parallelFactory = new FDBDatabaseFactoryImpl(); parallelFactory.setStateRefreshTimeMillis(100); parallelFactory.setAPIVersion(dbExtension.getAPIVersion()); - Supplier databaseSupplier = () -> new FDBDatabase(parallelFactory, null); + String clusterFile = database.getClusterFile(); + Supplier databaseSupplier = () -> new FDBDatabase(parallelFactory, clusterFile); consistently("uninitialized version is 0", () -> { try (FDBRecordContext context = database.openContext()) { return globalScope.getVersion(context.getTimer()).join(); @@ -933,7 +934,7 @@ void testVersionIncrementInvalidatesCache() { FDBDatabaseFactory factory = dbExtension.getDatabaseFactory(); factory.setDirectoryCacheSize(10); final FDBStoreTimer timer = new FDBStoreTimer(); - FDBDatabase fdb = factory.getDatabase(); + FDBDatabase fdb = factory.getDatabase(database.getClusterFile()); fdb.close(); // Make sure cache is fresh, and resets version fdb.setResolverStateRefreshTimeMillis(100); String key = "some-key"; diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/ResolverMappingReplicatorTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/ResolverMappingReplicatorTest.java index e893b656ad..204806c7d1 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/ResolverMappingReplicatorTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/ResolverMappingReplicatorTest.java @@ -237,7 +237,7 @@ public void testDigestsMatch() { @Test public void testCopyInSameDatabase() { ResolverMappingReplicator replicator = new ResolverMappingReplicator(primary, 10); - FDBDatabase differentDB = new FDBDatabase(dbExtension.getDatabaseFactory(), null); + FDBDatabase differentDB = new FDBDatabase(dbExtension.getDatabaseFactory(), database.getClusterFile()); KeySpacePath path = basePath.add("to").add("replica"); try (FDBRecordContext context = differentDB.openContext()) { diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/test/FDBDatabaseExtension.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/test/FDBDatabaseExtension.java index 189e5a5649..0095f1de77 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/test/FDBDatabaseExtension.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/test/FDBDatabaseExtension.java @@ -26,24 +26,27 @@ import com.apple.foundationdb.record.provider.foundationdb.FDBDatabase; import com.apple.foundationdb.record.provider.foundationdb.FDBDatabaseFactory; import com.apple.foundationdb.record.provider.foundationdb.FDBDatabaseFactoryImpl; +import com.apple.foundationdb.test.FDBTestEnvironment; import com.apple.foundationdb.test.TestExecutors; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Extension that allows the user to specify the database. It ensures that FDB has been properly initialized * and that the {@link FDBDatabase} object used during a test does not leak between test runs. This registers - * call backs that run before and after tests, so it is suggested that users use the {@link org.junit.jupiter.api.extension.RegisterExtension} + * call backs that run before and after tests, so it is suggested that users use the {@link RegisterExtension} * annotation to ensure that those callbacks run. Like so: * *
{@code
@@ -65,8 +68,10 @@ public class FDBDatabaseExtension implements AfterEachCallback {
     private static volatile FDB fdb;
     @Nullable
     private FDBDatabaseFactory databaseFactory;
-    @Nullable
-    private FDBDatabase db;
+    @Nonnull
+    private final Map databases = new HashMap<>();
+    private String defaultClusterFile = FDBTestEnvironment.randomClusterFile();
+
 
     public FDBDatabaseExtension() {
     }
@@ -96,9 +101,11 @@ private static FDB getInitedFDB() {
                     }
                     baseFactory.setAPIVersion(getAPIVersion());
                     baseFactory.setUnclosedWarning(true);
-                    FDBDatabase unused = baseFactory.getDatabase();
-                    unused.performNoOp(); // make sure FDB gets opened
-                    unused.close();
+                    for (final String clusterFile : FDBTestEnvironment.allClusterFiles()) {
+                        FDBDatabase unused = baseFactory.getDatabase(clusterFile);
+                        unused.performNoOp(); // make sure FDB gets opened
+                        unused.close(); // FDBDatabase does not implement AutoCloseable
+                    }
                     fdb = FDB.instance();
                 }
             }
@@ -139,26 +146,41 @@ public FDBDatabaseFactory getDatabaseFactory() {
 
     @Nonnull
     public FDBDatabase getDatabase() {
-        if (db == null) {
-            db = getDatabaseFactory().getDatabase();
-        }
-        return db;
+        return getDatabase(defaultClusterFile);
+    }
+
+    public FDBDatabase getDatabase(int clusterIndex) {
+        return getDatabase(FDBTestEnvironment.getClusterFile(clusterIndex));
+    }
+
+    public FDBDatabase getDatabase(@Nullable String clusterFile) {
+        return databases.computeIfAbsent(Objects.requireNonNullElse(clusterFile, ""),
+                key -> {
+                    LOGGER.info("Connecting to cluster file: " + key);
+                    return getDatabaseFactory().getDatabase(key.isEmpty() ? null : key);
+                });
     }
 
     public void checkForOpenContexts() {
-        assertNotNull(db, "Should not check for open contexts on a null database");
-        assertEquals(0, db.warnAndCloseOldTrackedOpenContexts(0), "should not have left any contexts open");
+        for (final Map.Entry clusterFileToDatabase : databases.entrySet()) {
+            assertEquals(0, clusterFileToDatabase.getValue().warnAndCloseOldTrackedOpenContexts(0),
+                    clusterFileToDatabase.getKey() + " should not have left any contexts open");
+        }
     }
 
     @Override
     public void afterEach(final ExtensionContext extensionContext) {
-        if (db != null) {
-            // Validate that the test closes all the transactions that it opens
-            checkForOpenContexts();
-            db.close();
-            db = null;
+        // Validate that the test closes all the transactions that it opens
+        checkForOpenContexts();
+        for (final FDBDatabase database : databases.values()) {
+            database.close();
+        }
+        databases.clear();
+        if (databaseFactory != null) {
             getDatabaseFactory().clear();
             databaseFactory = null;
         }
+        // we don't do this in a beforeEach, in case a test is accessing the database in the constructor.
+        defaultClusterFile = FDBTestEnvironment.randomClusterFile();
     }
 }
diff --git a/fdb-record-layer-icu/fdb-record-layer-icu.gradle b/fdb-record-layer-icu/fdb-record-layer-icu.gradle
index 62e5b9d84e..22f40b2185 100644
--- a/fdb-record-layer-icu/fdb-record-layer-icu.gradle
+++ b/fdb-record-layer-icu/fdb-record-layer-icu.gradle
@@ -33,6 +33,7 @@ dependencies {
     testImplementation(libs.bundles.test.impl)
     testRuntimeOnly(libs.bundles.test.runtime)
     testCompileOnly(libs.bundles.test.compileOnly)
+    testImplementation(libs.snakeyaml)
 }
 
 apply from: rootProject.file('gradle/publishing.gradle')
diff --git a/fdb-record-layer-lucene/fdb-record-layer-lucene.gradle b/fdb-record-layer-lucene/fdb-record-layer-lucene.gradle
index c31ec46c4d..b3c95ba309 100644
--- a/fdb-record-layer-lucene/fdb-record-layer-lucene.gradle
+++ b/fdb-record-layer-lucene/fdb-record-layer-lucene.gradle
@@ -40,6 +40,7 @@ dependencies {
     testRuntimeOnly(libs.junit.vintange)
     testRuntimeOnly(libs.bundles.test.runtime)
     testCompileOnly(libs.bundles.test.compileOnly)
+    testImplementation(libs.snakeyaml)
     testAnnotationProcessor(libs.autoService)
 }
 
diff --git a/fdb-record-layer-lucene/src/test/java/com/apple/foundationdb/record/lucene/FDBLuceneQueryTest.java b/fdb-record-layer-lucene/src/test/java/com/apple/foundationdb/record/lucene/FDBLuceneQueryTest.java
index 16753095f1..1bafed5657 100644
--- a/fdb-record-layer-lucene/src/test/java/com/apple/foundationdb/record/lucene/FDBLuceneQueryTest.java
+++ b/fdb-record-layer-lucene/src/test/java/com/apple/foundationdb/record/lucene/FDBLuceneQueryTest.java
@@ -1373,7 +1373,7 @@ void testQueryWithManyDocuments() {
         factory.setExecutor(new ForkJoinPool(PARALLELISM,
                 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                 null, false));
-        factory.getDatabase().setAsyncToSyncTimeout(event -> {
+        fdb.setAsyncToSyncTimeout(event -> {
             // Make AsyncToSync calls timeout after one second, otherwise a deadlock would just result in the test taking forever
             return Duration.ofSeconds(1L);
         });
diff --git a/fdb-record-layer-spatial/fdb-record-layer-spatial.gradle b/fdb-record-layer-spatial/fdb-record-layer-spatial.gradle
index 99e81042d5..ecf728bc54 100644
--- a/fdb-record-layer-spatial/fdb-record-layer-spatial.gradle
+++ b/fdb-record-layer-spatial/fdb-record-layer-spatial.gradle
@@ -36,6 +36,7 @@ dependencies {
     testImplementation(libs.bundles.test.impl)
     testRuntimeOnly(libs.bundles.test.runtime)
     testCompileOnly(libs.bundles.test.compileOnly)
+    testImplementation(libs.snakeyaml)
     testImplementation testFixtures(project(':fdb-extensions'))
     testImplementation project(path: coreProject, configuration: 'tests')
 }
diff --git a/fdb-relational-cli/fdb-relational-cli.gradle b/fdb-relational-cli/fdb-relational-cli.gradle
index 6250a3d92b..ccb306681e 100644
--- a/fdb-relational-cli/fdb-relational-cli.gradle
+++ b/fdb-relational-cli/fdb-relational-cli.gradle
@@ -18,6 +18,8 @@
  * limitations under the License.
  */
 
+import org.yaml.snakeyaml.Yaml
+
 plugins {
     id 'application'
 }
@@ -74,6 +76,18 @@ dependencies {
     antlr(libs.antlr)
 }
 
+// We only set FDB_CLUSTER_FILE here, to help ensure that none of the other tests use it, but sqlline doesn't support
+// using a custom cluster file, so it has to come from the environment variable
+def fdbEnvironmentYamlFile = new File("${rootProject.projectDir}/fdb-environment.yaml")
+if (fdbEnvironmentYamlFile.exists()) {
+    def firstClusterFile = new Yaml().loadAll(fdbEnvironmentYamlFile.newInputStream()).first().clusterFiles.first()
+    allprojects {
+        tasks.withType(Test) { task ->
+            task.environment(FDB_CLUSTER_FILE: firstClusterFile)
+        }
+    }
+}
+
 jar {
     duplicatesStrategy = "exclude"
     manifest {
diff --git a/fdb-relational-core/fdb-relational-core.gradle b/fdb-relational-core/fdb-relational-core.gradle
index fa30929c6f..533c642fb3 100644
--- a/fdb-relational-core/fdb-relational-core.gradle
+++ b/fdb-relational-core/fdb-relational-core.gradle
@@ -47,11 +47,13 @@ dependencies {
     testImplementation(libs.bundles.test.runtime)
     testCompileOnly(libs.bundles.test.compileOnly)
 
+    testImplementation testFixtures(project(':fdb-extensions'))
     testImplementation(testFixtures(project(":fdb-relational-api")))
     testImplementation(libs.guava.testlib)
     testImplementation(libs.h2)
     testImplementation(libs.opencsv)
     testImplementation(libs.bndtools) // for one annotation used by log4j
+    testImplementation(libs.snakeyaml) // required for FDBTestEnvironment
 
     // Support for the fdb-relational-core test fixture.
     testFixturesImplementation(project(":fdb-relational-api"))
diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCEmbedDriverTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCEmbedDriverTest.java
index ace27ee9b7..9924ae453e 100644
--- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCEmbedDriverTest.java
+++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCEmbedDriverTest.java
@@ -23,6 +23,7 @@
 import com.apple.foundationdb.record.provider.foundationdb.APIVersion;
 import com.apple.foundationdb.record.provider.foundationdb.FDBDatabase;
 import com.apple.foundationdb.record.provider.foundationdb.FDBDatabaseFactory;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.apple.foundationdb.relational.api.EmbeddedRelationalDriver;
 import com.apple.foundationdb.relational.api.Options;
 import com.apple.foundationdb.relational.api.RelationalDriver;
@@ -73,7 +74,7 @@ public static void beforeAll() throws SQLException, RelationalException {
         // This needs to be done prior to the first call to factory.getDatabase()
         FDBDatabaseFactory.instance().setAPIVersion(APIVersion.API_VERSION_7_1);
 
-        final FDBDatabase database = FDBDatabaseFactory.instance().getDatabase();
+        final FDBDatabase database = FDBDatabaseFactory.instance().getDatabase(FDBTestEnvironment.randomClusterFile());
         StoreCatalog storeCatalog;
         try (var txn = new DirectFdbConnection(database).getTransactionManager().createTransaction(Options.NONE)) {
             storeCatalog = StoreCatalogProvider.getCatalog(txn, RelationalKeyspaceProvider.instance().getKeySpace());
diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/EmbeddedRelationalExtension.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/EmbeddedRelationalExtension.java
index ff2df9befa..36d959bcf8 100644
--- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/EmbeddedRelationalExtension.java
+++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/EmbeddedRelationalExtension.java
@@ -33,6 +33,7 @@
 import com.apple.foundationdb.relational.recordlayer.ddl.RecordLayerMetadataOperationsFactory;
 import com.apple.foundationdb.relational.recordlayer.query.cache.RelationalPlanCache;
 
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.codahale.metrics.MetricRegistry;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
@@ -94,7 +95,7 @@ private void setup() throws RelationalException, SQLException {
         // This needs to be done prior to the first call to factory.getDatabase()
         FDBDatabaseFactory.instance().setAPIVersion(APIVersion.API_VERSION_7_1);
 
-        final var database = FDBDatabaseFactory.instance().getDatabase();
+        final var database = FDBDatabaseFactory.instance().getDatabase(FDBTestEnvironment.randomClusterFile());
         final StoreCatalog storeCatalog;
         try (var connection = new DirectFdbConnection(database);
                 var txn = connection.getTransactionManager().createTransaction(Options.NONE)) {
diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/KeySpacePathParsingTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/KeySpacePathParsingTest.java
index 398aa2b554..045ff9a316 100644
--- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/KeySpacePathParsingTest.java
+++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/KeySpacePathParsingTest.java
@@ -24,6 +24,7 @@
 import com.apple.foundationdb.record.provider.foundationdb.FDBDatabaseFactory;
 import com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext;
 import com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.apple.foundationdb.record.provider.foundationdb.keyspace.DirectoryLayerDirectory;
 import com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpace;
 import com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpaceDirectory;
@@ -150,12 +151,13 @@ void testDirectoryLayer() throws RelationalException {
         Assertions.assertEquals(expected, uri, "Invalid parsing of URI or KeySpacePaths");
 
         // Assert all values for the keySpacePath are Long
-        FDBRecordContext context = FDBDatabaseFactory.instance().getDatabase().openContext();
+        String clusterFile = FDBTestEnvironment.randomClusterFile();
+        FDBRecordContext context = FDBDatabaseFactory.instance().getDatabase(clusterFile).openContext();
         List numbers1 = getResolvedValuesForKeySpacePath(path, context);
         numbers1.stream().forEach(n -> Assertions.assertTrue(n instanceof Long, "Unexpected value type"));
 
         // Read the resolved values again, and assert again
-        context = FDBDatabaseFactory.instance().getDatabase().openContext();
+        context = FDBDatabaseFactory.instance().getDatabase(clusterFile).openContext();
         List numbers2 = getResolvedValuesForKeySpacePath(path, context);
         numbers2.stream().forEach(n -> Assertions.assertTrue(n instanceof Long, "Unexpected value type"));
 
diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/CatalogMetaDataProviderTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/CatalogMetaDataProviderTest.java
index 374ae9cfb9..8d37d56cc4 100644
--- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/CatalogMetaDataProviderTest.java
+++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/CatalogMetaDataProviderTest.java
@@ -23,6 +23,7 @@
 import com.apple.foundationdb.record.RecordMetaData;
 import com.apple.foundationdb.record.RecordMetaDataProto;
 import com.apple.foundationdb.record.provider.foundationdb.FDBDatabaseFactory;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.apple.foundationdb.relational.api.Options;
 import com.apple.foundationdb.relational.api.Transaction;
 import com.apple.foundationdb.relational.api.catalog.StoreCatalog;
@@ -53,7 +54,7 @@ void canLoadMetaDataFromStore() throws RelationalException, Descriptors.Descript
 
         //now create a RecordStore in that Catalog
         FDBDatabaseFactory factory = FDBDatabaseFactory.instance();
-        FdbConnection fdbConn = new DirectFdbConnection(factory.getDatabase());
+        FdbConnection fdbConn = new DirectFdbConnection(factory.getDatabase(FDBTestEnvironment.randomClusterFile()));
         StoreCatalog storeCatalog;
         try (Transaction txn = fdbConn.getTransactionManager().createTransaction(Options.NONE)) {
             //create the Catalog RecordStore
diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogImplTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogImplTest.java
index cc4c873061..c0897dbc05 100644
--- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogImplTest.java
+++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogImplTest.java
@@ -24,6 +24,7 @@
 import com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext;
 import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStore;
 import com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpacePath;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.apple.foundationdb.relational.api.Transaction;
 import com.apple.foundationdb.relational.api.exceptions.ErrorCode;
 import com.apple.foundationdb.relational.api.exceptions.RelationalException;
@@ -45,7 +46,7 @@ public class RecordLayerStoreCatalogImplTest extends RecordLayerStoreCatalogTest
 
     @BeforeEach
     void setUpCatalog() throws RelationalException {
-        fdb = FDBDatabaseFactory.instance().getDatabase();
+        fdb = FDBDatabaseFactory.instance().getDatabase(FDBTestEnvironment.randomClusterFile());
         // create a FDBRecordStore
         try (Transaction txn = new RecordContextTransaction(fdb.openContext())) {
             storeCatalog = StoreCatalogProvider.getCatalog(txn, keySpace);
diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogWithNoTemplateOperationsTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogWithNoTemplateOperationsTest.java
index f6466f95be..a36f6bbc87 100644
--- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogWithNoTemplateOperationsTest.java
+++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/catalog/RecordLayerStoreCatalogWithNoTemplateOperationsTest.java
@@ -24,6 +24,7 @@
 import com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext;
 import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStore;
 import com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpacePath;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.apple.foundationdb.relational.api.Transaction;
 import com.apple.foundationdb.relational.api.exceptions.ErrorCode;
 import com.apple.foundationdb.relational.api.exceptions.RelationalException;
@@ -43,7 +44,7 @@ public class RecordLayerStoreCatalogWithNoTemplateOperationsTest extends RecordL
 
     @BeforeEach
     void setUpCatalog() throws RelationalException {
-        fdb = FDBDatabaseFactory.instance().getDatabase();
+        fdb = FDBDatabaseFactory.instance().getDatabase(FDBTestEnvironment.randomClusterFile());
         // create a FDBRecordStore
         try (Transaction txn = new RecordContextTransaction(fdb.openContext())) {
             storeCatalog = StoreCatalogProvider.getCatalogWithNoTemplateOperations(txn);
diff --git a/fdb-relational-jdbc/fdb-relational-jdbc.gradle b/fdb-relational-jdbc/fdb-relational-jdbc.gradle
index 3754f6ec51..5afc37609f 100644
--- a/fdb-relational-jdbc/fdb-relational-jdbc.gradle
+++ b/fdb-relational-jdbc/fdb-relational-jdbc.gradle
@@ -18,6 +18,8 @@
  * limitations under the License.
  */
 
+import org.yaml.snakeyaml.Yaml
+
 plugins {
     alias(libs.plugins.serviceloader)
     alias(libs.plugins.shadow)
@@ -47,6 +49,19 @@ dependencies {
     testImplementation(libs.grpc.testing)
 }
 
+// We only set FDB_CLUSTER_FILE here, to help ensure that none of the other tests use it, but these tests depend on
+// automatically creating an InProcessRelationalServer through reflection, and having that support some way of providing
+// the cluster file seems complicated at best, and counter-productive at worst
+def fdbEnvironmentYamlFile = new File("${rootProject.projectDir}/fdb-environment.yaml")
+if (fdbEnvironmentYamlFile.exists()) {
+    def firstClusterFile = new Yaml().loadAll(fdbEnvironmentYamlFile.newInputStream()).first().clusterFiles.first()
+    allprojects {
+        tasks.withType(Test) { task ->
+            task.environment(FDB_CLUSTER_FILE: firstClusterFile)
+        }
+    }
+}
+
 serviceLoader {
     serviceInterface 'java.sql.Driver'
 }
diff --git a/fdb-relational-server/fdb-relational-server.gradle b/fdb-relational-server/fdb-relational-server.gradle
index 93006e99ae..0efa90b517 100644
--- a/fdb-relational-server/fdb-relational-server.gradle
+++ b/fdb-relational-server/fdb-relational-server.gradle
@@ -112,10 +112,13 @@ dependencies {
     testFixturesImplementation(project(":fdb-java-annotations")) {
         exclude(group: "com.squareup", module: "javapoet")
     }
+    testFixturesImplementation testFixtures(project(':fdb-extensions'))
+    testFixturesImplementation(libs.snakeyaml) // required for FDBTestEnvironment
     testFixturesImplementation(libs.promethus.simpleclient)
     testFixturesImplementation(libs.log4j.api)
 
     testImplementation(libs.bundles.test.impl)
+    testImplementation testFixtures(project(':fdb-extensions'))
     testRuntimeOnly(libs.bundles.test.runtime)
     testCompileOnly(libs.bundles.test.compileOnly)
     testImplementation(libs.grpc.testing)
diff --git a/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/RelationalServer.java b/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/RelationalServer.java
index 5653040d9e..50856f5d95 100644
--- a/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/RelationalServer.java
+++ b/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/RelationalServer.java
@@ -83,13 +83,20 @@ public class RelationalServer implements Closeable {
     private final int httpPort;
     private FRL frl;
     private final CollectorRegistry collectorRegistry;
+    private final String clusterFile;
 
     // Visible for the test fixture only so it can pass a CollectorRegistry.
     @VisibleForTesting
     RelationalServer(int grpcPort, int httpPort, CollectorRegistry collectorRegistry) {
+        this(grpcPort, httpPort, collectorRegistry, null);
+    }
+
+    @VisibleForTesting
+    RelationalServer(int grpcPort, int httpPort, CollectorRegistry collectorRegistry, String clusterFile) {
         this.grpcPort = grpcPort;
         this.httpPort = httpPort;
         this.collectorRegistry = collectorRegistry;
+        this.clusterFile = clusterFile;
     }
 
     public RelationalServer(int grpcPort, int httpPort) {
@@ -131,7 +138,7 @@ RelationalServer start() throws IOException {
         // Create access to backing database.
         // TODO: Make this multi-query/-tenant/-database!
         try {
-            frl = new FRL();
+            frl = new FRL(com.apple.foundationdb.relational.api.Options.NONE, clusterFile);
         } catch (RelationalException ve) {
             throw new IOException(ve);
         }
diff --git a/fdb-relational-server/src/test/java/com/apple/foundationdb/relational/server/InProcessRelationalServerTest.java b/fdb-relational-server/src/test/java/com/apple/foundationdb/relational/server/InProcessRelationalServerTest.java
index 8e29e1687b..200fe1b82f 100644
--- a/fdb-relational-server/src/test/java/com/apple/foundationdb/relational/server/InProcessRelationalServerTest.java
+++ b/fdb-relational-server/src/test/java/com/apple/foundationdb/relational/server/InProcessRelationalServerTest.java
@@ -20,6 +20,7 @@
 
 package com.apple.foundationdb.relational.server;
 
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import io.grpc.ManagedChannel;
 import io.grpc.inprocess.InProcessChannelBuilder;
 import org.junit.jupiter.api.AfterAll;
@@ -36,7 +37,7 @@ public class InProcessRelationalServerTest {
 
     @BeforeAll
     public static void beforeAll() throws IOException {
-        server = new InProcessRelationalServer().start();
+        server = new InProcessRelationalServer(FDBTestEnvironment.randomClusterFile()).start();
     }
 
     @AfterAll
diff --git a/fdb-relational-server/src/testFixtures/java/com/apple/foundationdb/relational/server/ServerTestUtil.java b/fdb-relational-server/src/testFixtures/java/com/apple/foundationdb/relational/server/ServerTestUtil.java
index b1132ffea4..7d6dfaba65 100644
--- a/fdb-relational-server/src/testFixtures/java/com/apple/foundationdb/relational/server/ServerTestUtil.java
+++ b/fdb-relational-server/src/testFixtures/java/com/apple/foundationdb/relational/server/ServerTestUtil.java
@@ -21,13 +21,13 @@
 package com.apple.foundationdb.relational.server;
 
 import com.apple.foundationdb.annotation.API;
-
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import io.prometheus.client.CollectorRegistry;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import java.io.IOException;
 import java.net.BindException;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 /**
  * Stand up a server for downstream modules to use in test.
@@ -53,12 +53,22 @@ private ServerTestUtil() {
      * @return Return a started {@link RelationalServer}
      */
     public static RelationalServer createAndStartRelationalServer(int preferredPort) throws IOException {
+        return createAndStartRelationalServer(preferredPort, FDBTestEnvironment.randomClusterFile());
+    }
+
+    /**
+     * Create and start a RelationalServer with a specific cluster file.
+     * @param preferredPort The preferred port to start on
+     * @param clusterFile The cluster file to use for FDB connection
+     * @return Return a started {@link RelationalServer}
+     */
+    public static RelationalServer createAndStartRelationalServer(int preferredPort, String clusterFile) throws IOException {
         RelationalServer relationalServer = null;
         for (int port = preferredPort; port <= (preferredPort + PORT_RETRY_MAX); port += 2) {
             // Create a CollectorRegistry when a test fixture else "java.lang.IllegalArgumentException:
             // Collector already registered that provides name: grpc_server_started_total" when the second instance
             // of the test fixture runs. Otherwise, just use the default in VServer.
-            relationalServer = new RelationalServer(port, port + 1, new CollectorRegistry(true));
+            relationalServer = new RelationalServer(port, port + 1, new CollectorRegistry(true), clusterFile);
             try {
                 relationalServer.start();
                 // Successful start.
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java
index 1e2208ac99..558d7ec8fe 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java
@@ -47,15 +47,18 @@ public class SimpleYamlConnection implements YamlConnection {
     private final List versions;
     @Nonnull
     private final String connectionLabel;
+    @Nonnull
+    private final String clusterFile;
 
-    public SimpleYamlConnection(@Nonnull Connection connection, @Nonnull SemanticVersion version) throws SQLException {
-        this(connection, version, version.toString());
+    public SimpleYamlConnection(@Nonnull Connection connection, @Nonnull SemanticVersion version, @Nonnull String clusterFile) throws SQLException {
+        this(connection, version, version.toString(), clusterFile);
     }
 
-    public SimpleYamlConnection(@Nonnull Connection connection, @Nonnull SemanticVersion version, @Nonnull String connectionLabel) throws SQLException {
+    public SimpleYamlConnection(@Nonnull Connection connection, @Nonnull SemanticVersion version, @Nonnull String connectionLabel, @Nonnull String clusterFile) throws SQLException {
         underlying = connection.unwrap(RelationalConnection.class);
         this.versions = List.of(version);
         this.connectionLabel = connectionLabel;
+        this.clusterFile = clusterFile;
     }
 
     @Nonnull
@@ -138,6 +141,11 @@ public  T executeTransactionally(final SQLFunction transac
         return result;
     }
 
+    @Override
+    public String getClusterFile() {
+        return clusterFile;
+    }
+
     @Override
     public String toString() {
         return connectionLabel;
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java
index e380edaacc..b041daf837 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java
@@ -108,4 +108,11 @@ public interface YamlConnection extends AutoCloseable {
     SemanticVersion getInitialVersion();
 
      T executeTransactionally(SQLFunction transactionalWork) throws SQLException, RelationalException;
+
+    /**
+     * The Cluster File that this connection is connected to, so that other connections can point to the same
+     * FDB instance.
+     * @return the cluster file for the underlying FDB cluster
+     */
+    String getClusterFile();
 }
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlTestExtension.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlTestExtension.java
index d8a0522647..974db4ccba 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlTestExtension.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlTestExtension.java
@@ -31,6 +31,7 @@
 import com.apple.foundationdb.relational.yamltests.configs.ShowPlanOnDiff;
 import com.apple.foundationdb.relational.yamltests.configs.YamlTestConfig;
 import com.apple.foundationdb.relational.yamltests.server.ExternalServer;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import com.google.common.collect.Iterables;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -69,8 +70,9 @@ public class YamlTestExtension implements TestTemplateInvocationContextProvider,
     private final String clusterFile;
     private final boolean includeMethodInDescriptions;
 
+    @SuppressWarnings("unused") // Used implicitly with @ExtendWith(YamlTestExtension.class)
     public YamlTestExtension() {
-        this(null, false);
+        this(FDBTestEnvironment.randomClusterFile(), false);
     }
 
     /**
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/Command.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/Command.java
index 5f5dfcb24c..de36ec9390 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/Command.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/Command.java
@@ -41,11 +41,11 @@
 import com.apple.foundationdb.relational.yamltests.YamlConnection;
 import com.apple.foundationdb.relational.yamltests.YamlExecutionContext;
 import com.apple.foundationdb.relational.yamltests.generated.schemainstance.SchemaInstanceOuterClass;
-
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import java.net.URI;
 import java.sql.SQLException;
 import java.util.Locale;
@@ -126,8 +126,9 @@ private static void applyMetadataOperationEmbedded(@Nonnull EmbeddedRelationalCo
         connection.setAutoCommit(true);
     }
 
-    private static void applyMetadataOperationDirectly(@Nonnull RecordLayerConfig rlConfig, @Nonnull ApplyState applyState) throws RelationalException {
-        final FDBDatabase fdbDb = FDBDatabaseFactory.instance().getDatabase();
+    private static void applyMetadataOperationDirectly(@Nonnull RecordLayerConfig rlConfig, @Nonnull ApplyState applyState,
+                                                       @Nullable String clusterFile) throws RelationalException {
+        final FDBDatabase fdbDb = FDBDatabaseFactory.instance().getDatabase(clusterFile);
         final RelationalKeyspaceProvider keyspaceProvider = RelationalKeyspaceProvider.instance();
         keyspaceProvider.registerDomainIfNotExists("FRL");
         KeySpace keySpace = keyspaceProvider.getKeySpace();
@@ -164,7 +165,7 @@ public void executeInternal(@Nonnull YamlConnection connection) throws SQLExcept
                 if (embedded != null) {
                     Command.applyMetadataOperationEmbedded(embedded, RecordLayerConfig.getDefault(), applyState);
                 } else {
-                    Command.applyMetadataOperationDirectly(RecordLayerConfig.getDefault(), applyState);
+                    Command.applyMetadataOperationDirectly(RecordLayerConfig.getDefault(), applyState, connection.getClusterFile());
                 }
             }
         };
@@ -189,7 +190,7 @@ public void executeInternal(@Nonnull YamlConnection connection) throws SQLExcept
                 if (embedded != null) {
                     Command.applyMetadataOperationEmbedded(embedded, rlConfig, applyState);
                 } else {
-                    Command.applyMetadataOperationDirectly(rlConfig, applyState);
+                    Command.applyMetadataOperationDirectly(rlConfig, applyState, connection.getClusterFile());
                 }
             }
         };
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/EmbeddedConfig.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/EmbeddedConfig.java
index f07f3565bb..36f1fa6c5e 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/EmbeddedConfig.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/EmbeddedConfig.java
@@ -37,10 +37,6 @@ public class EmbeddedConfig implements YamlTestConfig {
     @Nullable
     private final String clusterFile;
 
-    public EmbeddedConfig() {
-        this(null);
-    }
-
     public EmbeddedConfig(@Nullable final String clusterFile) {
         this.clusterFile = clusterFile;
     }
@@ -66,7 +62,7 @@ public void afterAll() throws Exception {
 
     @Override
     public YamlConnectionFactory createConnectionFactory() {
-        return new EmbeddedYamlConnectionFactory();
+        return new EmbeddedYamlConnectionFactory(clusterFile);
     }
 
     @Override
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/JDBCInProcessConfig.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/JDBCInProcessConfig.java
index c0b4275484..ed0f217b4d 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/JDBCInProcessConfig.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/JDBCInProcessConfig.java
@@ -64,7 +64,7 @@ public void afterAll() throws Exception {
 
     @Override
     public YamlConnectionFactory createConnectionFactory() {
-        return new JDBCInProcessYamlConnectionFactory(server);
+        return new JDBCInProcessYamlConnectionFactory(server, clusterFile);
     }
 
     @Nonnull
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/EmbeddedYamlConnectionFactory.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/EmbeddedYamlConnectionFactory.java
index 276839e89c..bf9b19674c 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/EmbeddedYamlConnectionFactory.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/EmbeddedYamlConnectionFactory.java
@@ -32,10 +32,16 @@
 import java.util.Set;
 
 public class EmbeddedYamlConnectionFactory implements YamlConnectionFactory {
+    private final String clusterFile;
+
+    public EmbeddedYamlConnectionFactory(String clusterFile) {
+        this.clusterFile = clusterFile;
+    }
+
     @Override
     public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
         return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()),
-                SemanticVersion.current(), "Embedded");
+                SemanticVersion.current(), "Embedded", clusterFile);
     }
 
     @Override
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/ExternalServerYamlConnectionFactory.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/ExternalServerYamlConnectionFactory.java
index 4583bd6dcc..1e0d565220 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/ExternalServerYamlConnectionFactory.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/ExternalServerYamlConnectionFactory.java
@@ -46,7 +46,7 @@ public ExternalServerYamlConnectionFactory(final ExternalServer externalServer)
     public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
         String uriStr = connectPath.toString().replaceFirst("embed:", "relational://localhost:" + externalServer.getPort());
         LOG.info("Rewrote {} as {}", connectPath, uriStr);
-        return new SimpleYamlConnection(DriverManager.getConnection(uriStr), externalServer.getVersion());
+        return new SimpleYamlConnection(DriverManager.getConnection(uriStr), externalServer.getVersion(), externalServer.getClusterFile());
     }
 
     @Override
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/JDBCInProcessYamlConnectionFactory.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/JDBCInProcessYamlConnectionFactory.java
index 47c3f0b5aa..1dac77ebdf 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/JDBCInProcessYamlConnectionFactory.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/JDBCInProcessYamlConnectionFactory.java
@@ -38,9 +38,11 @@
 public class JDBCInProcessYamlConnectionFactory implements YamlConnectionFactory {
     private static final Logger LOG = LogManager.getLogger(JDBCInProcessYamlConnectionFactory.class);
     private final InProcessRelationalServer server;
+    private final String clusterFile;
 
-    public JDBCInProcessYamlConnectionFactory(final InProcessRelationalServer server) {
+    public JDBCInProcessYamlConnectionFactory(final InProcessRelationalServer server, final String clusterFile) {
         this.server = server;
+        this.clusterFile = clusterFile;
     }
 
     @Override
@@ -49,7 +51,7 @@ public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLExcep
         URI connectPathPlusServerName = JDBCURI.addQueryParameter(connectPath, JDBCURI.INPROCESS_URI_QUERY_SERVERNAME_KEY, server.getServerName());
         String uriStr = connectPathPlusServerName.toString().replaceFirst("embed:", "relational://");
         LOG.info("Rewrote {} as {}", connectPath, uriStr);
-        return new SimpleYamlConnection(DriverManager.getConnection(uriStr), SemanticVersion.current(), "JDBC In-Process");
+        return new SimpleYamlConnection(DriverManager.getConnection(uriStr), SemanticVersion.current(), "JDBC In-Process", clusterFile);
     }
 
     @Override
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java
index 31f21efbf4..c0e1801ca4 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java
@@ -231,6 +231,12 @@ public boolean supportsMetricCollector() {
             return false;
         }
 
+        @Override
+        public String getClusterFile() {
+            // All underlying connections should be using the same cluster file
+            return underlyingConnections.get(0).getClusterFile();
+        }
+
         /**
          * Get the connection to send requests to.
          * This method conditionally advances the connection selector. This is done for ease of testing, where some
diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/server/ExternalServer.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/server/ExternalServer.java
index 76413c49c5..9e2c394136 100644
--- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/server/ExternalServer.java
+++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/server/ExternalServer.java
@@ -105,6 +105,10 @@ public SemanticVersion getVersion() {
         return version;
     }
 
+    public String getClusterFile() {
+        return clusterFile;
+    }
+
     public void start() throws Exception {
         grpcPort = getAvailablePort(-1);
         final int httpPort = getAvailablePort(grpcPort);
diff --git a/yaml-tests/src/test/java/InitialVersionTest.java b/yaml-tests/src/test/java/InitialVersionTest.java
index 84f85fae23..8e43dec66c 100644
--- a/yaml-tests/src/test/java/InitialVersionTest.java
+++ b/yaml-tests/src/test/java/InitialVersionTest.java
@@ -25,6 +25,7 @@
 import com.apple.foundationdb.relational.yamltests.YamlRunner;
 import com.apple.foundationdb.relational.yamltests.configs.EmbeddedConfig;
 import com.apple.foundationdb.relational.yamltests.server.SemanticVersion;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -44,7 +45,8 @@
  */
 public class InitialVersionTest {
     private static final SemanticVersion VERSION = SemanticVersion.parse("3.0.18.0");
-    private static final EmbeddedConfig config = new EmbeddedConfig(null);
+    private static final String CLUSTER_FILE = FDBTestEnvironment.randomClusterFile();
+    private static final EmbeddedConfig config = new EmbeddedConfig(CLUSTER_FILE);
 
     @BeforeAll
     static void beforeAll() throws Exception {
@@ -72,7 +74,7 @@ YamlConnectionFactory createConnectionFactory() {
         return new YamlConnectionFactory() {
             @Override
             public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
-                return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION);
+                return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION, CLUSTER_FILE);
             }
 
             @Override
diff --git a/yaml-tests/src/test/java/MultiServerConnectionFactoryTest.java b/yaml-tests/src/test/java/MultiServerConnectionFactoryTest.java
index 882a533d98..b1ee176d1d 100644
--- a/yaml-tests/src/test/java/MultiServerConnectionFactoryTest.java
+++ b/yaml-tests/src/test/java/MultiServerConnectionFactoryTest.java
@@ -20,11 +20,12 @@
 
 import com.apple.foundationdb.relational.api.RelationalConnection;
 import com.apple.foundationdb.relational.api.RelationalPreparedStatement;
-import com.apple.foundationdb.relational.yamltests.connectionfactory.MultiServerConnectionFactory;
 import com.apple.foundationdb.relational.yamltests.SimpleYamlConnection;
 import com.apple.foundationdb.relational.yamltests.YamlConnection;
 import com.apple.foundationdb.relational.yamltests.YamlConnectionFactory;
+import com.apple.foundationdb.relational.yamltests.connectionfactory.MultiServerConnectionFactory;
 import com.apple.foundationdb.relational.yamltests.server.SemanticVersion;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
@@ -42,6 +43,7 @@
 public class MultiServerConnectionFactoryTest {
     private static final SemanticVersion PRIMARY_VERSION = SemanticVersion.parse("2.2.2.0");
     private static final SemanticVersion ALTERNATE_VERSION = SemanticVersion.parse("1.1.1.0");
+    private static final String CLUSTER_FILE = FDBTestEnvironment.randomClusterFile();
 
     @ParameterizedTest
     @CsvSource({"0", "1"})
@@ -156,7 +158,7 @@ YamlConnectionFactory dummyConnectionFactory(@Nonnull SemanticVersion version) {
             public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
                 // Add query string to connection so we can tell where it came from
                 URI newPath = URI.create(connectPath + "?version=" + version);
-                return new SimpleYamlConnection(dummyConnection(newPath), version);
+                return new SimpleYamlConnection(dummyConnection(newPath), version, CLUSTER_FILE);
             }
 
             @Override
diff --git a/yaml-tests/src/test/java/SupportedVersionTest.java b/yaml-tests/src/test/java/SupportedVersionTest.java
index dd5d95862c..4053f60d23 100644
--- a/yaml-tests/src/test/java/SupportedVersionTest.java
+++ b/yaml-tests/src/test/java/SupportedVersionTest.java
@@ -25,6 +25,7 @@
 import com.apple.foundationdb.relational.yamltests.YamlRunner;
 import com.apple.foundationdb.relational.yamltests.configs.EmbeddedConfig;
 import com.apple.foundationdb.relational.yamltests.server.SemanticVersion;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -45,7 +46,8 @@
 public class SupportedVersionTest {
 
     private static final SemanticVersion VERSION = SemanticVersion.parse("3.0.18.0");
-    private static final EmbeddedConfig config = new EmbeddedConfig(null);
+    private static final String CLUSTER_FILE = FDBTestEnvironment.randomClusterFile();
+    private static final EmbeddedConfig config = new EmbeddedConfig(CLUSTER_FILE);
 
     @BeforeAll
     static void beforeAll() throws Exception {
@@ -65,7 +67,7 @@ YamlConnectionFactory createConnectionFactory() {
         return new YamlConnectionFactory() {
             @Override
             public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
-                return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION);
+                return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION, CLUSTER_FILE);
             }
 
             @Override
diff --git a/yaml-tests/src/test/java/TransactionSetupTest.java b/yaml-tests/src/test/java/TransactionSetupTest.java
index 2aa57b8425..bb9a5ea4d8 100644
--- a/yaml-tests/src/test/java/TransactionSetupTest.java
+++ b/yaml-tests/src/test/java/TransactionSetupTest.java
@@ -26,6 +26,7 @@
 import com.apple.foundationdb.relational.yamltests.configs.EmbeddedConfig;
 import com.apple.foundationdb.relational.yamltests.configs.YamlTestConfig;
 import com.apple.foundationdb.relational.yamltests.server.SemanticVersion;
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -46,8 +47,9 @@
 public class TransactionSetupTest {
 
     private static final SemanticVersion VERSION = SemanticVersion.parse("4.4.8.0");
-    private static final YamlTestConfig config = new EmbeddedConfig(null);
+    private static final YamlTestConfig config = new EmbeddedConfig(FDBTestEnvironment.randomClusterFile());
     private static final boolean CORRECT_METRICS = false;
+    private static final String CLUSTER_FILE = FDBTestEnvironment.randomClusterFile();
 
     @BeforeAll
     static void beforeAll() throws Exception {
@@ -73,7 +75,7 @@ YamlConnectionFactory createConnectionFactory() {
         return new YamlConnectionFactory() {
             @Override
             public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
-                return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION);
+                return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION, CLUSTER_FILE);
             }
 
             @Override
diff --git a/yaml-tests/src/test/java/com/apple/foundationdb/relational/yamltests/server/ExternalServerTest.java b/yaml-tests/src/test/java/com/apple/foundationdb/relational/yamltests/server/ExternalServerTest.java
index c544db86c0..edd5720bee 100644
--- a/yaml-tests/src/test/java/com/apple/foundationdb/relational/yamltests/server/ExternalServerTest.java
+++ b/yaml-tests/src/test/java/com/apple/foundationdb/relational/yamltests/server/ExternalServerTest.java
@@ -20,6 +20,7 @@
 
 package com.apple.foundationdb.relational.yamltests.server;
 
+import com.apple.foundationdb.test.FDBTestEnvironment;
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -54,8 +55,9 @@ private static File getCurrentServerPath() throws IOException {
     @Test
     void startMultiple() throws Exception {
         final List servers = new ArrayList<>();
+        final String clusterFile = FDBTestEnvironment.randomClusterFile();
         for (int i = 0; i < 3; i++) {
-            servers.add(new ExternalServer(currentServerPath, null));
+            servers.add(new ExternalServer(currentServerPath, clusterFile));
         }
         try {
             for (final ExternalServer server : servers) {
diff --git a/yaml-tests/yaml-tests.gradle b/yaml-tests/yaml-tests.gradle
index 8ed4e50c92..6d0a38dfbf 100644
--- a/yaml-tests/yaml-tests.gradle
+++ b/yaml-tests/yaml-tests.gradle
@@ -59,6 +59,7 @@ dependencies {
     implementation(libs.protobuf)
     implementation(libs.protobuf.util)
     implementation(libs.snakeyaml)
+    implementation testFixtures(project(':fdb-extensions'))
     implementation(libs.jcommander)
 
     testImplementation(libs.bundles.test.impl)