Migrating 0.16.24 → 0.17.0¶
KGLite remains pre-1.0. This release deliberately tightens several invalid or
ambiguous inputs, changes a few result shapes, and restricts operations whose
old behavior could silently use the wrong graph value. Pin kglite==0.16.24
until you have checked the affected rows below.
Before upgrading¶
Back up the complete .kgl file or disk-graph directory together with its WAL.
A CSV/blueprint export is useful for portable node, relationship, and property
recovery, but it does not contain every engine feature. Finish or checkpoint
important WAL-backed work with 0.16.24 before replacing the runtime.
If you embed the Rust crate, pin an exact version while migrating. KGLite’s pre-1.0 policy permits documented breaks in patch releases. The C ABI remains additive; this migration changes C query-parameter validation but removes no C function or status value.
Changed behavior and migration¶
Area |
0.16.24 |
0.17.0 |
What to change |
|---|---|---|---|
Persisted Python graph ownership |
Closing an owner could leave writer/WAL authority reachable through retained handles. |
Close/context exit ends publication authority. Retained data remains privately readable and mutable, but an earlier transaction cannot publish through the ended owner. A failed checkpoint retains authority for retry. |
Commit or roll back transactions before closing. Do not use a retained snapshot as a route back to the closed file. |
Derived-handle capture |
Independent handles could publish changes into the source graph’s capture stream. |
Independently derived handles do not publish into the source capture stream. |
Write through the owner when changes belong in its CDC/WAL history; use |
Query defaults and deadlines |
Derived handles did not consistently preserve the source defaults. Transaction lifetime |
Snapshots, sessions, cursors, and transactions capture defaults when created. Omitted query options inherit them, including the built-in 180-second query timeout. |
Pass |
Context exit and later save |
Save/exit behavior could leave unclear ownership. |
A clean graph context checkpoints on exit. It does not undo WAL commits when the block raises. After close, |
Use transactions for rollback. Coordinate concurrent writers before an explicit-path save; there is no universal atomic-save promise across bindings and storage modes. |
Declared integer ingestion |
Some decimal/scientific whole values and nullable frame integers rounded through float. |
Declared integers preserve signed 64-bit values exactly; fractional or out-of-range inputs become NULL under the existing tolerant ingestion policy. Blueprint CSV and DataFrame scalar text share integer, Boolean, float, and date parsing. |
Validate NULLs after tolerant ingestion if rejection is required. This policy is separate from strict query parameters. |
DataFrame output |
Nullable/mixed integers could round during pandas inference; a list of empty maps could lose rows. |
Nullable integer columns use pandas |
Remove float-based workarounds and expect pandas extension/object dtypes where exact integers require them. |
Python query parameters |
Oversized integers rounded to float and unsupported nested objects became NULL. |
Every Python query handle rejects integers outside signed 64-bit range and unsupported objects at the exact nested path. Ordinary containers, NumPy signed values in range, and non-finite float parameters remain accepted. |
Catch |
JSON query parameters |
Rust, C, and MCP JSON routes could round oversized integer tokens. A process-wide arbitrary-precision representation could also reinterpret an ordinary object whose first key was |
Integer tokens must fit signed 64-bit; decimal/exponent tokens must fit finite |
Encode intentional floating values with a decimal/exponent and keep them finite. Do not reserve or rewrite |
Numeric casts and predicates |
|
Invalid casts return NULL; finite in-range floats truncate toward zero. Mixed predicates compare exactly, with conservative index fallback when needed. Structural |
Audit boundary-value filters and any code that expected cast saturation. Do not infer that integer and float values became one universal identity type. |
Aware datetimes and timestamp text |
Aware Python datetimes and some RDF/text/protocol paths lost offsets or fractional seconds. |
Aware Python inputs normalize to UTC; explicit dates keep their calendar date. Supported timestamp constructors and text outputs retain fractions. Bolt refuses a timestamp it cannot represent. |
Compare UTC instants, and stop relying on silently truncated protocol timestamps. Human table text remains compact. |
Endpoint result values |
Endpoint values resolved differently across eager/lazy, nested, and direct Python output paths. |
Results resolve against the executing immutable view while preserving structural node/relationship IDs. |
Keep raw native references within their originating view. Ordinary projected values are safe to consume; this does not create durable cross-graph physical IDs. |
RDF loading |
Rust |
|
Bootstrap RDF into |
Embedding services and transactions |
Transactions could lose a registered model; |
|
Register runtime-only models again after load. Avoid same-thread reentrant writes from a Session callback. Do not assume cross-thread causal refusal or immutable model weights. |
NetworkX import |
Nullable node/edge property columns could round exact integers. |
Signed 64-bit properties survive nullable columns exactly. Existing ID coercion, heterogeneous-object stringification, and same-type parallel-edge deduplication remain. |
Remove property-rounding workarounds. Do not treat this as a new lossless policy for NetworkX IDs or arbitrary mixed objects. |
Fluent/grouped dictionaries |
Equal titles, suffix-like labels, and metadata keys could overwrite groups or endpoints. |
Collision-safe presentation keys retain every group for |
Iterate all returned entries. Treat keys as display labels, never stable graph identifiers; mutation or selection changes can change them. |
Selection set algebra |
Selections from unrelated graphs could combine equal physical slots and return plausible wrong nodes. |
Union/intersection/difference/symmetric difference require the same exact immutable graph view. Diverged copy-on-write siblings are refused. |
Derive both selections from the same snapshot. Map by application IDs yourself when combining independent graphs. |
|
Python keys collapsed IDs such as |
|
Add |
New stored endpoint properties |
A property assigned |
Admission recursively snapshots the referenced node’s statement/source-view title before constraints, indexes, CDC, or WAL. Lists/maps, titles, |
Store a relationship when you need a changing association. Low-level Rust |
Legacy stored references |
Complete old snapshots could retain physical-slot behavior; WAL replay could guess a slot after allocation changed. |
Recoverable values in complete portable/disk snapshots normalize against the unchanged complete source before constraints, restored indexes, or publication. A malformed persisted disk edge-property payload refuses load. Load does not rewrite the source until explicit save. Uncheckpointed raw-reference WAL frames refuse before replay or repair; checkpointed residue is skipped. |
Load before slot-changing work, validate values against application truth, repair any already-retargeted value, then explicitly save a new checkpoint. KGLite cannot recover historical identity after an older deletion/reuse/compaction. |
Load memory ceilings |
The metadata estimate was the only portable-load budget check. |
Affected legacy portable files receive a conservative post-decode normalization-overlay check before private mutation/publication. |
An affected file can pass the public estimate and still raise |
Machine CSV |
CLI CSV compacted floats/timestamps recursively; CR/LF record accounting could split MCP records. |
CLI/core CSV preserves numeric/timestamp text precision and RFC quoting. MCP caps 200 complete logical records and reports logical CSV counts, including CSV-over-HTTP acknowledgement. Human tables stay compact; empty string and NULL remain the same empty CSV field. |
Use JSON when empty string versus NULL matters. Parse MCP CSV as RFC CSV rather than physical lines. |
Java timeout conversion |
A positive duration below 1 ms narrowed to zero, disabling the timeout. |
Positive fractional milliseconds round up to 1 ms; larger positive values round up and overflow saturates at |
No workaround is needed after upgrade; keep using positive |
Bolt |
A nonzero timeout was silently ignored. |
Top-level nonzero or mistyped |
Omit the key or send zero. Enforce a deadline in the client until KGLite implements it. WAL durability |
MCP skills |
Unknown predicate keys could activate a typo-gated skill; docs misstated precedence, size, and live refresh. |
Unknown nested |
Restart after changing the graph or skill files. Correct predicate typos. Use |
Stored endpoint example¶
New writes follow this rule now. The legacy-file guidance after the example describes the complete-snapshot load contract:
g.cypher("CREATE (a:Item {id: 1}), (b:Item {id: 2, title: 'Beta'}), (a)-[:LINK]->(b)")
g.cypher("MATCH (a:Item)-[r:LINK]->() SET a.owner = endNode(r)")
g.cypher("MATCH (b:Item {id: 2}) SET b.title = 'Renamed'")
assert g.cypher("MATCH (a:Item {id: 1}) RETURN a.owner").scalar() == "Beta"
In 0.16.24 the final value could be "Renamed" because the property held a
physical slot. For an older checkpoint, load and validate before mutation, fix
from your own source of truth if necessary, then save to a new path. A successful
load proves only what the current complete snapshot can resolve.
Performance costs and limits¶
Release measurements are part of the contract record. Comparisons against 0.16.24 include all intervening changes unless an isolated pair says otherwise.
Session writes that can invoke an embedding callback measured about 1.056–1.069× the no-model path for the reported 1/10,000-node cells, improved from 1.250–1.770× before callback-path isolation was narrowed. Snapshot and getter safety adds tens-of-nanoseconds costs.
Collision-safe grouped output improved the measured unique-label fast path: 100 groups by about 12.3% and 1,000 groups by about 10.2%. Collision cases return more data than 0.16.24 and are correctness-cost cells.
Strict JSON number parsing added about 5–6% in focused parse cells. A broader query comparison stayed within its declared limit; fixed-hop
EXISTSwas the largest observed control change at 15–16%, and a later cumulative comparison measured 17.8–19.0%.The cumulative JSON transport comparison measured CLI ordinary output at +4.0%/-1.1%, CLI precision at +0.2%/-0.2%, and MCP ordinary output 15.6–16.6% faster across two pairs. Multiline MCP cells ranged from 5.6–9.8% slower for one record to 11.7–12.5% faster for 201 records. Removing process-wide arbitrary precision improved measured integer/float JSON-array cells about 2–4%. These end-to-end cells do not isolate validator throughput.
Ordinary 100–1,000-node outline rendering is 33–39% slower than 0.16.24. Without a current ID index, exact root ambiguity detection scans the complete primary-type bucket; the 10,000-node release probe measured about 0.09–0.11 ms. The extra identity projection/conversion is required for the corrected result and remains an explicit performance exception.
Stored endpoint-reference admission left ordinary SET controls within roughly 0–4.5% of their before captures. Endpoint-reference SET and transfer cells return corrected values, so their timing is a correctness cost. A later source-equivalent comparison kept the broader fixed-hop
EXISTSwatch cell at 16.9–19.0% over baseline.Complete-snapshot normalization measured 0.402–0.448 ms for an ordinary disk fixture versus 0.243–0.249 ms before the change, a 61.4–84.4% increase. Ordinary portable loads measured 0.671–0.683 ms versus 0.593–0.598 ms, a 12.2–15.2% increase. An earlier implementation measured 0.619–0.624 ms for ordinary disk before the final checked decoder correction. Reference-bearing fixtures return corrected values, so their 1.316–1.373 ms disk and 0.828–0.849 ms portable means are correctness costs. The unchanged disk format has no persisted marker proving that an edge-property heap lacks legacy references, so every nonempty blob is visited on complete-snapshot load, including blobs written by 0.17.0; uncommon shapes use canonical decoding. This is load-time work, not query timing, and these small fixtures do not establish large-graph scaling.
A broader disk-directory reopen cell measured 0.817 ms before normalization and 3.134–3.314 ms after it (+283.8–305.8%), with a bounded retake at 3.197 ms (+291.5%). This is the same complete-snapshot load work at a different fixture boundary.
The immediate CSV/binding comparison also includes the preceding JSON and snapshot work. Equivalent CLI ordinary output was +1.44%/-0.05% across two pairs, while equivalent MCP ordinary output was 12.88–13.10% faster; that is not an isolated formatter speedup. Corrected CLI precision was +0.58%/-1.69%, one-record multiline MCP +5.84%/-10.94%, and 201-record multiline MCP -5.51%/-4.80%. Those changed-output cells are correctness costs. An earlier published-wheel baseline had MCP ordinary output 20.3–20.7% slower. These end-to-end measurements establish no isolated formatter or record-scanner speed claim.
No release-wide speedup is claimed. Direct statistics retain f64 results,
string-normalized grouping, compact human text, representation-specific NULL
and precision behavior, and nondeterministic ordering among ties unless a
specific API documents otherwise.