`FACTORY_TAGS[NATIVE_OFFLOAD] = false` hides regressions of #7267
To reproduce
Note: this bug was discovered and documented by an AI agent that was tasked with diagnosing a memory leak leading to a crash in 9.4.0.
Version: master (checked 9848be5f). Same skip exists in 9.4.0.
Component: tests (AbstractCairoTest, QueryAssertion)
Related: #7267 (factory clear() now resetCapacity()s OFFLOAD lists). Depends on the reduce-ring shrink in #7590 — enabling the tag without that will fail assertMemoryLeak after any JIT filter test.
Description
Per-test native-leak accounting still excludes NATIVE_OFFLOAD:
// AbstractCairoTest and QueryAssertion static init
FACTORY_TAGS[MemoryTag.NATIVE_OFFLOAD] = false;#7267 added AsyncFilterContextTest as the only wiring check, and says so:
// NATIVE_OFFLOAD is excluded from the per-factory leak check (AbstractCairoTest),
// so this assertion is the only cover for the wiring.That means:
- A revert of
AsyncFilterContext.clear()’sresetCapacity()calls would not fail the suite except that one test. - A new parallel path that allocates
NATIVE_OFFLOADand forgets to shrink/free it is invisible toassertMemoryLeak. - This is how the original 9.4.0 leak survived: an older comment already said OFFLOAD was “ignored for the time being.”
After #7267, a closed factory should hold zero OFFLOAD (lists are Misc.freed in AsyncFilterContext.close()). A cached factory after cursor.close() should hold only initial-capacity lists (256 longs each). Both are assertable.
The remaining reason the global skip exists is the process-lifetime PageFrameReduceTask ring (#7590 ): those lists stay at peak across tests in the same engine.
Suggested fix
Land the
PageFrameReduceTask.of()shrink from issue 01 first.Flip the tag on:
FACTORY_TAGS[MemoryTag.NATIVE_OFFLOAD] = true;in both
AbstractCairoTestandQueryAssertion.Keep
AsyncFilterContextTest.testClearShrinksRowIdListsThroughCursorTeardownas the explicit “cursor close while factory lives” guard (that is a capacity assertion, not a zero-bytes assertion).If some tests still see a bounded residual (initial 256 × workers × 8 bytes on a cached factory that outlives the test’s
assertMemoryLeakwindow), assertNATIVE_OFFLOAD <= workers * initialCapacity * 8instead of 0 for the cursor-closed-factory-alive case — but engine/factory close must still be 0.
Do not leave the skip in place “because the ring exists.” That is circular: the skip is why the ring leak has no test.
Suggested test (once issue #7590 is in)
@Test
public void testPageFrameReduceTaskOfShrinksOffloadOnNewQuery() {
assertMemoryLeak(() -> {
PageFrameReduceTask task = new PageFrameReduceTask(configuration, MemoryTag.NATIVE_OFFLOAD);
try {
long baseline = Unsafe.getMemUsedByTag(MemoryTag.NATIVE_OFFLOAD);
task.getFilteredRows().setCapacity(1_000_000);
Assert.assertTrue(Unsafe.getMemUsedByTag(MemoryTag.NATIVE_OFFLOAD) - baseline >= 1_000_000 * 8);
// of() with a different sequence id must resetCapacity
// ... bind a dummy PageFrameSequence with a new id, call of(), then:
Assert.assertEquals(
configuration.getPageFrameReduceRowIdListCapacity(),
task.getFilteredRows().getCapacity()
);
} finally {
Misc.free(task);
}
});
}QuestDB version:
master
OS, in case of Docker specify Docker and the Host OS:
Docker, Debian
File System, in case of Docker specify Host File System:
btrfs
Full Name:
Mark Dierolf
Affiliation:
FinancialContent
Have you followed Linux, MacOs kernel configuration steps to increase Maximum open files and Maximum virtual memory areas limit?
- Yes, I have
Additional context
No response
Source: questdb/questdb