Hi @alexmiller @ajbeamon, while trying to estimate the memory used by the FDB-C client for our java based layer, I looked at MemoryMetrics
traces by enabling tracing the client-side.
It appears that on setting a large timeout on the transaction, the memory used by the transaction is not freed for the entire duration of the timeout, even though the transaction itself is completed and closed.
I am still not sure if there is some error in my methodology or measurement;
Below a trivial code snippet that reproduces the problem: it creates one million KV entries, and then in the next clean run reads each of these entries serially.
If no explicit timeout is set on the transaction, then MemoryMetrics
indicated bytes stabilizes at ~6MB for TotalMemory64
for the lifetime of this process; however, if it is set to a large value, say 10 mins, then the counter TotalMemory64
keeps growing till about 150MB (which is I suppose my total data size).
I have tried this on a Mac for now, but I can try this on Linux if that could create a difference.
FDB version used: v6.1.8
Also attaching the trace file that indicates a memory build-up, and one that seems to be clean:
public static void read(final Database db) {
final long TEN_MIN = 10L * 60 * 1000;
for (int i = 0; i < 1_000_000; i++) {
final Tuple key = Tuple.from(1, 1, "a" + i);
db.run(tx -> {
// ****This option seems to create memory build up in C client ****
tx.options().setTimeout(TEN_MIN);
// **************************************************************
return tx.get(key.pack()).join();
});
if (i % 10_000 == 0) {
System.out.println("read " + i + " rows");
}
}
System.out.println("-- done reading --");
}
public static void write(final Database db) {
final byte[] val = "CiwKB09jZWFuaWESCUF1c3RyYWxpYRoCQVUiACoAMgA6AEIASgJlblIAWgBiAA==".getBytes();
final List<CompletableFuture<Void>> tasks = new ArrayList<>();
final Semaphore permits = new Semaphore(100);
for (int i = 0; i < 1_000_000; i++) {
final Tuple key = Tuple.from(1, 1, "a" + i);
permits.acquireUninterruptibly();
tasks.add(db.runAsync(tx -> {
tx.set(key.pack(), val);
return CompletableFuture.<Void>completedFuture(null);
}).whenComplete((v, t) -> permits.release()));
}
AsyncUtil.whenAll(tasks).join();
System.out.println("-- all writes done --");
}
public static void main(String[] args) {
final com.apple.foundationdb.FDB fdb = com.apple.foundationdb.FDB.selectAPIVersion(610);
fdb.options().setTraceEnable("/tmp/");
fdb.options().setTraceMaxLogsSize(4194304);
fdb.options().setTraceRollSize(1048576);
final Database db= fdb.open("/usr/local/etc/foundationdb/fdb.cluster", Executors.newFixedThreadPool(10));
// write(db); // In first run, uncomment this to populate the data
// read(db); // in Second run onwards, comment write() call and uncomment this call.
System.exit(0);
}