In the java binding, there is a local concurrency bug:
Inside a single transaction, if multiple threads are createOrOpening directories in a base directory with a common prefix, the directory might return as created, but in reality, it won’t be created. Even inside the same transation, listing it will cause an error.
7.1.x, Linux and Windows tested. Reproducer:
(“a”, “b”) is the base directory.
(“a”, “b”, “prefix”, random-uuid) are the directories being created.
public static void main(String[] args) {
int subDirectoryCount = 100;
var executor = Executors.newFixedThreadPool(8);
var db = FDB.selectAPIVersion(710)
.open(null, Runnable::run);
List<String> basePath = List.of("a", "b");
// Delete root dir of it existed
db.run(tr -> {
DirectoryLayer.getDefault()
.removeIfExists(tr, basePath)
.join();
return null;
});
// Create root dir
var baseDirectory = db.run(tr -> {
return DirectoryLayer.getDefault()
.createOrOpen(tr, basePath)
.join();
});
// Create many subdirectories in PARALLEL
try (var tr = db.createTransaction(Runnable::run)) {
CompletableFuture[] dirFutures = IntStream.range(0, subDirectoryCount)
.mapToObj(x -> CompletableFuture.supplyAsync(() -> {
try {
String subDirName = UUID.randomUUID().toString();
baseDirectory.createOrOpen(tr, List.of("prefix", subDirName))
.join();
if (!baseDirectory.exists(tr, List.of("prefix", subDirName)).join()) {
throw new IllegalStateException("Subdir does not exist, although it was just created");
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executor))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(dirFutures)
.join();
tr.commit().join();
}
executor.shutdownNow();
db.close();
System.out.println("No bug detected");
}