Database Management¶
Comprehensive guide to database lifecycle, configuration, and resource management in ArcadeDB Python bindings.
Overview¶
ArcadeDB databases are embedded, file-based databases stored on your local filesystem. The database lifecycle includes creation, opening, operations, and cleanup.
Database Modes:
- Embedded: Database runs in your Python process
- In-Process: Direct Java API access via JPype
- File-Based: Data persisted to disk
Quick Start¶
Create New Database¶
import arcadedb_embedded as arcadedb
# Simple creation
with arcadedb.create_database("./mydb") as db:
# Use the database
db.command("sql", "CREATE VERTEX TYPE User")
with db.transaction():
db.command("sql", "INSERT INTO User SET name = ?", "Alice")
Open Existing Database¶
# Open existing database
with arcadedb.open_database("./mydb") as db:
# Query data
result = db.query("sql", "SELECT FROM User")
for user in result:
print(user.get("name"))
Check if Database Exists¶
if arcadedb.database_exists("./mydb"):
with arcadedb.open_database("./mydb") as db:
pass
else:
with arcadedb.create_database("./mydb") as db:
pass
Database Lifecycle¶
1. Creation¶
import arcadedb_embedded as arcadedb
# Create new database
with arcadedb.create_database("./mydb") as db:
print(f"Database created at: {db.get_database_path()}")
What happens during creation:
- Directory
./mydbcreated on filesystem - Schema files initialized
- System files created (dictionary, configuration)
- Database opened in READ_WRITE mode
- Ready for operations
2. Opening¶
# Open existing database
with arcadedb.open_database("./mydb") as db:
print(f"Database opened: {db.get_name()}")
3. Using¶
with arcadedb.open_database("./mydb") as db:
# Create schema
db.command("sql", "CREATE VERTEX TYPE Person")
db.command("sql", "CREATE PROPERTY Person.name STRING")
# Insert data
with db.transaction():
db.command("sql", "INSERT INTO Person SET name = ?", "Bob")
# Query data
result = db.query("sql", "SELECT FROM Person")
for person in result:
print(person.get("name"))
4. Closing¶
# Explicit close
db.close()
# Or use context manager (recommended)
with arcadedb.open_database("./mydb") as db:
# Use database
pass
# Automatically closed
What happens during close:
- Active transactions rolled back
- Pages written since open are flushed (a database only read since open closes without an fsync per file, from 26.10.1)
- Files closed
- Resources released
- Database removed from active instances
5. Dropping¶
with arcadedb.open_database("./mydb") as db:
# Drop database (deletes all files)
db.drop()
# Database and all files are permanently deleted
⚠️ Warning: drop() is irreversible and deletes all data!
DatabaseFactory Class¶
For advanced use cases, use DatabaseFactory directly:
import arcadedb_embedded as arcadedb
# Create factory
factory = arcadedb.DatabaseFactory("./mydb")
# Open or create via factory
if factory.exists():
print("Database exists")
with factory.open() as db:
with db.transaction():
# Operations
pass
else:
print("Creating new database")
with factory.create() as db:
with db.transaction():
# Operations
pass
Factory Pattern Benefits¶
- Explicit control: Clear separation of creation/opening logic
- Reusability: One factory for multiple operations
Context Managers¶
Database Context Manager¶
# Automatic resource cleanup
with arcadedb.open_database("./mydb") as db:
# Use database
result = db.query("sql", "SELECT FROM User")
for row in result:
print(row.get("name"))
# Database automatically closed on exit
Benefits:
- ✅ Automatic close on normal exit
- ✅ Automatic close on exception
- ✅ Guaranteed resource cleanup
- ✅ Pythonic style
Nested Context Managers¶
# Open database and use transaction
with arcadedb.open_database("./mydb") as db:
with db.transaction():
db.command("sql", "INSERT INTO User SET name = ?", "Charlie")
# Transaction committed, database closed
Configuration¶
Database Directory Structure¶
./mydb/
├── configuration.json # Database configuration
├── schema.json # Schema definition
├── schema.prev.json # Schema backup
├── dictionary.*.dict # String dictionary
├── statistics.json # Database statistics
├── User_0.*.bucket # User type data files
├── HasFriend_0.*.bucket # Edge type data files
└── database.lck # Lock file (when open)
Database Location¶
import os
# Relative path
with arcadedb.create_database("./mydb"):
pass
# Absolute path
with arcadedb.create_database("/var/data/mydb"):
pass
Resource Management¶
Proper Cleanup¶
# ✓ Recommended: Context manager
with arcadedb.open_database("./mydb") as db:
# Use database
pass
# ✓ Acceptable: Explicit close
db = arcadedb.open_database("./mydb")
try:
# Use database
pass
finally:
db.close()
# ✗ Bad: No cleanup
db = arcadedb.open_database("./mydb")
# Operations...
# Database never closed!
Multiple Databases¶
with arcadedb.open_database("./database1") as db1, \
arcadedb.open_database("./database2") as db2:
# Use both databases
result1 = db1.query("sql", "SELECT FROM User")
result2 = db2.query("sql", "SELECT FROM Product")
Database Locking¶
Lock File:
- Created when database opens:
database.lck, with an OS file lock held on it - Prevents concurrent access from same/different processes
- Automatically removed on clean close
- After a crash the OS releases the lock and the file stays behind. A leftover file blocks nothing: the next open sees it and replays the WAL to recover. Do not delete it by hand: deleting it skips that recovery.
# If database locked by another process
try:
with arcadedb.open_database("./mydb"):
pass
except Exception as e:
print(f"Database locked: {e}")
# Another running process holds the lock: close that process.
# Do not delete ./mydb/database.lck (see above).
Common Patterns¶
Database Initialization¶
def init_database(path: str):
"""Initialize database with schema."""
# Create if doesn't exist
if not arcadedb.database_exists(path):
with arcadedb.create_database(path) as db:
# Several schema statements: one transaction writes the schema once
with db.transaction():
db.command("sql", "CREATE VERTEX TYPE User")
db.command("sql", "CREATE PROPERTY User.email STRING")
db.command("sql", "CREATE INDEX ON User (email) UNIQUE_HASH")
db.command("sql", "CREATE VERTEX TYPE Post")
db.command("sql", "CREATE PROPERTY Post.title STRING")
db.command("sql", "CREATE EDGE TYPE Authored")
print(f"Database initialized at {path}")
return arcadedb.open_database(path)
# Usage
with init_database("./myapp") as db:
pass
Database Reset¶
def reset_database(path: str):
"""Drop and recreate database."""
if arcadedb.database_exists(path):
with arcadedb.open_database(path) as db:
db.drop()
print(f"Database dropped: {path}")
with arcadedb.create_database(path) as db:
print(f"Database created: {path}")
return arcadedb.open_database(path)
# Usage
with reset_database("./mydb") as db:
pass
Database Backup Pattern¶
import shutil
import datetime
def backup_database(db_path: str, backup_dir: str):
"""Backup database files."""
# Close database first
if arcadedb.database_exists(db_path):
with arcadedb.open_database(db_path):
pass # Ensure clean state
# Create backup with timestamp
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{backup_dir}/backup_{timestamp}"
# Copy database directory
shutil.copytree(db_path, backup_path)
print(f"Backup created: {backup_path}")
return backup_path
# Usage
backup_path = backup_database("./mydb", "./backups")
Database Migration¶
def migrate_database(old_path: str, new_path: str):
"""Migrate data from old to new database."""
with arcadedb.open_database(old_path) as old_db, \
arcadedb.create_database(new_path) as new_db:
# Copy schema
new_db.command("sql", "CREATE VERTEX TYPE User")
new_db.command("sql", "CREATE VERTEX TYPE Post")
new_db.command("sql", "CREATE EDGE TYPE Authored")
# Copy data
batch_size = 1000
# Migrate users
result = old_db.query("sql", "SELECT FROM User")
users_batch = []
for user in result:
users_batch.append(user)
if len(users_batch) >= batch_size:
with new_db.transaction():
for u in users_batch:
new_db.command(
"sql",
"INSERT INTO User SET name = ?, email = ?",
u.get("name"),
u.get("email"),
)
users_batch = []
# Flush remaining
if users_batch:
with new_db.transaction():
for u in users_batch:
new_db.command(
"sql",
"INSERT INTO User SET name = ?, email = ?",
u.get("name"),
u.get("email"),
)
print("Migration complete")
# Usage
migrate_database("./old_db", "./new_db")
Database Information¶
Check Transaction Status¶
with arcadedb.open_database("./mydb") as db:
print(db.is_transaction_active()) # False
with db.transaction():
print(db.is_transaction_active()) # True
print(db.is_transaction_active()) # False
Error Handling¶
Database Already Exists¶
try:
with arcadedb.create_database("./mydb") as db:
pass
except Exception as e:
print(f"Error: {e}")
# Database already exists
with arcadedb.open_database("./mydb") as db:
pass
Database Not Found¶
try:
with arcadedb.open_database("./nonexistent") as db:
pass
except Exception as e:
print(f"Database not found: {e}")
# Create it
with arcadedb.create_database("./nonexistent") as db:
pass
Database Locked¶
import time
def open_with_retry(path, max_retries=3):
"""Open database with retry logic."""
for attempt in range(max_retries):
try:
return arcadedb.open_database(path)
except Exception as e:
if "locked" in str(e).lower():
if attempt < max_retries - 1:
print(f"Database locked, retry {attempt + 1}/{max_retries}")
time.sleep(1)
continue
raise
raise Exception("Failed to open database after retries")
# Usage
with open_with_retry("./mydb") as db:
pass
Advanced Topics¶
JVM Lifecycle and Databases¶
# JVM starts on first database creation
import arcadedb_embedded as arcadedb
from arcadedb_embedded.jvm import start_jvm
# Optional: configure JVM before first database or server
start_jvm(heap_size="8g")
# JVM runs for entire Python process
with arcadedb.create_database("./db1") as db1:
pass
with arcadedb.open_database("./db1") as db2: # Same JVM
pass
# JVM shuts down when Python exits
Multiple Databases, One JVM¶
# All databases share the same JVM
with arcadedb.open_database("./database1") as db1, \
arcadedb.open_database("./database2") as db2, \
arcadedb.open_database("./database3") as db3:
# Efficient: shared JVM resources
pass
See Also¶
- Database API Reference - Complete API documentation
- Transactions - Transaction management
- Server Mode - Multi-process database access
- Quick Start - Getting started guide