Skip to content

Build two distributions: arcadedb (native) and arcadedb-jvm (JVM) - #66

Open
tae898 wants to merge 15 commits into
mainfrom
native-two-dist
Open

tae898 wants to merge 15 commits into
mainfrom
native-two-dist

Conversation

@tae898

@tae898 tae898 commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Makes the repository build two distributions that both import as arcadedb, as decided for 26.11.1 (tracker #19): arcadedb is the native build (a GraalVM native-image shared library loaded with ctypes) and arcadedb-jvm is the JVM build this repository builds today. arcadedb-native stays a dependency-only alias for later. Draft: nothing is merged or published by this PR.

What is in it (one commit per phase)

  1. Rename the JVM distribution to arcadedb-jvm: pyproject name, release-jvm.yml (environment pypi-jvm, name guard, wheel-count checks, check_version.py --pypi arcadedb-jvm), after_tag_verify.sh, wheel globs (arcadedb_jvm-*.whl), the pip install '...[extra]' hints, install lines. The import name stays arcadedb.
  2. arcadedb.__backend__ ('jvm' or 'native') and a hard import guard: each distribution raises DistributionConflictError (an ImportError) when the other one is installed. The message names both packages, says how to fix it and links the Which package? page. This replaces the old warning. Tests in tests/test_dist_conflict.py, including a fresh interpreter with a fake sibling dist-info.
  3. Port of the native build: native/ (Java glue, Maven wrapper, scripts/build-image.sh), native/wheel/ (build_wheel.py builds the arcadedb wheel, py3-none-<platform>; gen_notices.py checks THIRD_PARTY_NOTICES.md against what is compiled in; parity_wheel.py runs this repository's tests against the installed wheel), src/arcadedb/_native/ plus the hooks in core.py, server.py, vector.py, dataframe.py, exporter.py, exceptions.py, and the native tests with the repository's collect-ignore rule (NATIVE_NOT_APPLICABLE, native-only files not collected on the JVM build). No arcadedb_embedded names anywhere (scripts/check_no_embedded_name.py passes). GraalVM Community Edition only, pinned by sha256 per platform.
  4. CI: .github/workflows/test-native.yml, .github/workflows/release-native.yml and scripts/select_native_matrix.py (legs pinned by tests/test_select_native_matrix.py). test-native.yml builds the wheels and runs the parity suite and never publishes. A pull request runs it only when a native path changed, not for a draft, on linux-x86_64 and Python 3.12. Push to main (native paths), nightly (only when something besides docs changed in 25 hours, a full run on Mondays) and dispatch run all four platforms on Python 3.12 and 3.14. release-native.yml runs on a version tag, calls test-native.yml with release: true (all four platforms on Python 3.11 to 3.14), applies the readiness gate and publishes. The JVM workflows ignore native/** and the two native workflow files.
  5. Docs: README first screen with the two-row table, install lines side by side on the installation page and the quickstart, both PyPI descriptions name the sibling in the first paragraph, a permanent Which package? page (docs/modules/ROOT/pages/which-package.adoc, replaces packages.adoc), the CI and release pages. Transition-only callouts carry a REMOVE WHEN comment.

Source of the port

Ported from humemai/arcadedb-embedded-python branch native-preview at 91a6ad719b0308c8ccbf569bf130412619292925, as it is, not from main. The open native-preview PRs #514 and #520 to #538 are not merged there and are not in this port; each has to be ported separately after it merges (#527, #535, #537 and #538 are on the readiness bar).

Workflow files and release rules

One test and one release workflow per build: test-jvm.yml and release-jvm.yml for arcadedb-jvm, test-native.yml and release-native.yml for arcadedb; test-python-examples.yml stays. Both release workflows start on the same bare-version tag. The check names Test arcadedb (<platform>, Python <v>) did not change.

  • A.B.C.devN publishes arcadedb through the environment pypi (the same one as final releases) when linux-x86_64, linux-aarch64 and macos-arm64 passed on Python 3.11 to 3.14. Windows is published too when it passed, and is informational otherwise.
  • A.B.C and A.B.C.postN publish to the environment pypi only when the repository variable ARCADEDB_NATIVE_READINESS_BAR_MET is true, all four platforms passed on all four Pythons, and no test is a NOTYET skip. Speed parity is not part of the bar. Until then the first final arcadedb waits and arcadedb-jvm 26.11.1 ships alone.
  • Setup needed outside this PR: the PyPI trusted publishers, arcadedb-jvm <- release-jvm.yml / environment pypi-jvm and arcadedb <- release-native.yml / environment pypi, and the repository variable. One environment per package: pypi-jvm and pypi both exist already; pypi-prerelease was dropped to keep it simple.

Verified locally (linux-x86_64, Python 3.12)

  • Native image built from the ported glue: GraalVM CE 25.4.4.1.1, engine 26.10.2, -march=compatibility, image generation 2 min 33 s on 8 cores. -J-Xmx8g ran out of memory in the builder; 12g (what CI uses) works.
  • Both wheels built (arcadedb-26.11.1.dev0-py3-none-manylinux_2_34_x86_64.whl, arcadedb_jvm-26.11.1.dev0-cp312-cp312-manylinux_2_34_x86_64.whl) and installed in fresh uv environments: __backend__ is native and jvm. With both installed, import arcadedb raises DistributionConflictError with the full message, in either install order.
  • Native parity against the installed native wheel: 1135 passed, 68 failed, 34 NOTYET, 172 NA-JVM, 1 skip (the skip is fixed in the last commit). The failures are all features that exist in this repository's main but not in native-preview, so the native path has no hook for them yet: positional-count (test_positional_count, 9), Python callbacks and register_function(replace=) (test_python_callbacks, 31, test_python_function_interrupt, 1), RID values (test_record_id_values, 3), the new product-quantization settings (test_vector_product_quantization, 3), parallel bulk insert (test_bulk_insert, 2, test_float_vector_bulk, 3), log directory (test_log_dir, 5), test_example29_hybrid_memory (2), test_xml_limits (1), and a crash in test_async_executor. The docs-site test (7) is ignored by the parity runner now. The native CI leg will therefore be red until these are ported; this is the work between this PR and a native arcadedb release.
  • JVM suite against the JVM wheel: queued behind the laptop memory budget when this PR was opened, not finished yet.

🤖 Generated with Claude Code

tae898 and others added 7 commits October 25, 2026 20:21
The JVM build is published as arcadedb-jvm (environment pypi-jvm); the name arcadedb is reserved for the native build. The import name stays arcadedb. Wheel files are arcadedb_jvm-<version>-..., so every glob, the wheel-count checks, the name guard in the publish job, the check_version.py call, after_tag_verify.sh and the pip hints for the extras follow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ibution

arcadedb.__backend__ is 'jvm' or 'native'. The import-time check that only warned when both distributions were installed now raises DistributionConflictError (an ImportError) in both builds: arcadedb-jvm refuses to import when arcadedb (native) is installed, and the other way round. The message names both packages, says how to fix it (pip uninstall arcadedb arcadedb-jvm, then install one) and links the Which package? page. Unreadable metadata still never breaks an import.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… backend, native tests

The distribution `arcadedb` is the native build: the engine compiled by GraalVM Community Edition into a shared library (libarcadedb) that Python loads with ctypes. No JVM, no JPype.

- native/: the Java glue (C entry points: ArcadeNative, NativeCodec, NativeExtras, BulkOps), the Maven wrapper, and scripts/build-image.sh (GraalVM CE 25.4.4.1.1, -O2, -march=compatibility, engine jars of a pinned release from Maven Central).
- native/wheel/: build_wheel.py copies src/arcadedb, rewrites the JPype imports to a stub, flips the default backend to native, adds the library and the licence texts, checks THIRD_PARTY_NOTICES.md against what is compiled in (gen_notices.py), computes the platform tag from the library and builds a py3-none-<platform> wheel named arcadedb. parity_wheel.py runs the test suite against the INSTALLED wheel, file by file.
- src/arcadedb/_native/: the Python native path, plus the hooks in core.py, server.py, vector.py, dataframe.py, exporter.py and exceptions.py. arcadedb.__backend__ is 'native' when it is active. The JVM wheel ships these files too (setup lists the subpackage; test_wheel_packages.py proves it); the library is only in the native wheel.
- tests: test_native_*.py (collected on the native build only), conftest.py (NATIVE_NOT_APPLICABLE files are not collected on the native build; tests that need a Java object or the JVM are NOTYET / NA-JVM skips that the parity run counts apart; any other skip fails), the parity-aware child scripts in test_bridge_fixes.py and test_sigint.py.

Ported from the humemai/arcadedb-embedded-python branch native-preview at 91a6ad719b0308c8ccbf569bf130412619292925 (open native-preview PRs #514 and #520-#538 are not merged there and are not in this port).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… arcadedb

.github/workflows/native.yml builds the native image per platform, builds the wheel, runs the gates (glibc floor, auditwheel, twine, name and version) and the test suite against the INSTALLED wheel. scripts/select_native_matrix.py holds the legs (pinned by tests/test_select_native_matrix.py), because a native image build takes 1 to 2.5 hours per platform and the organisation shares a runner cap with upstream:

- pull request: only when a native path changed (paths filter) and not for a draft; linux-x86_64, Python 3.12.
- push to main (native paths only), nightly (only when something besides docs changed in the last 25 hours, a full run on Mondays) and dispatch: linux-x86_64, linux-aarch64, macos-arm64, windows-x64; Python 3.12 and 3.14.
- release tag (A.B.C.devN, A.B.C, A.B.C.postN): all four platforms on Python 3.11 to 3.14.

Publishing: a pre-release (A.B.C.devN) goes to the environment pypi-prerelease when linux-x86_64, linux-aarch64 and macos-arm64 passed on every Python. A final release (A.B.C, A.B.C.postN) goes to the environment pypi only when the repository variable ARCADEDB_NATIVE_READINESS_BAR_MET is true, all four platforms passed on all four Pythons, and no test is a NOTYET skip. GraalVM Community Edition only, pinned by sha256 per platform.

The JVM workflows skip native/** and native.yml in their paths-ignore.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
README, the installation page, the quickstart and both PyPI descriptions (README.md for arcadedb-jvm, native/wheel/README.md for arcadedb) name both packages up front: arcadedb is the native build and the default for scripts, notebooks, tests, CLIs, serverless functions and agents; arcadedb-jvm is the JVM build for heavy, long-running loads; both import as arcadedb. The install lines are shown side by side with a one-line rule, never a bare pip install arcadedb.

New permanent page docs/modules/ROOT/pages/which-package.adoc (replaces packages.adoc): the table, how to check (arcadedb.__backend__), what changes between the builds, switching and the import guard, server use (the official Docker images arcadedata/arcadedb), drivers, and a marked transition section for the old names. Transition-only callouts carry a REMOVE WHEN comment. The CI and release pages describe native.yml and the readiness bar for a final arcadedb release. tests/test_native_wheel_template.py pins the template of the native wheel (name, classifiers, sibling named in the first paragraph).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in the parity run

test_native_startup.py looked for the glue under native-spike/ and skipped when it was missing; a skip fails CI in this repository, so it now asserts the file exists at native/src/main/java. parity_wheel.py names test files on the command line, which bypasses collect_ignore, so it applies the docs-site rule of tests/conftest.py itself.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…on never replays a partly committed block

Three Python-only ports from humemai/arcadedb-embedded-python native-preview (#537, #531, #527):

- NativeDatabase._exec calls refuse_surplus_positionals (already shared with the JVM build,
  PR #52): more positional values than the SQL text can hold is a TypeError, not a silently
  dropped value.
- The native RID class offers the Java RID surface (ordering, getBucketId, getPosition,
  toString, isValid, equals, compareTo) and Result.get_rid() reads a projected @Rid.
- run_in_transaction: the guard that refuses to replay a block whose BATCH boundary or commit()
  already made part of its work durable sat inside a try whose own except swallowed the raise.
  The raise moves outside it. New test tests/test_run_in_transaction_partial_commit.py runs on
  both builds.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tae898 and others added 6 commits October 25, 2026 20:50
…ver from failure text

Port of humemai/arcadedb-embedded-python#535, adapted to this repository's conftest. The native
build used to turn a failing test into an NA-JVM or NOTYET skip when its failure text matched a
list of strings ("JVMNotRunning", "assert (None is not None)", "KeyError: 'flush'"), which is also
what a real bug looks like. Now:

- NATIVE_NA_JVM_TESTS is keyed <file>::[<Class>::]<test> and lists only tests that reach into the
  JVM on purpose, with the reason. Rows were added for the tests this repository has that the
  upstream table did not know (temporal writers, XML limits, to_java_rid, db._java_db patching).
- pytest_runtest_makereport reports NOTYET only for NotOnNativeYet, raised by the native code.
- to_java_int_array and to_java_byte_array return NumPy arrays on the native build, so the sparse
  vector tests no longer import jpype.types.
- tests/README.md says how a test may be skipped on the native build.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ert_many into a vertex type creates vertices (P-01b)

Port of humemai/arcadedb-embedded-python#538. gbCreate called GraphBatch.builder(db), which skips
the engine's single-batch guard, so two concurrent batches on one database lost edges. It now
calls db.batch(), as the JPype path does. insert_parallel looked at no schema and always called
newDocument, which the JVM build refuses for a vertex type; it now looks the type up once per
call: a vertex type gets vertices, an edge type is refused. Tests: TestInsertManyVertexType, the
second-batch refusal and the two-writers-one-hub test (the last two run on both builds).

Needs a rebuilt native library.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… on an engine without the where option

- create_vector_index takes rerank_oversample and fused_pq (CREATE INDEX .. METADATA
  rerankOversample / fusedPQ) and get_metadata reports them.
- insert_columns: only a float32 matrix is a column of vectors (float64 and N x 0 are refused with
  the one-dimensional ValueError), and the non-fast and parallel paths accept the float32 matrix.
  A float64 NumPy array in a row of insert_many is the list it would have been, not a float[].
- import_documents takes disable_xml_limits. The glue lifts the three jdk.xml limits around that
  one load and restores them; the XMLMessages bundle goes into the image so that an entity-limit
  error names JAXP0001000x instead of "Could not load any resource bundle". New
  tests/test_native_xml_limits.py (native build only).
- example 29: the where form is asserted refused ("Unknown option 'where'") on an engine that
  predates 26.11.1, like has_pq_rerank does for the PQ settings.

The XML limit and bundle changes need a rebuilt native library.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, like the final job

arcadedb now has one PyPI trusted publisher (native.yml with the environment pypi) for dev, final
and post releases. The environment pypi-prerelease is gone (deleted on GitHub), and the workflow
header and docs/modules/ROOT/pages/development/release.adoc say so. The readiness gate
(ARCADEDB_NATIVE_READINESS_BAR_MET and the full matrix for a final or post release) is unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…m, test-native, release-native)

The workflow files now follow one scheme:

- test-python-bindings.yml -> test-jvm.yml (the job names `Test arcadedb (<platform>, Python <v>)` do not change)
- release.yml -> release-jvm.yml (publishes arcadedb-jvm through the environment pypi-jvm)
- native.yml -> test-native.yml (native build and parity on pull requests that touch native paths, on main and nightly; it never publishes)
- release-native.yml (new): on a version tag it validates the tag, calls test-native.yml with `release: true` (4 platforms x Python 3.11 to 3.14), applies the readiness gate ARCADEDB_NATIVE_READINESS_BAR_MET, and publishes arcadedb through the environment pypi in one job for dev, final and post
- test-python-examples.yml stays

The trusted publishers on PyPI are keyed on the release file names: arcadedb-jvm <- release-jvm.yml / pypi-jvm, arcadedb <- release-native.yml / pypi.

select_native_matrix.py: the `tag` event is now `release` and the `publish` output is gone (publishing is decided in release-native.yml). select_matrix.py: the `bindings` workflow key is now `jvm`. Paths filters, the actionlint config, the tests that read workflow files, README badges (JVM and native), CLAUDE.md and the CI and Release docs follow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…the nosec B608 marker applies

Bandit reports B608 on the line of the f-string, and the marker sat on a later line. The statement is now one line with its marker; the interpolated value is a RID created by the test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tae898 and others added 2 commits October 25, 2026 21:24
…to the Windows child

test_wheel_packages builds with setup.py build_py; the uv parity env had no setuptools, so the test
failed on windows-x64 and macos-arm64 and passed on Linux only because the runner's Python ships it
(humemai/arcadedb-embedded-python#539).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st names the arcadedb_jvm wheel

auditwheel wraps 'is consistent with the following platform tag' across lines, so the native image gate
failed on a good wheel. test_build_wheel_check still wrote an arcadedb-<v> wheel after the JVM dist
became arcadedb-jvm, so build.sh's check found none (3 failures on every JVM leg).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant