github ArcadeData/arcadedb 26.7.2

5 hours ago

ArcadeDB 26.7.2

Overview

This release is a large stability and correctness milestone. It ships the results of a deep engine audit (storage, WAL/recovery, transactions/MVCC, LSM indexes and concurrency), hardens High Availability (Raft) recovery, completes the Neo4j Bolt driver compatibility certification, and fixes a broad set of OpenCypher and SQL query bugs.

It also closes five security advisories from an internal audit (see Security Advisories below); upgrading is strongly recommended for server-mode and multi-tenant deployments.

Two changes require attention before upgrading (see Breaking Changes): Raft storage is now durable by default, and the Bolt protocol now carries native temporal types instead of ISO-8601 strings.

Over 130 issues were closed for this milestone.

Security Advisories

This release closes several security advisories found in an internal audit. Upgrading is strongly recommended for any server-mode or multi-tenant deployment.

  • RCE via JavaScript triggers (GHSA-x9f9-r4m8-9xc2). A JavaScript trigger could look up java.lang.* host classes and reach Runtime.getRuntime().exec(...), obtaining OS command execution with only UPDATE_SCHEMA privileges. java.lang.* is removed from the trigger host-class allow-list; only the benign value packages (java.util, java.time, java.math) remain.
  • Arbitrary JavaScript execution via DEFINE FUNCTION ... LANGUAGE js (GHSA-vwjc-v7x7-cm6g). The SQL route to JavaScript bypassed the polyglot scripting gate, letting a user with schema (or lesser) access define and run arbitrary JavaScript. Defining a js function now requires UPDATE_SECURITY, and the polyglot engine runs with IOAccess.NONE (closing load(path|url) host-file read and SSRF) and PolyglotAccess.NONE (no cross-language pivot). SQL and Cypher user functions keep the standard schema-level protection.
  • Secret disclosure / root impersonation via GET /api/v1/server (GHSA-46hj-24h4-j8gf). The server-settings export masked only keys literally containing "password", leaking arcadedb.ha.clusterToken in clear - which, combined with the cluster-forwarded-auth headers, let a low-privilege authenticated user impersonate root. Every setting flagged isHidden() (clusterToken and all password/secret keys) is now redacted, for both the current and default value.
  • Cross-database IDOR on time-series / batch / Prometheus / Grafana endpoints (GHSA-x8mg-6r4p-87pf). Fourteen handlers extended the base HTTP handler directly and resolved the path database with no canAccessToDatabase check, so a user authorized for one database could read and write any other database on the server. All affected handlers now enforce database authorization (HTTP 403) before any payload handling and fail closed on a missing database name; the batch handler checks before leader-forwarding.
  • Read-only identity could mutate persisted schema (GHSA-8vr5-263f-x5r3). Several schema/config mutators reachable from a remote command were missing the UPDATE_SCHEMA guard: ALTER TYPE ... CUSTOM / BUCKETSELECTIONSTRATEGY, ALTER MATERIALIZED VIEW ... REFRESH, DROP MATERIALIZED VIEW / DROP CONTINUOUS AGGREGATE, ALTER TIMESERIES TYPE ... DOWNSAMPLING POLICY, DEFINE FUNCTION, and the stats-only REBUILD INDEX path. All are now gated (database-settings mutators use UPDATE_DATABASE_SETTINGS); every guard is a no-op for embedded, schema-load and HA-replication paths.

Major Highlights

Engine deep-audit 2026-07 (storage, transactions, LSM, concurrency)

