Skip to content

Testing Best Practices

Summary of best practices learned from the ArcadeDB Python test suite.

Database Lifecycle

✅ Use Context Managers

# Good: Automatic cleanup
with arcadedb.create_database("./mydb") as db:
    db.query("sql", "SELECT ...")
# Database automatically closed
# Also good for servers
with arcadedb.create_server("./databases") as server:
    server.start()
    # "mydb" will be created at ./databases/databases/mydb
    db = server.create_database("mydb")
    # ... work ...
# Server automatically stopped

✅ Close When Done

# If not using context manager, explicit close
db = arcadedb.create_database("./mydb")
try:
    # ... work ...
finally:
    db.close()  # Always close to release lock

Transactions

✅ Always Use Transactions for Writes

# Good: Wrapped in transaction
with db.transaction():
    person = db.new_document("Person")
    person.set("name", "Alice").save()
    db.command("sql", "UPDATE Person SET age = 30 WHERE name = 'Alice'")
# Auto-commit on success, auto-rollback on exception

❌ Don't Write Without Transactions

# Bad: No transaction
db.command("sql", "INSERT INTO Person SET name = 'Alice'")  # May fail

Concurrency

✅ Use Threads for Parallelism

# Good: Share database instance across threads
db = arcadedb.create_database("./mydb")

def worker():
    result = db.query("sql", "SELECT FROM Data")
    # Process...

threads = [Thread(target=worker) for _ in range(10)]

✅ Use Server Mode for Multi-Process

# Good: Server mode for multiple processes
server = arcadedb.create_server(root_path="./databases")
server.start()

# Python process: embedded access
db = server.get_database("mydb")

# Other processes: HTTP API
# http://localhost:2480/api/v1/query/mydb

❌ Don't Try Concurrent Process Access

# Bad: Two processes, same database
# process1.py
db1 = arcadedb.create_database("./mydb")  # Locks

# process2.py (simultaneously)
db2 = arcadedb.open_database("./mydb")    # ❌ LockException!

Server Patterns

✅ Prefer Pattern 2 (Server First)

# Recommended: Start server first
server = arcadedb.create_server("./databases")
server.start()
# "mydb" will be created at ./databases/databases/mydb
db = server.create_database("mydb")

# Use embedded access (fast!)
# HTTP also available for other processes

⚠️ Pattern 1 Requires close()

# If using Pattern 1, MUST close
db = arcadedb.create_database("./mydb")
# ... populate ...
db.close()  # ⚠️ Critical!

# Then start server
server = arcadedb.create_server(...)

Data Import

✅ Adjust Batch Size for Performance

# Large files: bigger batches
arcadedb.import_csv(
    db,
    "huge.csv",
    type_name="Data",
    commit_every=10000  # Larger batches
)

✅ Define Schema Before Import

# Good: Schema first for better performance (Schema API preferred for embedded)
db.schema.create_document_type("Person")
db.schema.create_property("Person", "age", "INTEGER")
db.schema.create_property("Person", "name", "STRING")
db.schema.create_index("Person", ["name"])

# Then import
arcadedb.import_csv(db, "people.csv", type_name="Person")

Query Handling

✅ Iterate Results Efficiently

# Good: Iterate directly
result = db.query("sql", "SELECT FROM Person")
for person in result:
    process(person.get("name"))

✅ Convert to List When Needed

# Good when you need all results
result = db.query("sql", "SELECT FROM Person")
people = list(result)
print(f"Found {len(people)} people")

Error Handling

✅ Catch ArcadeDBError

from arcadedb_embedded.exceptions import ArcadeDBError

try:
    with db.transaction():
        person = db.new_document("Person")
        person.set("name", "Alice").save()
except ArcadeDBError as e:
    print(f"Database error: {e}")
    # Handle error

✅ Transactions Auto-Rollback

# Good: Exception triggers rollback
try:
    with db.transaction():
        person = db.new_document("Person")
        person.set("name", "Alice").save()
        raise Exception("Something went wrong")
except Exception:
    pass

# Transaction was automatically rolled back

Testing

✅ Clean Up Test Databases

import tempfile
import shutil

# Good: Use temp directory
temp_dir = tempfile.mkdtemp()
try:
    db = arcadedb.create_database(f"{temp_dir}/test_db")
    # ... tests ...
    db.close()
finally:
    shutil.rmtree(temp_dir)

✅ Use Fixtures for Setup/Teardown

import pytest

@pytest.fixture
def db():
    temp_dir = tempfile.mkdtemp()
    database = arcadedb.create_database(f"{temp_dir}/test_db")
    yield database
    database.close()
    shutil.rmtree(temp_dir)

def test_something(db):
    # db is ready to use
    db.schema.create_document_type("Test")

Performance

✅ Batch Operations in Transactions

# Good: One transaction for many operations
with db.transaction():
    for i in range(1000):
        rec = db.new_document("Data")
        rec.set("value", i).save()

❌ Don't Use Transaction Per Operation

# Bad: 1000 separate transactions
for i in range(1000):
    with db.transaction():
        rec = db.new_document("Data")
        rec.set("value", i).save()

✅ Server-Managed Embedded = Fast

# Fast: No HTTP overhead, direct JVM call
server = arcadedb.create_server("./databases")
server.start()
# "mydb" will be created at ./databases/databases/mydb
db = server.create_database("mydb")

# This is as fast as standalone embedded!
result = db.query("sql", "SELECT FROM Data")

Summary Checklist

  • Use context managers for automatic cleanup
  • Wrap writes in transactions
  • Use threads (not processes) for parallelism
  • Use server mode for multi-process access
  • Prefer Pattern 2 (server first) for new projects
  • Pre-create schema for better import performance
  • Batch operations in transactions
  • Clean up test databases
  • Catch ArcadeDBError exceptions
  • Close databases to release locks