ArcadeDB 26.10.1 closes 1,615 issues and pull requests under the 26.10.1 milestone: 1,244 issues and 371 PRs, out of 846 pull requests merged and about 3,100 commits since 26.9.1. 984 of the closed issues are bug fixes, 75 of them rated critical.
At a Glance
| Area | What changed | The number |
|---|---|---|
| High availability (Raft) | Snapshot install, follower catch-up, leadership hand-off, readiness, restart recovery | ~300 issues with the ha label |
| Silent data loss | Lost updates on growing records, hash-index key changes applied twice, stale-read writes | most concurrent-growth runs lost updates before |
| Equality | Index, SQL scan and Cypher disagreed for FLOAT, DECIMAL, DATETIME, BOOLEAN, null, signed zero | one answer everywhere |
| Query speed | Parallel scans, DISTINCT and aggregation, index-ordered LIMIT, striped database lock |
see below |
| Wire protocols | Postgres, MongoDB, Redis, Bolt, gRPC, Gremlin | 100+ fixes, plus Postgres TLS |
| Security | 4 advisories, one critical | all affecting 26.9.1 and earlier |
Upgrading is recommended for every deployment, and in particular for every HA cluster. Behaviour changes are collected in the upgrade checklist below.
New Features
Server and Operations
- TLS/SSL for the Postgres wire protocol (#8840).
- Opt-in OTLP log export, an OTLP/HTTP metrics endpoint fix, and
service.namein tracing (#7727, #7294, #7295). - Persistent instance id (
adb-<uuid>) shown in Studio, thearcadedb.support.idsetting, and Studio support requests that run a read-only query support asked for and send the result back (#8685, #8682, #8861). - A configurable config directory (#7415) and compact object headers enabled by
server.shwhen the JVM accepts them (#4537). HEADon/readyand/health(#8133); the HTTP server binds every local address the configured host resolves to (#8692).- Pool metrics for the per-server security executors (#7856).
- Read-your-writes bookmark (
X-ArcadeDB-Commit-Index) on streamed HTTP responses (#7351), and streaming ingestion with a temporary-id mapping returned chunk by chunk (#7353). - Startup
restore:publishes progress and waits out a conflicting operation (#7440, #7652).
Studio
- Index search and time series move into Query and Database, with a new Chart tab (#8788).
- A selected saved query is no longer run automatically (#7049), and the cluster page shows peer capabilities and rolling-upgrade readiness.
Query Languages and Engine
- Property rename as an in-place primitive, in the Java API and SQL (#7589).
COPY (<query>) TO STDOUTover the Postgres wire, in text and binary format (#7188), and NaN-transparent TimeSeriesSUM/AVG(#7089).algo.personalizedPageRanktakes a personalization vector (#8715); Cypherpoint()returns Neo4j SRIDs (#3993).- TimeSeries: bucket origin/offset on
ts.timeBucket, per-column codec syntax inCREATE TIMESERIES TYPE, andAggregationMetricsover HTTP (#8798, #7690, #7717). - Graph Importer imports JSON array properties (vector embeddings) from JSONL (#7185).
apoc.merge.node/apoc.merge.relationshiponMatchPropsanddb.index.fulltext.query*options (#8117, #8103). Teams moving from Neo4j can see how much of the surface is covered on the Neo4j alternative page.
Remote Clients and gRPC
A spec-driven client generation epic (#4894) closed the gaps between the HTTP, gRPC and Java remote APIs: a gRPC control plane (database lifecycle, backup, security, profiler, restore and import, groups and API tokens), a gRPC time-series API, an HTTP streaming query and vector search, an HTTP//ws insert session with a Java client, and RemoteDatabase access to /ts/* (#7304, #7305, #7306, #7307, #7403, #7406).
Major Highlights
High Availability: a Hardening Pass on Every Path a Cluster Takes to Recover
Around 300 issues carry the ha label this cycle. The ones that mattered most in practice:
- A follower stuck at a stale term after a restart or rolling restart, costing quorum on the next failure, is fixed at the root: the leader looped on install-snapshot notifications for ever after a follower installed at
leaderStart-1(#8289, #8341, #8360, #8449). - Stale entries after a snapshot install. A follower that re-applied its local log while a leader snapshot arrived applied stale entries onto the fresh copy and halted. Every install path now carries a boundary index, and a leader snapshot that is behind what the follower already applied is refused (#8577, #8579, #8454, #8651).
- A resync reopened pre-resync files. A snapshot resync skipped a database that was closed on the follower, a node could serve a snapshot of a database it had quarantined, and a node elected leader could reopen an unverified closed copy as the cluster’s copy (#8464, #8468, #8605, #8589). An audited override now exists for a peer that can never answer (#8641).
- Divergence after a graceful restart under load (extra rows and a corrupt unique LSM index) is closed (#8270), and so is a leader DDL that published its pages before its schema entry, so a concurrent replica transaction on the same pages merged against the old schema (#7438, #8686).
- Leadership hand-off. The automatic hand-offs only screened the target for lag, so a peer that was down was a valid target and the leader then refused every write. Targets are proven reachable now, the budget is sliced per candidate, and a node whose database was dropped for a resync can no longer be elected (#8556, #8491, #8533, #8480).
- A write that may have committed was retried.
MajorityCommittedAllFailedExceptionandReplicationDispatchedTimeoutExceptionwereNeedRetryExceptions, so a committed write could be replayed (#8481). The remote client now sendsX-Request-Id, so a write whose response was lost is retried safely (#8526, #8136). - Readiness is honest. The security-convergence gate is armed on a runtime join, per join, and on by default; a node re-added with a retained config volume no longer passes it on stale fingerprints (#7819, #8317, #8329, #8414). Followers are gated on the leader’s commit index (#7619).
- Two concurrent add-peer requests could commit two Raft peer ids for one address (#7802), and the embedded
addPeerAPI seeded no security documents at all (#7820). - A long-running seeded chaos test now exercises compound failure and recovery scenarios (#8279), and the HA integration tests run nightly instead of on every push.
New on the HA side: a bulk schema scope so a DDL script replicates as one Raft entry, peer-capability negotiation, peer priority on POST /api/v1/cluster/peer, joining a running node to a cluster, and per-peer capabilities and rolling-upgrade readiness in Studio (#6990, #7219, #7523, #7515, #7538). If you run ArcadeDB in client-server mode with a Raft cluster, this is the release to be on.
Lost Updates and Silent Data Loss
SET n = n + 1lost an increment underREAD_COMMITTEDwhen two transactions committed concurrently (#8538). A write computed from a read that went stale is now refused (#8610).- Concurrent updates of records that grow past a page lost committed updates in most runs with
txPageSlotMergeon (the default), and a document larger than one page was silently overwritten (#8985, #8982, #8988, #8989). - HASH indexes applied a key change twice at commit:
UNIQUE_HASHlet anUPDATEtake a key another record held, andNOTUNIQUE_HASHkept a stale entry (#8983). UPDATE ... SETon a nested map key or list index was silently lost, because the owner was never marked dirty (#8027);UPDATE ... REMOVE prop[0]on a Set deleted every element (#8032).- One interrupted commit permanently killed a WAL pool slot, so every later transaction on it blocked 30 s and failed (#7768).
- A UNIDIRECTIONAL edge created while its target was deleted committed as a ghost edge (#8986, #8676).
DROP TYPEleft the type’s aliases in the schema, so the dropped type came back alive after a restart (#8169);TRUNCATE TYPEnever refused a non-empty vertex or edge type (#8042).- Console data loss. The console split a command at the first closing brace followed by a newline, so a multi-line
UPDATE ... SET x = {json}ran with noWHEREand overwrote every row (#8246). - Clean close and WAL. A clean close now fsyncs only the files written since their last sync, and recovery fsyncs before it drops the WAL; a second embedded instance’s clean close could delete another live instance’s files (#8626, #7479, #7502); and a WAL housekeeping timer could delete recovery input (#8600).
- The schema is written once per DDL statement, never mid-transaction, and
schema.jsonis replaced atomically (#8635, #7279).
The Same Value Now Compares Equal Everywhere
The index, a SQL scan and Cypher disagreed on what “equal” means for several types, so the same WHERE returned different rows depending on the plan. They agree now for FLOAT/DOUBLE, DECIMAL, DATETIME, BOOLEAN, BigInteger, signed zero and null (#8884, #8885, #8886, #8887, #9113, #9114, #9160, #9274).
- Null equality is decided at execution, not at planning. A statement first run with a value kept its index plan, so a later run with
nullreturned the null-key records again, and aDELETEcould remove the wrong rows (#9274, #9238).UPDATE/DELETEwith positional parameters read theWHEREfrom an earlier cached statement (#9245). - Precision. A SQL decimal literal keeps digits beyond double precision, JSONL import keeps
DECIMALprecision, a suffix-less floating point literal is aDouble, and numeric comparison past 2^53 is exact (#8872, #8871, #7609, #7628). - Datetimes. An unparseable datetime is refused instead of stored as
NULL, andDATETIME_MICROSliterals with a space separator stop being truncated (#8090); sub-millisecond precision and temporal round trips were fixed in Cypher and over the wire. - Aggregates.
LONGsum overflow,COUNT(DISTINCT expr)(a syntax error until now), aggregates inORDER BY, andmin()/max()over an index range (#8972, #8889, #8973, #8812).
A result set that changes after the upgrade is the fix, not a regression.
Faster Queries
- Parallel scans, aggregation and
DISTINCT. Filtered scans and aggregation run on the dedicated scan workers, a transaction that has written nothing still scans in parallel,POST /queryno longer opens an auto-commit transaction that blocked it, and a satisfiedLIMITreleases its upstream (#8523, #8775, #8594, #8799, #8797). Scans also stopped re-resolving each record and build records from the page in hand (#8265, #8266, #8312). - The index serves more shapes: Cypher
ORDER BY ... LIMITfrom index order,LIKE 'prefix%',STARTS WITH, Cyphermin()/max(), CypherORindex seeks, case-insensitive index ranges, and an adaptive range fetch that parallelises large ranges (#8422, #8666, #8723, #8766, #8333). - Concurrent RID lookups scale. The database read/write lock is striped (#8838), and
checkDatabaseIsOpenno longer writeslastUsedOnon every call (#8523). - Plan caching.
UPDATE,DELETEandINSERTno longer re-plan on every execution (#9207), and a correlated LET subquery is memoized per outer binding (#8400, #8441). - Graph algorithms.
algo.wccuses a parallel union-find (Afforest) (#9133), andalgo.*waits for the restored Graph Analytical View on the first call after a reopen (#9220). - Cypher joins on disconnected patterns use a value hash join with compact, bounded buffers instead of buffering the full record of every right-hand row (#8583, #8584, #8585).
- A JVM-wide heap budget for query buffers keeps concurrent SQL and OpenCypher queries from exhausting the heap together (#8591).
- TimeSeries: tag-skipping mutable scans, merging of small sealed blocks, shared tag strings and bucket origin/offset for weekly buckets (#8574, #8798, #8794, #8793). For the numbers behind ArcadeDB’s engine speed, see the benchmarks.
Vector and Sparse Vector Search
Teams building retrieval pipelines on the GraphRAG stack get a round of correctness fixes in LSM_VECTOR and LSM_SPARSE_VECTOR:
- The graph rebuild loop stops when two records share a vector id, and
CHECK DATABASE FIXrepairs it (#8946). - Aborted transactions no longer leak tombstones or uncommitted vectors into vector indexes, a rebuild reads committed state only, and a delete reuses the persisted graph (#7931, #7974, #7842).
- Searches during an async rebuild returned poor neighbors; the first search after a reopen loads the graph via the ordinal map; a query that rebuilds a graph honours the JVM-wide rebuild limit (#8862, #8852, #7814).
- A
filterthat matches no records was treated as no filter and returned records outside it (#8959); grouped search fills every winning group (#8002). - Compaction lost commits and a search during compaction was wrong;
BINARYquantization, zero and non-finite vectors, and a window-based MaxScore for SPLADE top-K were fixed (#9132, #9252, #9200). - The HNSW build cache is sized from the heap ceiling, so a served database matches an embedded one (#7146).
New: vector.neighbors accepts a subquery result as its filter, dense vector search sees the open transaction’s rows, and sparse-vector indexes can be built over existing rows and rank by exact score (#7125, #7378, #8536, #8576).
Wire Protocols: TLS for Postgres and 100+ Fidelity Fixes
- TLS/SSL for the Postgres wire protocol (#8840), plus
COPY (<query>) TO STDOUTin text and binary format (#7188). - Postgres:
SETis session-scoped, transactional and read back bySHOW;COMMIT/ROLLBACKinside an implicit transaction block were silently ignored; a trailing semicolon bypassed the transaction-block state machine;ROLLBACK TO SAVEPOINTis refused instead of accepted as a no-op; portals are transaction-scoped;$Nparameters work on indexes; the Arrow ADBC driver bootstraps (pg_type,regclass); and binary arrays work (#8028, #8245, #8217, #7178). - MongoDB: lossless BSON types (Binary, regex, timestamp, MinKey, MaxKey and JavaScript were silently dropped), exact filter semantics, nested
$set, projection, unique_idand replace (#9062, #9137). - Redis: a newline batch was classified by its first command only, so a
GETfollowed by anHDELwas declared read-only;INCR/DECRare atomic on the query engine too;MULTI/EXECno longer double-applies; values are binary-safe;INCRBYFLOATis supported (#8247, #8248). - Bolt: node ids match
id(n), system procedures are served only when the statement is that call, and failures are named. - gRPC and remote clients: a lost response on a self-committing write is an unknown outcome, not a retryable error;
RemoteDatabase.transaction()has the partial-commit guard;RemoteServerdrop/createUser/dropUser go throughhttpCommand(#8525, #8062, #7796). - Gremlin: label loss, profile double execution, idempotent
drop, fractional index bounds and the advertised Gremlin port were fixed (#8258, #7408, #9147).
Security Advisories
This release closes four security advisories, published in full (impact, affected versions and credit) as GitHub Security Advisories on the repository. All four affect 26.9.1 and earlier, are patched in 26.10.1, and were reported by @manus-pi.
- GHSA-h2j4-28h8-cj5v (critical): the Gremlin Groovy engine lacked a scripting authorization check, enabling remote code execution by an authenticated user.
- GHSA-7fcg-pg8x-wf7w (medium): Bolt
SHOW DATABASESlisted every database on the server without per-user authorization filtering. - GHSA-hw9x-xx38-rg28 (medium): Bolt lacked per-database authorization, so an authenticated user could read another database’s schema metadata.
- GHSA-9c7g-grf7-j2r5 (medium): read-only users could change edge type
lightweight/uniqueschema flags viaALTER TYPE, bypassingUPDATE_SCHEMA.
Also Hardened in This Release
- Bolt applies the per-user database and type access to database selection and schema listings (#8415).
- Schema DDL gates:
ALTER TYPEedge-type settings andREBUILD TYPErequireUPDATE_SCHEMA(#8398). - Gremlin scripting is restricted further, including
io()and lambdas (#8227, #8338). - Request bounds: a chunked request body no longer bypasses
httpBodyContentMaxSize, gzip and Snappy ingest are bounded after decompression, and/wsframes are bounded (#7772, #8084, #8065). MongoDB wire regexes in aggregation are time-bounded (#9164), and a TimeSeries aggregation checks its caller’s ceiling before allocating buckets (#7476). - Secrets:
set_server_settingand the MCP request log mask hidden settings, andresetAll()no longer dumps the whole configuration (#8038, #8965). - AI chat store: the legacy-directory migration no longer awards a collided chat store to the wrong user, and accounts that differ only in case no longer share one (#7620, #8154).
- HA: five TLS and security issues were fixed, security mutations are refused off the leader, and the Raft gRPC peer allow-list is no longer frozen at startup (#7854, #8407, #7162).
- HTTP Basic credentials are split on the first colon only (#7783).
Major Fixes and Improvements
Storage, WAL and Integrity
- A write to a record the transaction already deleted is dropped, not reported as a deadlock (#7149).
- Bucket space reuse, a free tail measured directly on a rebased delete, and edge delete page merges were fixed (#8660, #8401, #9233).
- LIGHTWEIGHT edge types: reads answered 0, and
TRUNCATEandTRAVERSEwere wrong (#7477, #7480, #7481). CHECK DATABASEfinds records that violate their own type’s existence constraints (#7952), and a failedREBUILD INDEXrestores the old index (#9254).- A
LIMITquery could leave the database unable to close because cancelling its parallel scan interrupted a page read (#8944). - Windows backup archive nesting and path separators were fixed (#7586), and the ext4
lost+founddirectory is no longer registered as a database (#8805). - Backups cover the configuration files in the snapshot (#6114), and the HA snapshot ZIP skips index-compaction temporaries (#8019).
Indexes
- A range seek landed in the middle of a same-key run (#7611); transaction index ranges after compaction and the tx-overlay filtering rules shared across read paths were fixed (#8817, #6970).
CREATE INDEXon a populated type no longer truncates datetimes to milliseconds, andCREATE INDEX IF NOT EXISTSworks across super types (#7164, #7228).- SQL queries no longer fail while an index is created or dropped on the same type (#8855).
- Hash index overflow walk cost, prefix seek and lookup at page end were fixed, along with unique prefix lookup and
IS NULLon unique indexes (#9253, #8806). - A unique
BINARYkey skipped the uniqueness check because array keys were compared shallowly (#7881).
SQL
- Parser re-render,
copy(),LIMITcounting, positionalLIMIT ? SKIP ?binding, comments inORDER BY/GROUP BY, hex literals, nesting guards and planner exceptions were fixed (#7800, #8434, #9122, #9154). - A scalar left operand of
CONTAINSANYsilently matched nothing, andexpand()of a native array yields one row per element (#8475, #7910). - The
UPDATEHalloween problem,GROUP BYprojection alias rebuilt per record,ORDER BYover a null scan, andFULL_TEXTnever answering exact lookups were fixed (#8814, #8260, #8664, #8442). - SQL scripts replay blocks correctly, and
EXPLAINhas parity across sqlscript operation types (#8633, #7576).
openCypher
- Relationship uniqueness is scoped to the clause,
MERGEbinds the path it merged, and an eager read/write barrier means a query never reads back what it just created (#7165, #7169, #7171). MERGE ... ON MATCH SETno longer loses a concurrent increment, nodes are written once forMERGE ... SETandCREATE ... SET, andLIMITafter a write no longer cuts the write short (#8538, #8735, #8826, #8827).- An empty
FOREACHchangedOPTIONAL MATCHresults;FOREACH/REMOVEscope checks, existence constraints checked at end of statement, andCALL (*)no longer inheriting the outer clauses’ variables were fixed (#8733, #8105, #9205). - Unidirectional
in()/both()with a view, queries on the incoming side of unidirectional edge types,DELETEof a node with several relationships, and code-point string handling were fixed (#8939, #8625).
TimeSeries
- A
TIMESTAMPcolumn not declared first round-tripped through JSONL with its values in the wrong columns (#7899), and aggregation push-down mixed row and schema column indexes between the mutable and sealed halves (#8140). - A query now gives one answer whichever layer holds the data; sealed-store identity and repair were fixed, and an empty
TAGkey is refused instead of dropped silently (#7733, #8738, #8563). - PromQL conformance gaps were closed, and
ts.timeBucket()is exposed asLocalDateTime(#8926, #7610).
Importer, GraphBatch and Async
- Importer transaction ownership, RDF and CSV edge cases, row-error handling and post-import validation were fixed (#7943, #7948).
GraphBatchwrote the header end offset one byte short on property-less edges, wrote a null for a declared edge property as a type tag with no value, and now honours the caller’s transaction (#7448, #9018, #9242).- A periodic batch-commit conflict is retried instead of silently discarded (#7615), and
insert_manyandGraphBatch.create_vertexroll back on every failure path (#7882).
Upgrade Checklist
Behaviour changes are collected in the release notes. The ones worth checking before you upgrade:
- Security-convergence readiness gate is on by default on HA clusters, armed per join. A joining node is not reported ready until its security state matches the leader’s (#7819, #8414).
arcadedb.server.apiTokenRequireSecureTransport: check the first-run Docker flow onhttp://localhost:2480, where Studio’s API-token mint is refused once it is on (#8765, #7804).- Stricter refusals instead of silent acceptance: an unparseable datetime, a map value the declared type cannot take,
Type.convertof an impossible value,ROLLBACK TO SAVEPOINTover the Postgres wire, anUPDATEwhose value was computed from a stale read, andTRUNCATE TYPEon a non-empty vertex or edge type (#8090, #9110, #8610, #8042). - Queries that returned a plan-dependent answer now return the equality-consistent one (null keys,
FLOAT/DECIMAL/DATETIME/BOOLEAN, signed zero). - Index default page size changed (#9250); existing indexes keep their size.
- Schema DDL gates:
ALTER TYPEedge-type settings andREBUILD TYPEneedUPDATE_SCHEMA(#8398); Gremlin scripting andio()are restricted (#8227). - Rolling upgrade of an HA cluster: peers negotiate capabilities, and group or API-token changes wait until every peer supports them; Studio’s cluster page shows the state (#7219, #7538).
LSM_SPARSE_VECTORrescoring oversamples by default (rescoreOversample2) and ranks by exact score (#8576).
Dependency Updates
About 140 dependency bumps landed in this cycle, almost all through Dependabot. The notable ones: Netty 4.2.18, Apache Ratis 3.3.1, Gremlin (TinkerPop) 3.8.2, GraalVM 25.x, JVector 4.0.1, Jackson 2.22.3, Protobuf 4.36.2, SLF4J 2.0.20, Logback 1.6.5, OpenTelemetry 1.66.0, SnakeYAML 2.7, lz4-java 1.12.0, commons-lang3 3.21.0, JLine 4.4.6, Neo4j Java driver 6.3.0, Jedis 8.0.1 and gRPC protos 2.78.0. On the Studio side: ApexCharts 7.6.1, marked 18.0.14 and swagger-ui-dist 5.33.0.
Getting Started with 26.10.1
Docker
docker pull arcadedata/arcadedb:26.10.1
Visit our Docker Hub repository for more information.
Maven
<dependency>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-engine</artifactId>
<version>26.10.1</version>
</dependency>
All artifacts are available on Maven Central.
Documentation
For details on features and usage, see the documentation.
Download ArcadeDB 26.10.1 now: GitHub Releases
Thanks to everyone who reported, reproduced, reviewed, tested and fixed, in particular @ruispereira, @tae898, @rspereiratech, @YGY-001, @jjj-n, @gramian, @Das-Rabindra, @singhpratech, @rlaveycal, @wkpark, @rbonestell, @justinblethrow-cloud, @philiptran-tech, @GYWang1983, @astarso, @mdre, @ivan-velikanov, @willemijn-zeno, @shlomiassaf, @nx-mama, @maurovit, @LetMeSleep8h, @suhanrain, @manus-pi, @BingEdward, @anatshad, @borutjures, @ExtReMLapin, @jawahar4k, @JMQuill-SF, @M-Tesla, @odysseaspenta, @sainiteesh36, @shulei5831sl, @singh-hovr and @wsalembi.
Luca Garulli ArcadeDB Founder