A multi-area audit (#4962) hardened the core engine against data-loss and corruption edge cases under crashes, concurrency and load. Notable outcomes:

  • The WAL append is now the transaction's point of no return. Page versions are validated and bumped
    before the WAL write, so an aborted transaction can no longer be left in the WAL and partially replayed by
    recovery; a post-append failure is resolved by recovery replay and fences the database until reopen
    (#4936, #4937).
  • Torn 64KB page writes are repaired on recovery even when the on-disk version header already advanced (#4926); a clean close whose data fsync failed now
    preserves the WAL for recovery instead of deleting it (#4934).
  • Page-cache correctness: an older page version can no longer overwrite a newer committed one (lost
    update) and RAM accounting can no longer drift negative and disable eviction (unbounded growth) (#4925, #4933).
  • LSM index hardening: compaction no longer loses re-inserted keys or leaks orphaned pages on failure,
    range scans are no longer truncated by tombstoned keys, and non-unique point lookups no longer resurrect
    deleted RIDs (#4942, #4943, #4944, #4945, #4946, #4947).
  • Transactions: updating the same indexed record twice in one transaction no longer corrupts the index
    (#4935); dead-thread cleanup now rolls back abandoned
    transactions and releases their file locks, and is correct for virtual threads (#4941, #4956, #4939, #4940).
  • Async executor and shutdown: the stall detector no longer false-positives on slow tasks, shutdown no
    longer drops queued tasks or hangs completion waiters, and close()/drop() are now bounded so a wedged
    worker cannot hang shutdown forever (#4953, #4954, #5080, #4991).

Parallel query safety

Parallel bucket scans (enabled by default) could self-deadlock under pool saturation and wedge HTTP workers, race on the shared command context, or pin a CPU core at 100% when a consumer stopped draining. Scan producers now run on a dedicated pool, each worker gets its own context, and the native select().parallel() iterator bounds its producer offer and closes cleanly (#4948, #4949, #4950, #4951, #5065).

High Availability (Raft) recovery

  • Durable Raft storage by default removes a class of permanent follower divergence after a full-cluster cold restart (see Breaking Changes, #4835).
  • Diverged-follower resync no longer floods the logs and starves the snapshot download it triggers; only the first gap logs loudly (#4741 follow-up).
  • The leader now auto-recovers a follower's replication channel wedged on stale DNS after a Kubernetes pod-IP change, instead of dropping to bare quorum until a manual leadership transfer (#4696).
  • A distinct contract for "committed cluster-wide, local apply failed"
    (TransactionCommittedRemotelyException, mapped to HTTP 409) so applications no longer retry and create duplicates (#5064, #5017, #5018).
  • Offline bootstrap leadership-transfer path now commits its baseline (#5099).

Neo4j Bolt driver compatibility certification

The Bolt epic (#4882) reached certification: a shared conformance spec, per-language driver e2e suites (Java, Python, JS, Go, C#) CI-gated with a nightly run, and broad type-fidelity work. Highlights: native temporal PackStream structs in both directions (see Breaking Changes), native Path/Duration/Point structures, retryable-conflict mapping so drivers auto-retry, HA-aware ROUTE responses, Bolt 5.x negotiation and populated write-result counters (#4883-#4892, #4905, #4907, [#4908(https://github.com//issues/4908), #4922,
#4997-#5002, #5082, #5106, #5123).

Server & gRPC security hardening

A server-side security audit closed authentication, authorization and transport gaps: brute-force lockout and constant-time password compare, WebSocket change-stream per-database authorization, SSRF protection on restore/import, thread-safe security store persistence, and gRPC admin-RPC authorization, transaction-hijack protection and TLS/DoS hardening (#5021, #5022, #5027, #5029, #5030, #5034, #5037, #5039, #5040, #5048).

New Features & Improvements

  • User-defined functions are now persisted and distributed across an HA cluster
    (#5121).
  • LSM_SPARSE_VECTOR: configurable posting-weight quantization (FP32/FP16/INT8) via index
    METADATA, at parity with the dense vector index
    (#5143).
  • Commutative append-merge for super-node edges removes retry storms on high-degree vertices
    (#5148).
  • Extended Cypher write-counter (QueryStatistics) surfacing over HTTP/gRPC with UNION/CALL
    aggregation (#5015).
  • Point datatype and spatial index (#3401).
  • Python bindings: bulk-transport performance, embedded-only packaging and engine-sync fixes
    (#5010).

Major Fixes

OpenCypher

  • Dynamic bracket property mutations (SET n[key] = value, REMOVE n[key]) are now applied instead of silently ignored (#5141).
  • Inline relationship property filters {prop: value} are now enforced everywhere they were previously ignored: in MATCH (#5093), exists() (#5109), pattern comprehensions (#5111, #5139, #5146) and shortestPath/allShortestPaths (#5096).
  • exists() now enforces multi-label conjunctions on a bound start variable (#5095); count(n) on a multi-label pattern is no longer inflated (#5084).
  • count(*) after OPTIONAL MATCH no longer drops null-preserving rows (#5094); count(*) with an inline-filtered start node and anonymous endpoint no longer returns the global relationship count (#5071); the stale cached bucket counter behind count(*) drift is fixed (#5149, #5152).
  • Relationship writes inside FOREACH, CALL subqueries and top-level REMOVE now take effect (edge variable was being dropped by a planner optimization) (#4912, #4913, #5013); FOREACH updates are now visible to a later RETURN (#5114); FOREACH with MERGE/CASE WHEN exists() no longer drops SET or ignores conditions (#4993, #4994).
  • CASE fixes: simple CASE expr WHEN value in WHERE (#5083) and CASE WHEN r IS NOT NULL on an OPTIONAL MATCH relationship (#5137).
  • 2-hop pattern comprehension with an anonymous middle node now returns matches (#5007); COUNT {} inside a pattern-comprehension WHERE is now evaluated (#5140).
  • Correlated subquery fixes: EXISTS { ... WHERE node IN collected_nodes } now matches (#5138); EXISTS ... AND NOT EXISTS returns the correct result (#4995); CALL subquery ORDER BY LIMIT is correct (#5005).
  • Nested parameter field access ($param.field) resolves inside a MATCH pattern property map (#4909); optional path with while/@rid filter no longer NPEs on a missing prior segment (#4919).
  • Query planning/perf: independent MATCH pattern ordering no longer causes large runtime gaps (#5116, #5117); MATCH...CREATE now uses the unique index instead of a full scan (#5107);
    NodeIndexRangeScan now applies the temporal normalization it was bypassing (#5008).

SQL

  • FROM can now be used as a property name in DDL, indexes, projections and inserts (#5092).
  • Native select() ORDER BY on a non-index-served property no longer returns an empty result set (#5079).
  • TRAVERSE ... MAXDEPTH no longer under-approximates the d-hop ball (#5159) and STRATEGY BREADTH_FIRST now performs a breadth-first traversal (#5158).
  • Map string-key indexing $map["key"] now works inside INSERT ... CONTENT (#4915); Result.toJSON() serializes primitive arrays as JSON arrays (#4967); Type.convert() no longer wraps an empty array in a single-item list (#5091).

Indexes, vectors & encryption

  • FULL_TEXT (and every LSM_TREE) index now returns results on an encrypted database (#5142). Note: rebuild LSM_TREE/FULL_TEXT indexes (REBUILD INDEX *) on databases encrypted before this fix.
  • LSM compaction no longer loses entries on non-unique composite indexes with many duplicate keys (#5119, #5120); SEARCH_INDEX(..., 'term~') no longer NPEs after repeated deletes/updates (#5118).
  • CREATE INDEX ... BY KEY/BY VALUE now works, automatic-index series creation succeeds, and BY ITEM ... IF NOT EXISTS reports the correct created flag (#4879, #4880, #4881).
  • In-transaction iteration on a non-unique index no longer drops a committed RID sharing a key with an uncommitted entry (#5055).

Import / Export

  • IMPORT DATABASE no longer crashes on a JSONL dump that contains an LSM_VECTOR index; the index is rebuilt and repopulated from the exported embeddings (#5069).

Remote & Client

  • RemoteVertex.isConnectedTo() now accepts a Vertex argument, not only a RID (#5122).
  • Studio: re-login no longer adds a duplicate CodeMirror element (#4893).

Storage, transactions & concurrency (audit follow-ups)

  • HASH index: a cyclic overflow chain no longer spins a CPU core forever; scan walkers are bounded and throw
    an actionable error (#4743).
  • PageManager lifecycle is now refcounted, removing a check-then-act race on concurrent open/close of
    different databases (#4927).
  • Flush suspension is refcounted so overlapping backup/verify/snapshot each own their window without torn
    reads (#5068).
  • The fast async queue maps to a multi-producer DisruptorBlockingQueue (the previous SPSC queue could
    silently lose tasks) (#5066).
  • Additional low-severity storage/WAL/LSM/transaction/concurrency cleanups from the audit (#4924, #4928-#4932, #4938, #4952, #4955, #4957-#4961, #5067).

Breaking Changes (migration notes)

1. raftPersistStorage now defaults to true (durable Raft storage)

arcadedb.ha.raftPersistStorage now defaults to true (previously false/ephemeral outside Kubernetes).
Wiping the Raft log on restart could permanently diverge a lagging follower after a full-cluster cold restart
(WALVersionGapException) or silently re-form a fresh single-node cluster. Durable storage lets a restarted
node rejoin by replaying its persisted log. Ensure arcadedb.ha.raftStorageDirectory lives on durable
storage. A throwaway/test cluster can opt out explicitly with arcadedb.ha.raftPersistStorage=false
(#4835).

2. Bolt: temporal values now use native PackStream structures (not ISO-8601 strings)

The Neo4j Bolt protocol now carries date, time, localtime, datetime and localdatetime as native
PackStream temporal structures
in both directions. Inbound datetime query parameters are decoded to
java.time values (previously silently dropped), and outbound temporal properties are returned as native
structs. A Bolt client that previously read a temporal property as an ISO String must now read the native
temporal type (e.g. Value.asZonedDateTime() / asLocalDate()), the same as against Neo4j
(#4905,
#4907).

Dependencies

Runtime and library updates in this release:

  • Netty 4.2.16.Final
  • Jackson Databind 2.22.1
  • PostgreSQL JDBC 42.7.13
  • GraalVM 25.1.3
  • JLine 4.3.1
  • Neo4j Java driver 6.2.0
  • lz4-java 1.11.1
  • Jedis 7.5.3
  • proto-google-common-protos 2.73.0
  • protobuf-maven-plugin 5.1.7
  • Studio: apexcharts 5.16.0

Toolchain, build and CI updates: GitHub Actions (setup-go, github-script and grouped action bumps),
Prettier mirror, Testcontainers, Tomcat JDBC (test), and assorted Studio dev-dependency and e2e (JS/Go/Python)
driver bumps.

Full Changelog: 26.7.1...26.7.2

Don't miss a new arcadedb release

NewReleases is sending notifications on new releases.