ArcadeDB 26.10.2
Overview
682 issues and pull requests closed under the 26.10.2 milestone - 485 issues and 197 pull requests - out of 355 pull requests merged and about 1,360 commits since 26.10.1. 377 of the closed issues are bug fixes, 20 of them rated critical.
The headline work is in six places:
- Query control and speed - a JVM-wide query admission gate, list and terminate running queries over HTTP and every wire protocol, SQL and openCypher statements that differ only in their literals now share one parsed statement and one plan (a point lookup with the value in the text is 3 to 7 times faster), a parallel
GROUP BYthat evaluates each key once, and Customizable Contraction Hierarchies for shortest paths. - High availability (Raft) - schema and database-setting changes made outside a DDL statement now reach every node, vectors inserted on a follower no longer replace other vectors, an in-place Ratis restart no longer double-applies a commit, and a node removed from the Raft configuration is fenced. A follower's refusal of a write is a redirect on Bolt, MongoDB, Redis and gRPC, naming the leader.
- Time series and Prometheus - retention and compaction no longer corrupt or resurrect data in the sealed store, writes that used to be stored silently wrong are refused or reported, and PromQL follows Prometheus on a dozen edge cases.
- openCypher - temporal values (
time(),localtime(), zoneddatetime(),duration()) are stored natively,MERGEmatches exactly whatMATCHmatches, GQL constructs the engine used to ignore are refused, and thepath.*procedures answer as APOC does. - Security hardening - open wire-protocol and
/wssessions re-check the user on every request, SQL classifies a statement by everything nested inside it, lightweight edge types enforce create and delete grants, a type'sEXTERNALbucket follows the type's permissions, and Studio no longer runs HTML from an AI answer. - Silent wrong data - lost keys during a concurrent commit, a schema reload that issued the same RID twice, a schema change lost at close, LSM compaction resetting the null strategy, and WAL recovery re-issuing transaction ids.
Upgrading is recommended for every deployment, and in particular for every HA cluster and every time series workload. Behavior changes are collected in Upgrade notes.
Contents: New features · Highlights · Security · Major fixes and improvements · Upgrade notes · Dependency updates
New features
Server and operations
- Query admission gate (#9518). A FIFO queue in front of every remote protocol (HTTP, Postgres, Bolt, Redis, MongoDB, gRPC, MCP): when the server is busy, requests wait and start in arrival order instead of all starting at once and exhausting the heap. On by default (
arcadedb.queryMaxConcurrent, 2 x cores and at least 4,0disables it), bounded byarcadedb.queryQueueTimeoutandarcadedb.queryQueueMaxSize, and heap-aware througharcadedb.queryAdmissionHeapWatermark. A refused request is retryable on every protocol (HTTP 503, Postgres40001, RedisTRYAGAIN). - List and terminate running queries (#9680, #9689).
list queries [database <db>] [tag <t>],terminate query <id>,terminate queries tag <t>,list transactionsandterminate transaction <sessionId>overPOST /api/v1/server, with anX-ArcadeDB-Query-Tagheader to find your own statements and an acknowledgement that says whether the statement stopped. Bolt, gRPC (streaming loads included), MCP, the Postgres wire and/wsinsert sessions register their work too, and every entry names its client (#9774, #9838, #9875, #9962). - Statement and plan cache by query shape (#8307). SQL and openCypher texts that differ only in their literals are parsed and planned once. Measured on a unique-index point lookup, Cypher drops from 42 to 6.1 microseconds per query and SQL from 21.5 to 6.6; a query that already uses parameters costs what it did.
- Docker images run on Java 25 (
eclipse-temurin:25-alpine), so compact object headers are finally enabled in the image (#9602). The native image now ships Raft HA, metrics and tracing (#9407), runs the e2e battery in CI (#9495), and uses scalar vector math instead of the uncompiled Vector API, which was 6 to 27 times slower (#10072). - Studio is served with a Content-Security-Policy that refuses inline script (#9681), and the saved-query and history sidebar, the Reference panel, the sample-database cards, the API endpoint list and the Profiler rows are keyboard accessible (#8740, #9375, #9508).
- HA operations: an operator override to lift a quarantine or the stale-snapshot read floor on a sole voter (
POST /api/v1/cluster/accept-diverged/{database}) (#9449, #9498);GET /api/v1/clusterexposes the follower's replication-path and unreachable-leader state and in-place restart counters, with matching alerts (#9399, #9429); an alert when every voter has quarantined the same database (#9553); the remaining Raft executors published as pool metrics (#8856); and a configurable DEFLATE level for snapshot ship (arcadedb.ha.snapshotCompressionLevel) (#9295). - Backups: SQL
BACKUP DATABASEapplies the retention policy (#9844), and a full backup archives a vector index's graph consistently so a restore reuses it (#9917).
Query languages and engine
- Native openCypher temporals: four new property types,
OFFSET_TIME,LOCAL_TIME,ZONED_DATETIMEandDURATION, indexable byLSM_TREEandHASH; a String is never reinterpreted as a time again (#8572, #9462, #9636). - Customizable Contraction Hierarchies: a Graph Analytical View keeps a CCH per weight property (
CREATE GRAPH ANALYTICAL VIEW ... CCH (distance)), so weighted point-to-point shortest paths no longer depend on the distance (cchShortestPath(),algo.cch.shortestPath), and the hierarchy keeps up with weight updates in milliseconds (#9437, #9915). path.*procedures answer asapoc.path.*: APOC's filter syntax, nineuniquenessmodes,endNodes,terminatorNodes,allowlistNodes,denylistNodes,optional, a lazy walk that alimitreally stops, and a heap-charged breadth-first walk (#10025, #10048, #10049, #10017).- openCypher label expressions on variable-length relationships (
[:!R*],[:%*],shortestPath((a)-[:!R*]-(b))) now run (#9117). - SQL:
CREATE INDEX ... METADATA {"buildMode": "SORTED"}selects the sorted build (#9291);CREATE GRAPH ANALYTICAL VIEW v PROPERTIES ()builds a view with no vertex properties (#9831);algo.labelpropagationtakes atieBreakProperty(#9285);CHECK DATABASEreports unreadable stripe directories (#9266); GremlinhasLabelsees types created earlier in the same traversal (#9335). - PromQL: the
boolcomparison modifier (a > bool 1) (#9644).
Remote clients
- Typed "already exists" exceptions: the Java remote clients raise
RemoteAlreadyExistsExceptionfor a taken user, token or database name, and the server answers 409 for them over HTTP (#10073, #10074, #10075). RemoteSchema.alterMaterializedView()is implemented (#8726), a streamed error line rebuilds its typed exception (#8874).
Highlights
High availability: schema, settings and recovery reach every node
- Changes that were saved on the leader only now replicate.
ALTER PROPERTYconstraints and thePropertysetters, schema extensions, theALTER DATABASEdate formats andsetZoneId,ALTER TYPE ... WITH lightweight / unique, the time series downsampling policy,ALTER TYPE ... NAME(type, bucket, index and edge files),ALTER DATABASE/set database setting, a JSONL import's triggers, views, continuous aggregates and functions, anddb.migrateLegacyTemporals(). A constraint went unenforced on a node elected after a failover and date settings answered differently per node (#9747, #9762, #9763, #9764, #9712). During a rolling upgrade a rename or a setting change is refused with 409 until every node understands it. - Vectors inserted on a follower replaced other vectors. A replicated vector index page did not move the follower's id allocator, so the next insert reused an id the leader had allocated:
countEntries()stayed flat and the superseded vectors dropped out of search (#9428). A follower's vector graph also picks up vectors replicated after its first search (#9857, fixed by @blaus23 in #9859), and a snapshot install or a full backup no longer reuses a vector graph without the manifest that vouches for it (#9758, #9888). - An in-place Ratis restart double-applied a commit, and several replicated commit paths never fired after-commit callbacks (which also lost Redis transactions that only changed in-memory values) (#8785, #8784).
- No leader after a leader loss in a 3-node cluster. Once a stopped node was gone its host name could resolve to the node asking for it, so every Raft message meant for the dead node was delivered back to the sender and answered as if it were that node: the new leader received its own heartbeat and stepped down, and every election then counted its own vote twice. A node now refuses any Raft message addressed to another node and reconnects to a peer when it notices it has messaged itself (#10016, reported on a real cluster). A closed state machine refuses commits at once instead of waiting the quorum timeout (#9411), the Ratis close is bounded on
stop()and on an in-place restart (#9561), and a log close can no longer deadlock with an in-flight cache eviction (#9556). - Two committed schema changes gave the same file id to different compacted index files, quarantining the database on every node (#9547). A restarted node ran out of heap catching up about 61,000 Raft entries on a 1 GB heap; the entry cache is now bound to the heap (#9549).
- A node removed from the Raft configuration no longer accepts writes that stall for the quorum timeout, and refuses consistent reads, drops and installs (#9510, #9590, #9589).
- The proposer of a refused schema entry is quarantined and resynced instead of staying diverged, and a bootstrap fingerprint entry applied during shutdown is left for replay (#9555, #9558, #9568). A sole voter no longer quarantines a database on an unexpected apply error (#9308).
- A schema change no longer breaks time series writes on followers: every schema reload closed the files of each time series type that held data, so the next write failed and the follower resynced (#9886).
- A follower's refusal of leader-only work is answered at once (
Database.transaction()used to retry it through everytxRetriesattempt) and always names the leader, never the refusing node (#9995, #10066, #10060). - Cluster credential on every peer dial is built in one place (
PeerRequest), so the token and forwarded user always travel together (#9999, #10068).
Lost updates and silent data loss
- Index lookups lost a key while a commit published: a point lookup,
Database.lookupByKey,pid IN :idsin SQL and in openCypher missed (11 to 23 percent of reads in the reproduction) or served another key's record when a transaction deleted and re-created a key (#9369, #9397). - A schema reload issued the same new page and RID to two transactions, because the reloaded bucket started with an empty page-reservation state (#9835).
- A schema change pending at close was lost whenever the schema held an index: the save ran after the database reported itself closed, failed at
SEVEREand left the type or index gone after a reopen (#10059). - LSM index compaction reset the null strategy to
SKIPforERRORandINDEXindexes, and saved it toschema.json(#9717). - WAL recovery set the next transaction id from the last replayed transaction, not the highest, so after a crash with two concurrent commits the database re-issued transaction ids (#9795).
include()/exclude()returned a dirty record stamped with the source RID, so saving a projection truncated that record and orphaned its edges (#9840).INSERT ... FROM SELECT *, !secretcopied the excluded column (#9965).- A MongoDB insert made while the caller held a transaction committed its own and rolled the caller's back (#9977).
- A refused
DELETE_RECORDhad already removed the record's index entries, EXTERNAL values and edges; the check now runs first (#9305). An edge iterator'sremove()unlinked an edge the user may not delete (#9779). - A multi-page record could be read torn under
REPEATABLE_READafter a concurrent rewrite (#9070), a flush suspension released on the wrong thread left every later backup, freeze and close timing out (#9832, #9927), and an async batch on a one-bucket type lost 1,000 records in 24 of 6,000 runs until its replay waited a jittered backoff (#9529, #9694). - The Python driver rolls back after an interrupt in
begin(), andrun_in_transactionretries aLockTimeoutException(193 failures escaped in the reproduction) (#9321, #9629, #9970).
Time series and Prometheus: the sealed store stops losing and resurrecting data
- Retention appended its rewritten blocks after whatever an abandoned rewrite left in
.ts.sealed.tmp, so blocks it had just dropped came back on the next open and shipped to every follower (#9770).COMPACT TIMESERIES TYPEracing a retention pass corrupted the live store's in-memory directory and silently undid the retention at the next open (#9771). Acknowledged rows were missing after akill -9during a concurrent compaction (#9452). PRECISION SECONDlost every sample at the first retention pass, and aNANOSECONDtype never expired and answered nothing in PromQL, because no timestamp code read the precision (#9800).- Sealed store I/O: a thread interrupt closed the shared channel for every reader until the next compaction (#9621), and a short read dropped every later block of the directory (#9640).
- Writes are strict. An empty tag value, an undeclared tag or field key, a quoted datetime in a typed column and a value the column cannot store are refused or reported as a partial write instead of stored wrong (#8647, #8646, #9809, #9643).
arcadedb.timeSeriesUndeclaredKeysrestores the lenient behavior for schema-less producers. - Reads: an unbounded
SELECT FROM <timeseries type>and the gRPC stream no longer load the whole range (a 50M-point type ran out of heap) (#9420),scanRangeDescendingno longer stops early on overlapping blocks (#9982),ts.rankranks by value andts.movingAvgkeeps one slot per row (#9956, #9958), an ungroupedmin()/max()of aLONGpast 2^53 is exact (#9710), andcount(*)is answered from block statistics (#9612). - Prometheus: remote read keeps non-equality matchers and no longer merges series whose labels contain
|(#9803, #9623),/seriesappliesmatch[](#9386), remote write converts labels into typed tag columns and answers 415 to a 2.0 body (#9972, #9624), and PromQL refuses what Prometheus refuses (#9723, #9743, #9788, #9883, #9922, #9924).
Equality and typing agree everywhere
MERGEmatches exactly whatMATCHmatches: a storedFLOAT, a temporal against aSTRINGproperty, and-0.0/0.0no longer create duplicates or raise a duplicated-key error (#9693, #9616, #9617, #9618).- A pattern finds what it stored in a declared scalar property.
MERGE (n:S {v: 1})stores'1'in aSTRINGproperty and used to create a duplicate every time; the same holds for text written intoINTEGER,LONG,FLOAT,DOUBLE,BOOLEAN,DATEandDATETIME(#9695, #9701, #9794, #9935). - A
COLLATE ciindex no longer changes the answer of=/IN(#9403),k = [1, 2]is no longer read asIN(#9370), SQLsum()of an empty group is NULL (#9351), and an equality on aZONED_DATETIMEproperty agrees with and without an index (#9648). - Weighted shortest-path finders share one edge-weight rule: a missing weight is 1, a negative, NaN or infinite weight is not walked, in
dijkstra(),astar(),duanSSSP()and everyalgo.*finder (#9443). - A statement is classified by everything nested inside it, so a write hidden in a
LET, a projection or a sub-query is no longer run through the read-only paths (#9628).
Faster queries
- Statement and plan sharing by query shape (#8307, #9818, #9714) and admission control (#9518) - see New features.
GROUP BY: each key is aggregated once in a parallel scan, aggregates are evaluated off bound columns, and the in-heap budget no longer scales with the number of cores (a query that ran on 1 or 2 cores failed on 4 or more) (#9496, #9402).- Variable-length hops that need only distinct end vertices walk a visited set instead of every path:
MATCH (a)-[:LINK*1..D]->(b) RETURN DISTINCT bover 200,000 vertices and 1.8 million edges went from not finishing at depth 9 in 120 s to about half a second at depth 12 (#10062, contributed by @tae898). - LSM index scans no longer go quadratic after a hot key (#9461), bucket scan read-ahead is bounded by bytes (#9404, #9401), a commit skips the hole proof for pages written only by hole-free writes (#9483), and a scan stops paying a schema lookup per record (#9997, #10013).
- Count push-downs through a Graph Analytical View (LSQB Q2, Q3, Q7, Q8, Q9) take property predicates, Cartesian products and pass-through
WITH, and are exact on parallel, reciprocal, self-loop and light edges (#9298, #9350, #9354, #9484, #9485, #9486, #9595, #9596, #9597, #9608, #9282). - An index lookup that overlaps a commit no longer re-reads the whole key up to 9 times (#9902).
- Bolt coalesces RECORD messages into a few large socket writes (#9353) and no longer accumulates a whole PULL on the heap (#9372); a vector index rebuild is no longer deferred forever on a large index (#7260); the sparse top-K splits from 50,000 postings (#9482); and a time series grouped aggregation keeps its bucket cursor across rows (#10003).
Security
Authorization, redaction and input-bounds fixes found by audit and by reporters.
- Long-lived wire connections re-check the user (#9279). PostgreSQL, Bolt, MongoDB and Redis sessions re-resolve their user against the live security state on every request, as HTTP does: a deleted user, a changed password or a revoked grant now applies to connections that are already open.
/wsinsert andSUBSCRIBEframes do the same (#9311, #9312, #9340, #9424). - SQL classification looks inside nested statements (#9628).
LET $x = (INSERT ...),SELECT FROM (CREATE TYPE ...)and similar were reported as reads and ran throughGET/POST /api/v1/queryand read-only MCP identities.PROFILEfollows the statement it wraps (#9313), and GraphQL type definitions needUPDATE_SCHEMA(#9280). - Lightweight edge types enforce
createRecordanddeleteRecord(#9619, #9779); a vertex type's super-node stripe buckets and a type's pairedEXTERNALbucket follow the type's permissions and lock (#9780, #9637). DELETE_RECORDis checked first, before any index or graph cleanup (#9305).- LOAD CSV settings are security-sensitive (#9371): remote URLs, local files, the import directory and the blocked IP ranges need
updateSecurityto change throughALTER DATABASE. - Support diagnostics and logs redact
arcadedb.server.defaultDatabases, including the JVM arguments uploaded to the portal and the two log lines that spelled the setting with a space (#9625, #9773). Rotate any password configured this way on a server that already uploaded diagnostics. Studio's Support page no longer disables a column's auto-mask for good after one unmask (#9797). - Studio AI answers can no longer run HTML (#9626): the answer and the profiler analysis use an escape-first markdown renderer,
markedis no longer shipped, and Studio is served with a Content-Security-Policy (#9681). The AI Assistant caps every read of the portal and gateway answers (#9627). - gRPC:
arcadedb.grpc.tls.enabledand the otherarcadedb.grpc.*settings are read fromserver-configuration.jsonand the environment (asking for TLS in the file used to leave a plaintext endpoint listening), andarcadedb.grpc.hostis honored (#9316, #9318). - Production mode conceals error text on the Gremlin Server for every channelizer, on the
/wsinsert session, Bolt and Redis (#9317, #9639, #9749, #8749). The Python server's loopback-by-default hardening now covers the Postgres, Redis and Bolt listeners (#9632). - HA: security entries submitted from a node removed from the Raft configuration are refused (#9589).
Major fixes and improvements
Storage, WAL and integrity
- A schema reload keeps the page reservations, an interrupted flush resume no longer abandons batches, dropping a database with suspended flushes releases its WAL acknowledgements, and closing the last database inside
suspendFlushAndExecuteno longer fails with aNullPointerException(#9835, #9832, #9881, #9927). - A subtype that inherits an
EXTERNALproperty stores its values in its own_extbucket (#9683). A compacted LSM bucket index keeps its name across a restart and an HA schema reload (#9213, #9434). CHECK DATABASEreads each record of a type withMANDATORY/NOTNULLproperties once (#7981), and a failed read of a closed component file throwsIllegalArgumentExceptioninstead ofNullPointerException(#9306).- Interrupted snapshot reads reopen the channel instead of invalidating open windows or aborting a backup (#9306); a full backup skips index-compaction temporaries (#8848); orphan compaction temporaries are swept at open (#8849).
- An impossible
REGEXP, and aMANDATORYor default-valued property on aLIGHTWEIGHTedge type, are refused at declaration time (#9853, #9846, #9990, #9975).
SQL and indexes
LIKE '%'matches an empty string (#10005),values()on a sub-query row lists exactly the values ofkeys()(#9843), nested projection prefix wildcards@this:{s*}work (#9841), and a bareORDER BY @ridon aMATCHthat returns columns is refused instead of silently ignored (#9911).- A
MATCHnode withrid:honors itstype:andbucket:, and a node filtered bySEARCH_INDEX()is streamed instead of loaded whole (#9912, #9908).SEARCH_INDEXpushes@ridranges and merges score-orderedORbranches before loading records (#9895, #9909, #10022). UPDATE ... UPSERTrequires an equality on every property of a UNIQUE index (#9339, #9342); an asyncSELECTwith parameters binds them, not the executor's configuration (#9941); concurrentCREATE INDEX IF NOT EXISTSno longer fails with aNullPointerException(#9741);Schema.copyTypeno longer rolls back the caller's transaction (#9851).- The BM25 corpus counters of a
FULL_TEXTindex move when the transaction commits on every node and no longer race writers; the repair is kept and the counters are inspectable (#10018, #10019, #9337).SEARCH_FIELDSno longer matches terms in fields it was not given (#9910). vector.sum,vector.avg,vector.minandvector.maxaggregate when written with the dotted name, in SQL and openCypher (#9768);sum()andavg()work overBYTEvalues, andagg.*functions acceptFloat[]andShort[]properties (#9281, #9777).- A
COLLATE ciindex is skipped by nativeselect()range cursors and Gremlin rangehas(), andselect()with aLIMITreturns the rows the unindexed query does (#9302, #9613).
openCypher
- GQL constructs the parser accepted and the engine ignored are refused (
ANY SHORTEST,REPEATABLE ELEMENTS,IS DIRECTED, a nested quantified group), a path variable over a quantified group binds onePATH,LET,IS NORMALIZEDand the conditional queryWHEN ... THENrun, andRETURN ... ORDER BY/SKIP/LIMITis applied after the sort as one page (#9940). - A variable-length
MATCHhonors a previously bound relationship list (#9828, #9854, contributed by @mvanhorn),WHERE (booleanVariable)is no longer parsed as a pattern (#9542), and a variable cannot be both a node and a relationship in a pattern predicate (#9292). DROP CONSTRAINT <name>drops what the constraint created,SHOW CONSTRAINTS/SHOW INDEXESlist what exists, andIS :: <type>keeps the property's other constraints and refuses attributes the new type cannot use (#9646, #9706, #9304, #9645).- Write conflicts are retried by
Database.transaction(..., retries)like SQL's (#9487); temporal values compare under everydateImplementationand render withLocale.ROOT(#9647, #9649); durations are exact, ISO text is read as Neo4j reads it, and arithmetic raises on overflow (#9338, #10055). algo.bridgestreats edges as undirected andalgo.kShortestPathstakes a direction (#9766, #9914),algo.*keepstoleranceanddirectionon the accelerated path (#9829), and the count push-downs of a Graph Analytical View stay exact (see above).
Graph engine and analytical views
- A view over only some of the vertex types no longer accelerates count push-downs, traversal, shortest paths or
algo.*(#9301); a lightweight edge created or removed after the build reaches the view (#9572); a vertex whose edge list has an empty segment between non-empty ones no longer loses edges in the view or inCHECK DATABASE(#9715); parallel lightweight edges are distinct relationships in=,DISTINCTandDELETE(#10008). - A view restored from its persisted CSR after a reopen gives the speed-up it reports (#9587), a fused 2-hop
count(*)no longer holds every path on the heap (#9738), and a plainOPTIONAL MATCH ... WHERE h IS NULLplans as an anti-join (#9598). GraphBatch.withLightEdges(true)needs aLIGHTWEIGHTedge type (#9378).
Vector and sparse vector
vectorNeighbors(),vector.neighbors,db.index.vector.queryNodes,vector.sparseNeighborsandvector.recommend(grouped or not) top up candidates that a concurrent commit deleted instead of answering short (#9368, #9858, #9697, #10002, #9692, #9396); thedb.index.vector.queryNodesreport (#9858) is from @ofir-blaus.BINARYquantization reranks past deleted candidates and in grouped searches (#9633),vector.approxDistanceINT8keeps its per-vector scale (#9776), a sparse-vector update answers on its new dimensions (#9343), and FP16 and INT8 sparse searches no longer miss a document that belongs in the top-K (#9834).- A location load that stops partway no longer lets new vectors reuse ids held by other records (#9892).
Wire protocols
- Postgres:
SHOW,DISCARD,DEALLOCATE,CLOSEandsearch_pathare handled;START TRANSACTION,ABORT,BEGINwith isolation modes andAND NO CHAINare accepted;statement_timeoutis enforced; the extended protocol forwards a follower's DDL and writes to the leader; and a write inside an explicit transaction on a follower stays inside it (#9326, #9327, #9328, #9329, #9631, #9954, #10040). - Bolt: an index compaction left half-replicated no longer breaks every lookup with
ArrayIndexOutOfBoundsExceptionon a follower (#10046, reported by @rlaveycal). - MongoDB: a
MAPproperty with a non-String key no longer fails the wholefind()(#9973), and a follower's refusal isNotWritablePrimary(#9845). - Redis: a
MULTI/EXECblock retried by the HTTP auto-commit wrapper no longer appliesINCRtwice,HDELreports the records it removed and a multi-keyHDEL/HSETis one unit of work (#9322, #9837). - gRPC:
ExecuteCommandhonorsgrpcQueryMaxResultRows(#9861),arcadedb.grpc.compression.enabledis applied (#9782), a lost answer on a write is an unknown outcome, not a retryable error (#8822), and aZONED_DATETIME,OFFSET_TIME,LOCAL_TIMEandDURATIONkeep their type on the wire (#9969, #10034). - GraphQL: selections that share a response key are merged, and a sub-selection on a
LINKproperty is no longer dropped (#8744, #9467). - Gremlin:
elementMap()rows have one JSON shape, a nested map is named like the row (@rid,@type), the plugin fails its start on a port bind error, and the index step is guarded against a concurrentCREATE INDEX(#9576, #9781, #9319, #9651). - HTTP: a statement longer than
arcadedb.network.socketTimeoutis no longer killed with no response (#9953), the write watchdog survives an XNIO selector wakeup race (#9439), and the streamed query error line carries the classified label andretryAfter(#8899). - Remote client:
SELECT @rid FROM Personand GremlinelementMap()keep their metadata columns (#9804), and a malformedDuplicatedKeyExceptionno longer crashes the driver (#9473).
Importer, GraphBatch and async
- A 200M-vertex / 2B-edge
GraphImporterload no longer stalls in pass 1 (#9575); an async executor batch waits a jittered backoff before replay (#9529, #9694). - The native image resolves reflection-free SQL AST copies, Lucene and the missing function metadata, which fixed
UPDATE/DELETEover HTTP answering 500 withNoSuchMethodException(#9495, reported by @zek0), and registers the gRPC messages and Netty's ticker (#10070, #9564).
Python bindings and tooling
- The Python bindings are updated for 26.10.1: results that release the engine, exact
Decimalandbytesparameters, and tripwires for open engine bugs (#9294, contributed by @tae898). An interrupt duringbegin()pops only the level it pushed (#9970).
Upgrade notes
Each item links to the issue with the full migration detail; the complete write-up is in docs/release-26.10.2.md.
- Query admission gate is on by default (#9518). Past
arcadedb.queryQueueTimeoutorarcadedb.queryQueueMaxSizea request is refused as retryable (HTTP 503, Postgres40001, RedisTRYAGAIN). Setarcadedb.queryMaxConcurrent=0to disable it. - Docker images run on Java 25, and Studio is served with a Content-Security-Policy that refuses inline script (#9602, #9681).
- HA: the snapshot ship compresses at DEFLATE level 1 by default, so a resync over a WAN sends more bytes (
arcadedb.ha.snapshotCompressionLevel=6restores the previous level) (#9295). A follower restarted in place is no longer reformatted automatically (#8901). During a rolling upgrade a type rename,ALTER DATABASEandset database settingare refused with 409 until every node runs this build, and a rename cannot be downgraded past this release on its retained log (#9762, #9763). An index that already lost vectors to #9428 stays short and has to be rebuilt. - A follower's refusal is a redirect: Bolt
Neo.ClientError.Cluster.NotALeader, MongoDB10107 NotWritablePrimary, RedisREADONLY,/wsand gRPCNOT_LEADERwith the leader's address. A client that matched the old retryable codes must handle the new ones (#9845, #9868, #9930). - Queries that answered differently now answer consistently:
- a plain
=/INon aCOLLATE ciindex is case sensitive (#9403); k = <list>no longer expands (#9370), SQLsum()of an empty or all-NULL group is NULL (#9351), andUPSERTneeds a UNIQUE index (#9339);dijkstra(),astar()and thealgo.*finders count a missing weight as 1 and skip negative weights (#9443);MERGEandMATCHagree, and a declared scalar property matches the text it stored (#9693, #9695, #9794);path.expandconfigandpath.expandreturn every trail by default,path.subgraphallreturns every relationship between its nodes (#10025, #10048, #10049);include()andexclude()return a map (#9840),values()on aResultfollowskeys()(#9843), a bareORDER BY @ridon aMATCHwith columns is refused (#9911), and a path variable over a quantified group binds onePATH(#9940).
- a plain
- openCypher temporals are stored natively. A database that holds Cypher temporals in undeclared properties reads them back as Strings until the property is declared with the new type and the values are copied through a parked property (see the migration recipe in
docs/release-26.10.2.md) (#8572, #9636). - Stricter refusals instead of silent acceptance: time series writes with an empty tag value or an undeclared key (
arcadedb.timeSeriesUndeclaredKeys=ignorerestores the old behavior) (#8646, #8647), an ISO duration with no component or a misplaced fraction (#10055), an impossibleREGEXP(#9853), aMANDATORYor default-valued property on aLIGHTWEIGHTedge type (#9846), a statement that hides a write inside a read (#9628),GraphBatch.withLightEdges(true)into a type that is notLIGHTWEIGHT(#9378), and the GQL constructs listed above (#9940). - PromQL refuses what Prometheus refuses: a comparison between two scalars without
bool, a set operator with a scalar, a range vector or a string as an operand, a scalar argument toabs,ceil,floorandround, and a range query that returns a range vector or a string (#9723, #9743, #9788, #9883, #9922, #9924). - Existing names answer 409: creating a user, a token or a database under a taken name no longer answers 403 or 400 (#10073, #10074, #10075).
- Vector graphs: a snapshot install and a full backup now ship the
.vecgraphempty, so the first search after a restore builds and verifies its own graph; runREBUILD INDEXon the vector indexes of archives taken before this release (#9758, #9888). - Subtype
EXTERNALvalues written before the upgrade stay inline untilREBUILD TYPE <Subtype>moves them (#9683). - Gremlin Server in production mode refuses to start with a channelizer that cannot hide error details (#9639);
BACKUP DATABASEnow prunes the archive directory by the retention policy (#9844);arcadedb.grpc.compression.enabled=falseis honored (#9782). - Embedded API:
SQLFunctionHeuristicPathFinderAbstract.MINis gone (usecom.arcadedb.graph.EdgeWeight),Schema.alterMaterializedView()validates its arguments, and schema setters called directly on an HA follower throw the "schema changes need the leader" error (#9443, #8726, #9747).
Dependency updates
About 30 dependency bumps landed in this cycle, almost all through Dependabot. The notable ones:
- Server and engine: Netty 4.2.19, Undertow 2.4.4, Lucene 10.5.2, Jedis 8.0.2, JLine 4.4.7, snappy-java 1.1.10.11 and the PostgreSQL JDBC driver 42.7.14. The Docker base image moves to Eclipse Temurin 25.
- Studio: ApexCharts 7.8.0, swagger-ui-dist 5.33.1 and datatables.net 3.1.3;
markedis no longer shipped (#9626). - Build and test: Cucumber 8.0.4, Playwright 1.64, black 26.10.0 and the Go module bumps of the e2e suite.
- CI: GitHub Actions and pre-commit hook bumps.
To everyone who reported, reproduced, reviewed, tested and fixed - in particular @ruispereira, @tae898, @manus-pi, @ofir-blaus, @blaus23, @mvanhorn, @willemijn-zeno, @shulei5831sl, @YaeSakuraQ, @Das-Rabindra, @HarinathMaddela, @saeedtabrizi, @zpaulo80, @zek0, @YGY-001, @ruslan-butyk-fntext, @rlaveycal, @preshewar, @philiptran-tech