ArcadeDB Version
26.4.2 (build ef892b4/1776994711893/main)
Environment
- 3-node HA cluster (
arcadedb.ha.enabled: true, quorum: majority)
- Server mode: test
Expected behavior
INSERT INTO Type CONTENT {"key_0": "v", "key_1": "v", ... "key_49": "v"} should succeed
regardless of how many new (previously unseen) property keys are in the record.
Actual behavior
When a single INSERT introduces more than ~5 new property keys that don't yet exist in the
schema dictionary, the transaction fails with:
Error on transaction commit: Error on updating dictionary for key 'key_N'
com.arcadedb.exception.SchemaException
Steps to reproduce
The script below creates a fresh database, tests INSERT with increasing numbers of new keys,
and reports pass/fail. No external dependencies beyond Python 3 standard library.
Save as dict_bug_repro.py and run:
python dict_bug_repro.py --url http://<arcade-host>:2480/api/v1 --user root --password <password>
#!/usr/bin/env python3
"""
ArcadeDB HA Dictionary Bug Reproducer
Tests INSERT with increasing numbers of new property keys per transaction.
On HA clusters, fails at ~10 new keys per transaction.
"""
import argparse
import json
import time
import urllib.request
import urllib.error
import base64
import sys
DB_NAME = "dict_bug_repro"
def parse_args():
p = argparse.ArgumentParser(description="ArcadeDB HA dictionary bug reproducer")
p.add_argument("--url", required=True, help="ArcadeDB HTTP endpoint (e.g. http://host:2480/api/v1)")
p.add_argument("--user", required=True)
p.add_argument("--password", required=True)
return p.parse_args()
class ArcadeClient:
def __init__(self, url, user, password):
self.url = url.rstrip("/")
creds = base64.b64encode(f"{user}:{password}".encode()).decode()
self.headers = {"Content-Type": "application/json", "Authorization": f"Basic {creds}"}
def server_command(self, cmd):
data = json.dumps({"command": cmd}).encode()
req = urllib.request.Request(f"{self.url}/server", data=data, headers=self.headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=30) as r:
return r.status, r.read().decode()
except urllib.error.HTTPError as e:
return e.code, e.read().decode()
def sql(self, db, command):
data = json.dumps({"language": "sql", "command": command}).encode()
req = urllib.request.Request(f"{self.url}/command/{db}", data=data, headers=self.headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=30) as r:
return r.status, r.read().decode()
except urllib.error.HTTPError as e:
return e.code, e.read().decode()
def server_info(self):
req = urllib.request.Request(f"{self.url}/server", headers=self.headers)
with urllib.request.urlopen(req, timeout=10) as r:
return json.loads(r.read().decode())
def main():
args = parse_args()
client = ArcadeClient(args.url, args.user, args.password)
# Server info
print("Server Info")
print("-" * 40)
try:
info = client.server_info()
settings = {s["key"]: s["value"] for s in info.get("settings", [])}
version = info.get("version", "?")
ha = settings.get("arcadedb.ha.enabled", "?")
quorum = settings.get("arcadedb.ha.quorum", "?")
print(f" Version: {version}")
print(f" HA enabled: {ha}")
print(f" HA quorum: {quorum}")
except Exception as e:
print(f" Failed: {e}")
sys.exit(1)
# Setup
client.server_command(f"drop database {DB_NAME}")
status, _ = client.server_command(f"create database {DB_NAME}")
if status != 200:
print(f"Failed to create database")
sys.exit(1)
print(f"\nDatabase '{DB_NAME}' created\n")
results = []
# Test 1: INSERT with N new keys
print("Test 1: INSERT with N new property keys (one transaction)")
print("-" * 55)
for n in [1, 2, 3, 5, 8, 10, 15, 20, 50]:
client.sql(DB_NAME, "DROP TYPE T IF EXISTS UNSAFE")
client.sql(DB_NAME, "CREATE VERTEX TYPE T IF NOT EXISTS")
record = {f"key_{i}": f"value_{i}" for i in range(n)}
status, body = client.sql(DB_NAME, f"INSERT INTO T CONTENT {json.dumps(record)}")
passed = status == 200
print(f" {n:3d} new keys: {'PASS' if passed else 'FAIL'}")
if not passed:
detail = json.loads(body).get("detail", "") if body.startswith("{") else body
print(f" Error: {str(detail)[:120]}")
results.append((f"INSERT {n} new keys", passed))
client.sql(DB_NAME, "DROP TYPE T IF EXISTS UNSAFE")
# Test 2: 50 keys one at a time (1 key per transaction)
print(f"\nTest 2: 50 sequential INSERTs (1 new key each)")
print("-" * 55)
client.sql(DB_NAME, "CREATE VERTEX TYPE T IF NOT EXISTS")
ok = 0
for i in range(50):
status, _ = client.sql(DB_NAME, f"INSERT INTO T CONTENT {json.dumps({f'prop_{i}': f'v_{i}'})}")
if status == 200:
ok += 1
print(f" {ok}/50 passed")
results.append(("50 sequential 1-key INSERTs", ok == 50))
client.sql(DB_NAME, "DROP TYPE T IF EXISTS UNSAFE")
# Summary
print(f"\nSummary")
print("=" * 55)
for name, passed in results:
print(f" {'PASS' if passed else 'FAIL'} {name}")
# Cleanup
client.server_command(f"drop database {DB_NAME}")
print(f"\nDatabase '{DB_NAME}' dropped. Done.")
if __name__ == "__main__":
main()
Sample output (bug confirmed)
Server Info
----------------------------------------
Version: 26.4.2
HA enabled: True
HA quorum: majority
Database 'dict_bug_repro' created
Test 1: INSERT with N new property keys (one transaction)
-------------------------------------------------------
1 new keys: PASS
2 new keys: PASS
3 new keys: PASS
5 new keys: PASS
8 new keys: PASS
10 new keys: FAIL
Error: Error on updating dictionary for key 'key_9'
15 new keys: FAIL
20 new keys: FAIL
50 new keys: FAIL
Test 2: 50 sequential INSERTs (1 new key each)
-------------------------------------------------------
50/50 passed
Summary
=======================================================
PASS INSERT 1 new keys
PASS INSERT 2 new keys
PASS INSERT 3 new keys
PASS INSERT 5 new keys
PASS INSERT 8 new keys
FAIL INSERT 10 new keys
FAIL INSERT 15 new keys
FAIL INSERT 20 new keys
FAIL INSERT 50 new keys
PASS 50 sequential 1-key INSERTs
ArcadeDB Version
26.4.2 (build ef892b4/1776994711893/main)
Environment
arcadedb.ha.enabled: true, quorum: majority)Expected behavior
INSERT INTO Type CONTENT {"key_0": "v", "key_1": "v", ... "key_49": "v"}should succeedregardless of how many new (previously unseen) property keys are in the record.
Actual behavior
When a single INSERT introduces more than ~5 new property keys that don't yet exist in the
schema dictionary, the transaction fails with:
Steps to reproduce
The script below creates a fresh database, tests INSERT with increasing numbers of new keys,
and reports pass/fail. No external dependencies beyond Python 3 standard library.
Save as
dict_bug_repro.pyand run:Sample output (bug confirmed)