Repository navigation
JVM: opt-in JDK 25 AOT cache (ARCADEDB_AOT=1 / start_jvm(aot=True)) (#19) - #31
Merged
Merged
Conversation
) Ports humemai/arcadedb-embedded-python#490. A per-user Project Leyden cache of the classes the JVM loads and links at start; off by default, every failure is a normal start. The docs part of #490 is left out until the Antora component (#23) has a JVM API page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8 of 10 tasks
The only conflict was src/arcadedb_embedded/jvm.py, where this branch adds _aot_flags() and runtime-fixes (through main's #26) adds _restore_python_signal_handlers() after start_jvm(). Kept both functions; the start path calls _aot_flags() before JPype starts and the signal restore after. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Ports humemai/arcadedb-embedded-python#490 (merged as 43d723c9e1), part of #19. An opt-in Project Leyden AOT cache (JDK 25, JEP 483/514/515) for the JVM wheel.
ARCADEDB_AOT=1orstart_jvm(aot=True)(alsojvm_kwargs={"aot": True}) lets the JVM keep a per-user cache of the classes it loads and links at start. It is off by default, and nothing changes for anyone who does not set it. New modulesrc/arcadedb_embedded/aot.py, a small hook instart_jvm(_aot_flags(aot, jvm_args, classpath)and theaotkeyword) and tests intests/test_aot.py.$XDG_CACHE_HOME/arcadedb/aotor~/.cache/arcadedb/aot(macOS~/Library/Caches/arcadedb/aot, Windows%LOCALAPPDATA%\arcadedb\Cache\aot);ARCADEDB_AOT_CACHE=<file>overrides it. File name = hash of package version, bundled JRE version, jars (jar_fingerprintsha256 plus each jar's size and mtime), the exact class path (including JPype'sorg.jpype.jar, which the cache records too) and platform.-XX:AOTCacheOutput=<unique tmp>(the JVM writes it when the process exits normally); one trainer at a time via an exclusive lock file holding its pid, other processes start normally. The next start recovers a finished or dead trainer: the bundled JVM must accept the file (-XX:AOTMode=on -version), then an atomic rename and a sidecar with size and sha256. Use is-XX:AOTCache=<file> -XX:AOTMode=auto; a file whose size or sha256 differs from the sidecar is deleted and rebuilt, so a damaged cache is never mapped.arcadedb_embedded.aotlogger;start_jvmnever raises because of it. User-given-XX:AOT*,-XX:SharedArchiveFileor-Xshareflags win and the bindings add nothing.Why (measured in #490, not re-measured here)
Measured on the laptop with
ab.py(studyjvm-aot-ab-laptop-20261022in the benchmark notes, 12 clean pairs per figure): cold total -33 to -36 percent, JVM start -25 to -30 percent, database open -37 to -42 percent, and no detectable difference in steady-state inserts, lookups or scans. A tiny training run was as good as a broad one. A cache records absolute jar paths and jar mtime and size, so it cannot ship inside the wheel and has to be created on the user's machine.Robustness matrix (from #490)
Probe output in humemai/arcadedb-embedded-python
.notes/bench/repros/jvm-aot-robust-20261024. Every repetition is fresh processes and must end with a working database; "mapped" means the JVM's own-Xlog:aotlog shows it opened and mapped the cache. The probes ran the #490aot.pyandjvm.pyon top of the merged-main wheel 26.11.1.dev0 (Corretto 25.0.4.1 jre), cores 0-5. They were not rerun for this port; the module is a verbatim copy and thejvm.pyhook is the same code.Total: 95 of 95 repetitions passed; SUMMARY runs=514 failed_probe_variants=0. A damaged cache is never mapped and is rebuilt by the third run; after the 8-process race the cache is mapped with exactly one cache file and no leftover temp or lock files.
Known limit (from #490): if the JVM itself rejects a flag at creation, the start fails with the usual
ArcadeDBErrorand the lock is freed; there is no in-process retry because JPype cannot start a second JVM after a failed one. The flags are only added for a bundled JRE of version 25 or newer, so this is not reachable today. A process that never exits normally (os._exit, SIGKILL) trains nothing. Windows and macOS code paths (cache directory, pid check) are covered by unit tests with mocks only. Making it the default needs a newab.pyrun on the release wheel.Tests
tests/test_aot.py, 27 tests: key derivation, per-platform cache directory, JRE identity, promotion, damaged caches, stale locks, fallbacks (mocked), override path, off by default. Run on this branch'ssrcoverlaid on the unpacked linux/amd64 wheel 26.11.1.dev0 (the setup used for #27 and #29), together with the tests that exercisestart_jvm():tests/test_aot.py tests/test_jvm_args.py tests/test_log_dir.pygave 52 passed in 4.79 s. black 26.10.1, isort 9.0.2 (--profile black) and bandit (-c pyproject.toml --severity-level low --confidence-level low) are clean on the three files, with the# nosec B404/B603comments kept.Base
Based on #27 (
port/runtime-fixes), not main: both change thestart_jvm()signature, docstring and thejpype.startJVMcall, and #27 serializesstart_jvm(), so on main this hook would conflict. Merge #27 first; the diff then shows only the AOT change. #26 (Ctrl-C) also edits thejpype.startJVMregion, so whichever of #26 and this lands second needs a small rebase.Left out
The documentation part of #490 (a section in the JVM API page and a line in the architecture page): this repository has no counterpart page yet (the Antora component is #23). It can follow once #23 is merged. The code docstrings of
start_jvmandaot.pydescribe the option.🤖 Generated with Claude Code