Skip to content

JVM readers: a record or row to dict, the property names and result-set iteration in one crossing each - #233

Draft
tae898 wants to merge 1 commit into
mainfrom
perf/jvm-reader-crossings
Draft

tae898 wants to merge 1 commit into
mainfrom
perf/jvm-reader-crossings

Conversation

@tae898

@tae898 tae898 commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Closes #227

The JVM build read a record, a row, a list of property names and each row of a result set through many JPype calls: Document.to_dict() took 18 reads of Java objects for 3 properties, Result.to_dict() 14, the property names 12, and for row in rs two calls per row (counts of an untimed census, see the issue; no timing yet). A new bridge class, ReaderCalls (src/java/com/arcadedb/python/ReaderCalls.java, compiled with the other bridge classes into the same jar), cuts each to one call:

  • Result.to_dict() (results.py) and Document.to_dict() (graph.py): ReaderCalls.rowJson / documentJson write the row or record as the typed JSON of TypedRows (the transport ResultSet.to_list() already uses). Python makes one call, reads one array element and one string, and parses with the C json module; the side array of the engine's own objects is read only when the JSON carries the tag character. Values have the types convert_java_to_python gives them, and the key order is the engine's.
  • get_property_names() and property_names of both classes (_property_names_tuple): rowNames / documentNames return the names as one JSON text instead of one JPype read per name.
  • ResultSet.__next__ (results.py): nextOrNull does hasNext() and next() in one call. A row is still read when it is asked for, an error in the statement surfaces from the call as before, the end is the same None as hasNext() false, and the closed and exhausted states are unchanged.

Without the class (a jar that lacks it) every caller takes the old path. The native build is not touched. TypedRows.appendRow and TypedRows.quote lose private so the new class can use them.

One difference on purpose: Document.to_dict() now stores the names of the read it just made, so a property set after an earlier to_dict() or get_property_names() is no longer missing from the next to_dict() (the cached tuple of names was never refreshed on a mutable record). A record whose wrapped Java object is not a com.arcadedb.database.Document (a binary record) now raises a TypeError from the bridge call where it raised AttributeError for the missing getPropertyNames.

Tests (tests/test_reader_crossings.py, JVM only, listed in NATIVE_NOT_APPLICABLE): a counting proxy on the bridge classes and the Java objects, which also counts the reads of the arrays and the Java set they return, pins the exact calls (to_dict is the call and one array read; the old path is at least 7 for a record and 9 for a row; iteration is one call per row against two), and every value type of the wide table, the values SQL cannot produce, tag-named properties and unusual property names are compared with an independent per-property read of the Java object (same Python types, same key order). Closed-database errors, laziness of iteration and the close semantics are pinned too.

Overlap with #214 (issue #213): #214 edits RowAccess.java, DbCalls.java, results.py (ResultSet.one, ResultSet.count), graph.py (Document.get_raw) and core.py. This PR adds its Java in a new file, edits ResultSet.__next__, Result._property_names_tuple, Result.to_dict, Document._property_names_tuple and Document.to_dict, and one line of tests/conftest.py (next to the line #214 adds). The hunks do not touch, so the two merge in either order; the only shared line region is the NATIVE_NOT_APPLICABLE table in tests/conftest.py, which may need a trivial rebase.

Draft: no timed run exists. A pre-registered timed A/B of the main CI JVM wheel against this PR's CI JVM wheel (a Java helper ships in the PR, so the arms are wheels) is prepared and decides whether this leaves draft.

🤖 Generated with Claude Code

…et iteration in one crossing each (#227)

Document.to_dict took 18 reads of Java objects for 3 properties, Result.to_dict 14, a
property-names call 12, and each row of a result set two calls (hasNext, next). The new
bridge class ReaderCalls writes a record or a row as the typed JSON of TypedRows (one call,
one string read, the C json decoder), returns the names as one JSON text, and hands over
the next row in one call. Values keep the types convert_java_to_python gives them; the side
array is read only when the JSON refers to it; a result set is still read one row per call.
Without the class the old paths run. Document.to_dict now also refreshes the cached names,
so a property set after an earlier read is no longer missing from get_property_names().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tae898

tae898 commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator Author

CI on head 3403dd2: workflow_dispatch run 41716185993 of test-jvm.yml, 18 of 18 jobs succeeded, including all 10 test legs (the new tests/test_reader_crossings.py ran on each). No timed run exists yet; the PR stays draft. The timed A/B is prepared with its rule and the GRAY rerun clause written before any run.

This branch has not been deployed

No deployments
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.

JVM wheel: record and row readers cross into the JVM many times (to_dict, property names, result-set iteration)

1 participant