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_name()}")
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
- Buffers flushed to disk
- 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
- Configuration: Set options before creating/opening
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
└── .lock # 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")
# Or with context managers
with arcadedb.open_database("./database1") as db1, \
arcadedb.open_database("./database2") as db2:
# Use both databases
pass
Database Locking¶
Lock File:
- Created when database opens:
.lock - Prevents concurrent access from same/different processes
- Automatically removed on clean close
- Manual removal only if process crashed
# If database locked by another process
try:
with arcadedb.open_database("./mydb"):
pass
except Exception as e:
print(f"Database locked: {e}")
# Check if process is still running
# If not, remove lock file
import os
lock_file = "./mydb/.lock"
if os.path.exists(lock_file):
os.remove(lock_file)
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:
db.command("sql", "CREATE VERTEX TYPE User")
db.command("sql", "CREATE PROPERTY User.email STRING")
db.command("sql", "CREATE INDEX ON User (email) UNIQUE")
db.command("sql", "CREATE VERTEX TYPE Post")
db.command("sql", "CREATE PROPERTY Post.title STRING")
db.command("sql", "CREATE EDGE TYPE Authored UNIDIRECTIONAL")
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 UNIDIRECTIONAL")
# 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/importer creation
import arcadedb_embedded as arcadedb
# Optional: configure JVM before first database/importer
arcadedb.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
- Quick Start - Getting started guide
- Server Mode - Multi-process database access