Appendix III: Error Codes and Troubleshooting
Overview
This chapter provides comprehensive documentation for all error codes, exception types, and troubleshooting procedures in Vektagraf. Understanding error patterns and resolution strategies is essential for building robust applications and maintaining system reliability.
Learning Objectives
- Understand all error codes and their meanings
- Master systematic troubleshooting procedures
- Learn error prevention strategies and best practices
- Explore diagnostic tools and monitoring techniques
- Understand performance troubleshooting and optimization
Prerequisites
- Basic understanding of Vektagraf architecture
- Familiarity with exception handling in Dart
- Knowledge of database operations and transactions
Error Classification System
Vektagraf uses a hierarchical error classification system with specific error codes and categories:
Error Code Format
VKG-[CATEGORY]-[SUBCATEGORY]-[NUMBER]
Examples:
VKG-DB-001 - Database connection error
VKG-TX-002 - Transaction conflict error
VKG-SEC-003 - Authentication failure
VKG-VEC-004 - Vector dimension mismatch
Error Categories
| Category | Code | Description |
|---|---|---|
| Database | DB | Core database operations |
| Transaction | TX | Transaction and concurrency |
| Security | SEC | Authentication and authorization |
| Vector | VEC | Vector operations and search |
| Graph | GRP | Graph operations and traversal |
| Schema | SCH | Schema parsing and validation |
| Network | NET | Network and transport layer |
| Storage | STO | Storage engine operations |
| Configuration | CFG | Configuration and setup |
| Multi-Tenant | MT | Multi-tenancy operations |
Core Database Errors
VKG-DB-001: Database Connection Failed
Description: Failed to establish connection to database
Common Causes:
- Invalid database path or URL
- Insufficient permissions
- Database file corruption
- Network connectivity issues (hosted mode)
Resolution Steps:
try {
await database.open('/path/to/database.vektagraf');
} on DatabaseException catch (e) {
if (e.message.contains('connection failed')) {
// Check file permissions
final file = File('/path/to/database.vektagraf');
if (!await file.exists()) {
print('Database file does not exist, creating new database');
// Create parent directories if needed
await file.parent.create(recursive: true);
await database.open('/path/to/database.vektagraf');
} else {
// Check permissions
try {
await file.readAsBytes();
print('File is readable, checking for corruption');
// Attempt database repair
await repairDatabase('/path/to/database.vektagraf');
} catch (permissionError) {
print('Permission denied: ${permissionError}');
// Fix permissions or use alternative path
}
}
}
}
Prevention:
- Validate database paths before opening
- Ensure proper file permissions
- Use connection pooling for hosted mode
- Implement health checks
VKG-DB-002: Database Already Open
Description: Attempt to open database that is already open
Resolution:
if (!database.isOpen) {
await database.open(databasePath);
} else {
print('Database is already open');
}
VKG-DB-003: Database Not Open
Description: Operation attempted on closed database
Resolution:
Future<void> ensureDatabaseOpen() async {
if (!database.isOpen) {
await database.open(databasePath);
}
}
// Use before operations
await ensureDatabaseOpen();
final users = await database.objects<User>();
VKG-DB-004: Invalid Object Type
Description: Object type not defined in schema or invalid
Resolution:
try {
final users = await database.objects<User>();
} on SchemaException catch (e) {
print('Schema error: ${e.message}');
// Check if User is defined in schema
final schema = await loadSchema();
if (!schema.models.containsKey('User')) {
print('User model not found in schema');
// Add User model to schema or use correct type
}
}
Transaction Errors
VKG-TX-001: Transaction Already Active
Description: Attempt to start transaction when one is already active
Resolution:
// Check transaction state before starting
if (!transaction.isActive) {
await database.transaction((txn) async {
// Transaction operations
});
} else {
print('Transaction already active');
}
VKG-TX-002: Concurrency Conflict
Description: Optimistic locking failure due to concurrent modifications
Common Causes:
- Multiple clients modifying same object
- Stale revision numbers
- Long-running transactions
Resolution:
Future<void> handleConcurrencyConflict<T>(
VektagrafId objectId,
T updatedObject,
int maxRetries = 3,
) async {
int attempts = 0;
while (attempts < maxRetries) {
try {
await database.transaction((txn) async {
final current = await txn.getById<T>(objectId);
if (current != null) {
// Get current revision
final currentRevision = getCurrentRevision(current);
await txn.update(objectId, updatedObject, currentRevision);
}
});
return; // Success
} on ConcurrencyConflictException catch (e) {
attempts++;
if (attempts >= maxRetries) {
print('Max retries exceeded: ${e.message}');
rethrow;
}
// Exponential backoff
await Future.delayed(Duration(milliseconds: 100 * (1 << attempts)));
// Refresh object state
updatedObject = await refreshObjectState(objectId, updatedObject);
}
}
}
VKG-TX-003: Transaction Timeout
Description: Transaction exceeded maximum allowed duration
Resolution:
// Use shorter transactions
await database.transaction((txn) async {
// Break large operations into smaller chunks
final batchSize = 100;
for (int i = 0; i < objects.length; i += batchSize) {
final batch = objects.skip(i).take(batchSize);
await txn.saveAll(batch);
// Commit periodically for long operations
if (i > 0 && i % 1000 == 0) {
await txn.commit();
// Start new transaction for next batch
}
}
});
VKG-TX-004: Transaction Rollback Failed
Description: Failed to rollback transaction properly
Resolution:
try {
await database.transaction((txn) async {
// Operations that might fail
throw Exception('Simulated failure');
});
} catch (e) {
print('Transaction failed and was rolled back: $e');
// Check database consistency
await validateDatabaseConsistency();
}
Security Errors
VKG-SEC-001: Authentication Failed
Description: Invalid credentials or authentication failure
Resolution:
try {
final client = VektagrafClient(VektagrafClientConfig(
username: 'user',
password: 'password',
));
await client.open('https://api.example.com');
} on VektagrafAuthenticationException catch (e) {
print('Authentication failed: ${e.message}');
// Check credentials
if (e.message.contains('invalid credentials')) {
// Prompt for new credentials
final newCredentials = await promptForCredentials();
// Retry with new credentials
} else if (e.message.contains('account locked')) {
// Handle account lockout
await handleAccountLockout();
}
}
VKG-SEC-002: Authorization Failed
Description: Insufficient permissions for requested operation
Resolution:
try {
await database.objects<User>().save(newUser);
} on VektagrafAuthorizationException catch (e) {
print('Authorization failed: ${e.message}');
// Check user permissions
final permissions = await getUserPermissions();
if (!permissions.contains('user:create')) {
print('User lacks create permission');
// Request permission elevation or use different approach
}
}
VKG-SEC-003: Rate Limit Exceeded
Description: Too many requests within time window
Resolution:
Future<T> executeWithRateLimit<T>(
Future<T> Function() operation,
{int maxRetries = 3}
) async {
int attempts = 0;
while (attempts < maxRetries) {
try {
return await operation();
} on VektagrafRateLimitException catch (e) {
attempts++;
if (attempts >= maxRetries) rethrow;
// Extract retry-after header if available
final retryAfter = extractRetryAfter(e.message) ??
Duration(seconds: 1 << attempts);
print('Rate limited, waiting ${retryAfter.inSeconds}s');
await Future.delayed(retryAfter);
}
}
throw StateError('Max retries exceeded');
}
VKG-SEC-004: Tenant Limit Exceeded
Description: Tenant has exceeded configured limits
Resolution:
try {
await tenantMiddleware.executeWithLimits(
tenantId,
userId,
'create',
() => database.objects<Document>().save(document),
);
} on TenantLimitException catch (e) {
print('Tenant limit exceeded: ${e.message}');
switch (e.limitType) {
case 'storage':
await handleStorageLimit(e.tenantId);
break;
case 'rate_limit':
await handleRateLimit(e.tenantId);
break;
case 'request_quota':
await handleRequestQuota(e.tenantId);
break;
}
}
Future<void> handleStorageLimit(String tenantId) async {
// Notify tenant of storage limit
await notifyTenantOfLimit(tenantId, 'storage');
// Suggest cleanup or upgrade
final usage = await getTenantStorageUsage(tenantId);
print('Current usage: ${usage['currentBytes']} / ${usage['limitBytes']}');
// Offer cleanup suggestions
final suggestions = await getCleanupSuggestions(tenantId);
for (final suggestion in suggestions) {
print('Suggestion: ${suggestion}');
}
}
Vector Search Errors
VKG-VEC-001: Dimension Mismatch
Description: Vector dimensions don't match vector space configuration
Resolution:
try {
final vectorSpace = database.vectorSpace('embeddings', 512);
await vectorSpace.addVector(vector); // Wrong dimensions
} on ArgumentError catch (e) {
if (e.message.contains('dimension')) {
print('Vector dimension mismatch: ${e.message}');
// Check vector dimensions
print('Vector dimensions: ${vector.length}');
print('Expected dimensions: 512');
// Resize vector if possible
if (vector.length > 512) {
vector = vector.take(512).toList(); // Truncate
} else if (vector.length < 512) {
vector = [...vector, ...List.filled(512 - vector.length, 0.0)]; // Pad
}
// Retry with corrected vector
await vectorSpace.addVector(vector);
}
}
VKG-VEC-002: Invalid Vector Data
Description: Vector contains invalid values (NaN, infinity)
Resolution:
List<double> sanitizeVector(List<double> vector) {
return vector.map((value) {
if (value.isNaN) return 0.0;
if (value.isInfinite) return value.isNegative ? -1.0 : 1.0;
return value;
}).toList();
}
// Use before adding vectors
final sanitizedVector = sanitizeVector(originalVector);
await vectorSpace.addVector(sanitizedVector);
VKG-VEC-003: Vector Space Not Found
Description: Attempting to access non-existent vector space
Resolution:
VektagrafVectorSpace getOrCreateVectorSpace(String name, int dimensions) {
try {
return database.vectorSpace(name, dimensions);
} catch (e) {
print('Vector space $name not found, creating...');
// Vector spaces are created automatically when accessed
return database.vectorSpace(name, dimensions);
}
}
Graph Operation Errors
VKG-GRP-001: Node Not Found
Description: Graph node does not exist
Resolution:
try {
final node = await database.graph.getNode(nodeId);
if (node == null) {
print('Node $nodeId not found');
// Create node if needed
final newNodeId = await database.graph.addNode({
'type': 'User',
'name': 'Default User',
});
return newNodeId;
}
} on VektagrafNotFoundException catch (e) {
print('Node not found: ${e.message}');
// Handle missing node
}
VKG-GRP-002: Circular Reference
Description: Graph operation would create circular reference
Resolution:
Future<bool> wouldCreateCycle(VektagrafId fromId, VektagrafId toId) async {
// Check if adding edge from->to would create cycle
final path = await database.graph.shortestPath(toId, fromId);
return path != null; // If path exists, adding edge would create cycle
}
// Use before adding edges
if (!await wouldCreateCycle(fromId, toId)) {
await database.graph.addEdge(fromId, toId);
} else {
print('Cannot add edge: would create circular reference');
}
Schema Errors
VKG-SCH-001: Schema Parse Error
Description: Invalid JSON schema format
Resolution:
try {
final parser = VektagrafSchemaParser();
final schema = parser.parseFromString(schemaJson);
} on SchemaParseException catch (e) {
print('Schema parse error: ${e.message}');
if (e.field != null) print('Field: ${e.field}');
if (e.model != null) print('Model: ${e.model}');
// Validate JSON first
try {
jsonDecode(schemaJson);
} catch (jsonError) {
print('Invalid JSON: $jsonError');
// Fix JSON syntax errors
}
// Check schema structure
final json = jsonDecode(schemaJson) as Map<String, dynamic>;
if (!json.containsKey('models')) {
print('Schema missing required "models" section');
}
}
VKG-SCH-002: Model Not Found
Description: Referenced model does not exist in schema
Resolution:
void validateModelReferences(VektagrafSchema schema) {
for (final model in schema.models.values) {
for (final field in model.relationshipFields) {
final targetModel = field.relationshipConfig!.targetModel;
if (!schema.models.containsKey(targetModel)) {
print('Model ${model.name} references unknown model: $targetModel');
// Add missing model or fix reference
}
}
}
}
Network and Transport Errors
VKG-NET-001: Connection Timeout
Description: Network operation timed out
Resolution:
final client = VektagrafClient(VektagrafClientConfig(
timeout: Duration(seconds: 60), // Increase timeout
username: 'user',
password: 'password',
));
// Implement retry with exponential backoff
Future<T> withRetry<T>(Future<T> Function() operation) async {
int attempts = 0;
const maxAttempts = 3;
while (attempts < maxAttempts) {
try {
return await operation();
} on VektagrafTimeoutException catch (e) {
attempts++;
if (attempts >= maxAttempts) rethrow;
final delay = Duration(seconds: 1 << attempts);
print('Timeout, retrying in ${delay.inSeconds}s...');
await Future.delayed(delay);
}
}
throw StateError('Max attempts exceeded');
}
VKG-NET-002: Service Unavailable
Description: Server is temporarily unavailable
Resolution:
try {
await client.open('https://api.example.com');
} on VektagrafServiceUnavailableException catch (e) {
print('Service unavailable: ${e.message}');
// Check server status
final status = await checkServerStatus();
if (status.isMaintenanceMode) {
print('Server in maintenance mode until ${status.maintenanceEnd}');
// Wait or use fallback
} else {
// Try alternative endpoints
for (final endpoint in alternativeEndpoints) {
try {
await client.open(endpoint);
break;
} catch (e) {
print('Alternative endpoint failed: $endpoint');
}
}
}
}
Performance Troubleshooting
Slow Query Performance
Symptoms:
- Queries taking longer than expected
- High CPU usage during queries
- Memory usage spikes
Diagnostic Steps:
// Enable query profiling
final config = VektagrafConfig(
enableQueryProfiling: true,
profilingThreshold: Duration(milliseconds: 100),
);
// Monitor query performance
class QueryProfiler {
final Map<String, List<Duration>> _queryTimes = {};
Future<T> profileQuery<T>(
String queryName,
Future<T> Function() query,
) async {
final stopwatch = Stopwatch()..start();
try {
final result = await query();
return result;
} finally {
stopwatch.stop();
_recordQueryTime(queryName, stopwatch.elapsed);
}
}
void _recordQueryTime(String queryName, Duration duration) {
_queryTimes.putIfAbsent(queryName, () => []).add(duration);
// Log slow queries
if (duration > Duration(milliseconds: 100)) {
print('Slow query detected: $queryName took ${duration.inMilliseconds}ms');
}
}
Map<String, QueryStats> getQueryStats() {
return _queryTimes.map((name, times) {
final avgTime = times.fold<int>(0, (sum, time) => sum + time.inMilliseconds) / times.length;
final maxTime = times.fold<Duration>(Duration.zero, (max, time) => time > max ? time : max);
return MapEntry(name, QueryStats(
queryName: name,
executionCount: times.length,
averageTime: Duration(milliseconds: avgTime.round()),
maxTime: maxTime,
));
});
}
}
class QueryStats {
final String queryName;
final int executionCount;
final Duration averageTime;
final Duration maxTime;
QueryStats({
required this.queryName,
required this.executionCount,
required this.averageTime,
required this.maxTime,
});
}
Optimization Strategies:
// 1. Add indexes for frequently queried fields
await database.createIndex('User', 'email'); // If supported
// 2. Use property-based queries instead of full scans
final users = await database.objects<User>()
.whereProperty('isActive', true) // Optimized
.take(100);
// Instead of:
final allUsers = await database.objects<User>();
final activeUsers = allUsers.where((u) => u.isActive).take(100);
// 3. Batch operations
final batchSize = 100;
for (int i = 0; i < users.length; i += batchSize) {
final batch = users.skip(i).take(batchSize);
await database.transaction((txn) async {
await txn.saveAll(batch);
});
}
// 4. Use pagination for large result sets
Future<List<T>> getPaginatedResults<T>(
VektagrafList<T> list,
int page,
int pageSize,
) async {
return list.skip(page * pageSize).take(pageSize).toList();
}
Memory Usage Issues
Symptoms:
- Out of memory errors
- Gradual memory increase
- Poor garbage collection performance
Diagnostic Steps:
class MemoryMonitor {
Timer? _timer;
final List<MemorySnapshot> _snapshots = [];
void startMonitoring() {
_timer = Timer.periodic(Duration(seconds: 30), (_) {
_takeSnapshot();
});
}
void stopMonitoring() {
_timer?.cancel();
}
void _takeSnapshot() {
// In a real implementation, you'd use platform-specific memory APIs
final snapshot = MemorySnapshot(
timestamp: DateTime.now(),
heapUsage: _getHeapUsage(),
objectCount: _getObjectCount(),
);
_snapshots.add(snapshot);
// Keep only last 100 snapshots
if (_snapshots.length > 100) {
_snapshots.removeAt(0);
}
// Check for memory leaks
if (_snapshots.length > 10) {
final recent = _snapshots.takeLast(10);
final trend = _calculateMemoryTrend(recent);
if (trend > 0.1) { // 10% increase trend
print('Potential memory leak detected');
_analyzeMemoryUsage();
}
}
}
double _calculateMemoryTrend(Iterable<MemorySnapshot> snapshots) {
if (snapshots.length < 2) return 0.0;
final first = snapshots.first.heapUsage;
final last = snapshots.last.heapUsage;
return (last - first) / first;
}
void _analyzeMemoryUsage() {
// Analyze memory usage patterns
print('Memory usage analysis:');
print('Current heap: ${_getHeapUsage()} bytes');
print('Object count: ${_getObjectCount()}');
// Suggest optimizations
print('Optimization suggestions:');
print('- Reduce cache size if memory constrained');
print('- Use pagination for large result sets');
print('- Clear unused object references');
}
int _getHeapUsage() {
// Platform-specific implementation needed
return 0;
}
int _getObjectCount() {
// Platform-specific implementation needed
return 0;
}
}
class MemorySnapshot {
final DateTime timestamp;
final int heapUsage;
final int objectCount;
MemorySnapshot({
required this.timestamp,
required this.heapUsage,
required this.objectCount,
});
}
Memory Optimization:
// 1. Reduce cache size for memory-constrained environments
final config = VektagrafConfig(
cacheSize: 100, // Reduce from default 1000
maxMemoryBytes: 64 * 1024 * 1024, // 64MB limit
);
// 2. Use streaming for large datasets
Stream<User> streamUsers() async* {
const batchSize = 100;
int offset = 0;
while (true) {
final batch = await database.objects<User>()
.skip(offset)
.take(batchSize);
if (batch.isEmpty) break;
for (final user in batch) {
yield user;
}
offset += batchSize;
}
}
// 3. Clear references when done
void processUsers() async {
var users = await database.objects<User>();
// Process users
for (final user in users) {
await processUser(user);
}
// Clear reference to allow garbage collection
users = null;
}
```## Syst
ematic Troubleshooting Procedures
### General Troubleshooting Workflow
```mermaid
flowchart TD
A[Issue Reported] --> B[Gather Information]
B --> C[Identify Error Category]
C --> D[Check Common Causes]
D --> E[Apply Resolution Steps]
E --> F{Issue Resolved?}
F -->|Yes| G[Document Solution]
F -->|No| H[Escalate or Deep Dive]
H --> I[Advanced Diagnostics]
I --> J[Apply Advanced Solutions]
J --> F
G --> K[Monitor for Recurrence]
Information Gathering Checklist
When troubleshooting issues, collect the following information:
Environment Information:
class DiagnosticInfo {
static Map<String, dynamic> gatherSystemInfo() {
return {
'platform': Platform.operatingSystem,
'version': Platform.operatingSystemVersion,
'dartVersion': Platform.version,
'vektagrafVersion': getVektagrafVersion(),
'timestamp': DateTime.now().toIso8601String(),
'memoryUsage': _getMemoryUsage(),
'diskSpace': _getDiskSpace(),
};
}
static Map<String, dynamic> gatherDatabaseInfo(VektagrafDatabase database) {
return {
'isOpen': database.isOpen,
'config': database.config?.toJson(),
'connectionMode': database.isHosted ? 'hosted' : 'embedded',
'activeTransactions': _getActiveTransactionCount(),
'cacheStats': _getCacheStatistics(),
};
}
static Map<String, dynamic> gatherErrorContext(Exception error) {
return {
'errorType': error.runtimeType.toString(),
'message': error.toString(),
'stackTrace': StackTrace.current.toString(),
'timestamp': DateTime.now().toIso8601String(),
};
}
}
Configuration Validation:
class ConfigurationDiagnostics {
static List<String> validateConfiguration(VektagrafConfig config) {
final issues = <String>[];
// Memory validation
if (config.maxMemoryBytes < 64 * 1024 * 1024) {
issues.add('Memory too low: ${config.maxMemoryBytes} bytes (minimum 64MB)');
}
// Cache validation
if (config.cacheSize < 100) {
issues.add('Cache size too small: ${config.cacheSize} (minimum 100)');
}
// Multi-tenancy validation
if (config.isMultiTenantEnabled) {
if (config.multiTenancy?.strictIsolation != true) {
issues.add('Multi-tenancy enabled but strict isolation disabled');
}
}
return issues;
}
static Map<String, dynamic> analyzePerformanceConfig(VektagrafConfig config) {
final analysis = <String, dynamic>{};
// Memory analysis
final memoryMB = config.maxMemoryBytes / (1024 * 1024);
analysis['memoryMB'] = memoryMB;
analysis['memoryLevel'] = memoryMB < 128 ? 'low' :
memoryMB < 512 ? 'medium' : 'high';
// Cache analysis
analysis['cacheSize'] = config.cacheSize;
analysis['cacheLevel'] = config.cacheSize < 1000 ? 'small' :
config.cacheSize < 5000 ? 'medium' : 'large';
// Durability analysis
analysis['durabilityMode'] = config.syncMode.name;
analysis['walEnabled'] = config.enableWAL;
return analysis;
}
}
Database Health Checks
class DatabaseHealthChecker {
final VektagrafDatabase database;
DatabaseHealthChecker(this.database);
Future<HealthCheckResult> performHealthCheck() async {
final results = <String, bool>{};
final issues = <String>[];
// Connection check
try {
results['connection'] = database.isOpen;
if (!database.isOpen) {
issues.add('Database connection is closed');
}
} catch (e) {
results['connection'] = false;
issues.add('Connection check failed: $e');
}
// Basic operation check
try {
await database.objects<TestObject>();
results['basicOperations'] = true;
} catch (e) {
results['basicOperations'] = false;
issues.add('Basic operations failed: $e');
}
// Transaction check
try {
await database.transaction((txn) async {
// Simple transaction test
return true;
});
results['transactions'] = true;
} catch (e) {
results['transactions'] = false;
issues.add('Transaction test failed: $e');
}
// Vector space check
try {
database.vectorSpace('health_check', 128);
results['vectorSpaces'] = true;
} catch (e) {
results['vectorSpaces'] = false;
issues.add('Vector space check failed: $e');
}
// Graph operations check
try {
database.graph;
results['graphOperations'] = true;
} catch (e) {
results['graphOperations'] = false;
issues.add('Graph operations check failed: $e');
}
final overallHealth = results.values.every((result) => result);
return HealthCheckResult(
isHealthy: overallHealth,
checks: results,
issues: issues,
timestamp: DateTime.now(),
);
}
Future<PerformanceMetrics> gatherPerformanceMetrics() async {
final stopwatch = Stopwatch();
// Measure basic operations
stopwatch.start();
await database.objects<TestObject>();
final objectsTime = stopwatch.elapsedMilliseconds;
stopwatch.reset();
await database.transaction((txn) async {
return true;
});
final transactionTime = stopwatch.elapsedMilliseconds;
stopwatch.reset();
database.vectorSpace('perf_test', 128);
final vectorSpaceTime = stopwatch.elapsedMilliseconds;
return PerformanceMetrics(
objectsOperationMs: objectsTime,
transactionMs: transactionTime,
vectorSpaceMs: vectorSpaceTime,
timestamp: DateTime.now(),
);
}
}
class HealthCheckResult {
final bool isHealthy;
final Map<String, bool> checks;
final List<String> issues;
final DateTime timestamp;
HealthCheckResult({
required this.isHealthy,
required this.checks,
required this.issues,
required this.timestamp,
});
@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('Health Check Result: ${isHealthy ? 'HEALTHY' : 'UNHEALTHY'}');
buffer.writeln('Timestamp: $timestamp');
buffer.writeln('Checks:');
for (final entry in checks.entries) {
final status = entry.value ? 'PASS' : 'FAIL';
buffer.writeln(' ${entry.key}: $status');
}
if (issues.isNotEmpty) {
buffer.writeln('Issues:');
for (final issue in issues) {
buffer.writeln(' - $issue');
}
}
return buffer.toString();
}
}
class PerformanceMetrics {
final int objectsOperationMs;
final int transactionMs;
final int vectorSpaceMs;
final DateTime timestamp;
PerformanceMetrics({
required this.objectsOperationMs,
required this.transactionMs,
required this.vectorSpaceMs,
required this.timestamp,
});
}
class TestObject {
final String id;
final String name;
TestObject({required this.id, required this.name});
}
Automated Diagnostics
class AutomatedDiagnostics {
static Future<DiagnosticReport> runFullDiagnostics(
VektagrafDatabase database,
) async {
final report = DiagnosticReport();
// System information
report.systemInfo = DiagnosticInfo.gatherSystemInfo();
report.databaseInfo = DiagnosticInfo.gatherDatabaseInfo(database);
// Configuration validation
if (database.config != null) {
report.configIssues = ConfigurationDiagnostics.validateConfiguration(database.config!);
report.performanceAnalysis = ConfigurationDiagnostics.analyzePerformanceConfig(database.config!);
}
// Health checks
final healthChecker = DatabaseHealthChecker(database);
report.healthCheck = await healthChecker.performHealthCheck();
report.performanceMetrics = await healthChecker.gatherPerformanceMetrics();
// Resource usage
report.resourceUsage = await _gatherResourceUsage();
// Generate recommendations
report.recommendations = _generateRecommendations(report);
return report;
}
static Future<Map<String, dynamic>> _gatherResourceUsage() async {
return {
'memoryUsage': _getCurrentMemoryUsage(),
'diskUsage': await _getDiskUsage(),
'cpuUsage': _getCpuUsage(),
'networkStats': await _getNetworkStats(),
};
}
static List<String> _generateRecommendations(DiagnosticReport report) {
final recommendations = <String>[];
// Performance recommendations
final perfAnalysis = report.performanceAnalysis;
if (perfAnalysis['memoryLevel'] == 'low') {
recommendations.add('Consider increasing maxMemoryBytes for better performance');
}
if (perfAnalysis['cacheLevel'] == 'small') {
recommendations.add('Consider increasing cacheSize for better read performance');
}
// Health check recommendations
if (!report.healthCheck.isHealthy) {
recommendations.add('Address health check failures before production use');
}
// Configuration recommendations
if (report.configIssues.isNotEmpty) {
recommendations.add('Fix configuration issues: ${report.configIssues.join(', ')}');
}
return recommendations;
}
static int _getCurrentMemoryUsage() => 0; // Platform-specific implementation
static Future<int> _getDiskUsage() async => 0; // Platform-specific implementation
static double _getCpuUsage() => 0.0; // Platform-specific implementation
static Future<Map<String, dynamic>> _getNetworkStats() async => {}; // Platform-specific implementation
}
class DiagnosticReport {
Map<String, dynamic> systemInfo = {};
Map<String, dynamic> databaseInfo = {};
List<String> configIssues = [];
Map<String, dynamic> performanceAnalysis = {};
late HealthCheckResult healthCheck;
late PerformanceMetrics performanceMetrics;
Map<String, dynamic> resourceUsage = {};
List<String> recommendations = [];
@override
String toString() {
final buffer = StringBuffer();
buffer.writeln('=== Vektagraf Diagnostic Report ===');
buffer.writeln('Generated: ${DateTime.now()}');
buffer.writeln();
buffer.writeln('System Information:');
systemInfo.forEach((key, value) {
buffer.writeln(' $key: $value');
});
buffer.writeln();
buffer.writeln('Database Information:');
databaseInfo.forEach((key, value) {
buffer.writeln(' $key: $value');
});
buffer.writeln();
if (configIssues.isNotEmpty) {
buffer.writeln('Configuration Issues:');
for (final issue in configIssues) {
buffer.writeln(' - $issue');
}
buffer.writeln();
}
buffer.writeln('Performance Analysis:');
performanceAnalysis.forEach((key, value) {
buffer.writeln(' $key: $value');
});
buffer.writeln();
buffer.writeln(healthCheck.toString());
buffer.writeln();
buffer.writeln('Performance Metrics:');
buffer.writeln(' Objects operation: ${performanceMetrics.objectsOperationMs}ms');
buffer.writeln(' Transaction: ${performanceMetrics.transactionMs}ms');
buffer.writeln(' Vector space: ${performanceMetrics.vectorSpaceMs}ms');
buffer.writeln();
if (recommendations.isNotEmpty) {
buffer.writeln('Recommendations:');
for (final recommendation in recommendations) {
buffer.writeln(' - $recommendation');
}
}
return buffer.toString();
}
}
Error Prevention Strategies
Input Validation
class InputValidator {
static void validateVectorDimensions(List<double> vector, int expectedDimensions) {
if (vector.length != expectedDimensions) {
throw ArgumentError(
'Vector dimension mismatch: expected $expectedDimensions, got ${vector.length}'
);
}
for (int i = 0; i < vector.length; i++) {
final value = vector[i];
if (value.isNaN) {
throw ArgumentError('Vector contains NaN at index $i');
}
if (value.isInfinite) {
throw ArgumentError('Vector contains infinite value at index $i');
}
}
}
static void validateTenantId(String tenantId) {
if (tenantId.isEmpty) {
throw ArgumentError('Tenant ID cannot be empty');
}
if (!RegExp(r'^[a-zA-Z0-9_-]+$').hasMatch(tenantId)) {
throw ArgumentError('Tenant ID contains invalid characters: $tenantId');
}
if (tenantId.length > 64) {
throw ArgumentError('Tenant ID too long: ${tenantId.length} characters (max 64)');
}
}
static void validateObjectSize(dynamic object, int maxSizeBytes) {
final serialized = jsonEncode(object);
final sizeBytes = utf8.encode(serialized).length;
if (sizeBytes > maxSizeBytes) {
throw ArgumentError(
'Object too large: $sizeBytes bytes (max $maxSizeBytes bytes)'
);
}
}
}
Connection Management
class ConnectionManager {
final VektagrafDatabase _database;
final String _connectionString;
final VektagrafConfig _config;
Timer? _healthCheckTimer;
ConnectionManager(this._database, this._connectionString, this._config);
Future<void> ensureConnection() async {
if (!_database.isOpen) {
await _reconnect();
}
}
Future<void> _reconnect() async {
int attempts = 0;
const maxAttempts = 3;
while (attempts < maxAttempts) {
try {
await _database.open(_connectionString, config: _config);
print('Database connection established');
_startHealthChecks();
return;
} catch (e) {
attempts++;
print('Connection attempt $attempts failed: $e');
if (attempts < maxAttempts) {
await Future.delayed(Duration(seconds: 1 << attempts));
}
}
}
throw Exception('Failed to establish database connection after $maxAttempts attempts');
}
void _startHealthChecks() {
_healthCheckTimer?.cancel();
_healthCheckTimer = Timer.periodic(Duration(minutes: 1), (_) async {
try {
if (!_database.isOpen) {
print('Database connection lost, attempting reconnection...');
await _reconnect();
}
} catch (e) {
print('Health check failed: $e');
}
});
}
void dispose() {
_healthCheckTimer?.cancel();
}
}
Resource Management
class ResourceManager {
final Map<String, Timer> _cleanupTimers = {};
final Map<String, int> _resourceCounts = {};
void trackResource(String resourceType) {
_resourceCounts[resourceType] = (_resourceCounts[resourceType] ?? 0) + 1;
// Schedule cleanup if resource count is high
if (_resourceCounts[resourceType]! > 1000) {
_scheduleCleanup(resourceType);
}
}
void releaseResource(String resourceType) {
if (_resourceCounts.containsKey(resourceType)) {
_resourceCounts[resourceType] = _resourceCounts[resourceType]! - 1;
if (_resourceCounts[resourceType]! <= 0) {
_resourceCounts.remove(resourceType);
}
}
}
void _scheduleCleanup(String resourceType) {
_cleanupTimers[resourceType]?.cancel();
_cleanupTimers[resourceType] = Timer(Duration(minutes: 5), () {
_performCleanup(resourceType);
});
}
void _performCleanup(String resourceType) {
print('Performing cleanup for resource type: $resourceType');
// Implement resource-specific cleanup
_resourceCounts[resourceType] = 0;
_cleanupTimers.remove(resourceType);
}
Map<String, int> getResourceCounts() => Map.from(_resourceCounts);
}
Monitoring and Alerting
Error Rate Monitoring
class ErrorRateMonitor {
final Map<String, List<DateTime>> _errorOccurrences = {};
final Duration _timeWindow;
final int _alertThreshold;
final void Function(String errorType, int count) _alertCallback;
ErrorRateMonitor({
this._timeWindow = const Duration(minutes: 5),
this._alertThreshold = 10,
required void Function(String errorType, int count) alertCallback,
}) : _alertCallback = alertCallback;
void recordError(String errorType) {
final now = DateTime.now();
_errorOccurrences.putIfAbsent(errorType, () => []).add(now);
// Clean old occurrences
_cleanOldOccurrences(errorType, now);
// Check if alert threshold is exceeded
final recentCount = _errorOccurrences[errorType]!.length;
if (recentCount >= _alertThreshold) {
_alertCallback(errorType, recentCount);
}
}
void _cleanOldOccurrences(String errorType, DateTime now) {
final cutoff = now.subtract(_timeWindow);
_errorOccurrences[errorType]!.removeWhere((time) => time.isBefore(cutoff));
}
Map<String, int> getCurrentErrorRates() {
final now = DateTime.now();
final rates = <String, int>{};
for (final entry in _errorOccurrences.entries) {
_cleanOldOccurrences(entry.key, now);
rates[entry.key] = entry.value.length;
}
return rates;
}
}
Performance Monitoring
class PerformanceMonitor {
final Map<String, List<Duration>> _operationTimes = {};
final Duration _alertThreshold;
final void Function(String operation, Duration time) _slowOperationCallback;
PerformanceMonitor({
this._alertThreshold = const Duration(seconds: 1),
required void Function(String operation, Duration time) slowOperationCallback,
}) : _slowOperationCallback = slowOperationCallback;
Future<T> monitorOperation<T>(
String operationName,
Future<T> Function() operation,
) async {
final stopwatch = Stopwatch()..start();
try {
final result = await operation();
return result;
} finally {
stopwatch.stop();
_recordOperationTime(operationName, stopwatch.elapsed);
}
}
void _recordOperationTime(String operationName, Duration duration) {
_operationTimes.putIfAbsent(operationName, () => []).add(duration);
// Keep only recent measurements
final recent = _operationTimes[operationName]!;
if (recent.length > 100) {
recent.removeAt(0);
}
// Alert on slow operations
if (duration > _alertThreshold) {
_slowOperationCallback(operationName, duration);
}
}
Map<String, OperationStats> getOperationStats() {
return _operationTimes.map((name, times) {
if (times.isEmpty) {
return MapEntry(name, OperationStats(
operationName: name,
count: 0,
averageTime: Duration.zero,
minTime: Duration.zero,
maxTime: Duration.zero,
));
}
final totalMs = times.fold<int>(0, (sum, time) => sum + time.inMilliseconds);
final avgTime = Duration(milliseconds: totalMs ~/ times.length);
final minTime = times.reduce((a, b) => a < b ? a : b);
final maxTime = times.reduce((a, b) => a > b ? a : b);
return MapEntry(name, OperationStats(
operationName: name,
count: times.length,
averageTime: avgTime,
minTime: minTime,
maxTime: maxTime,
));
});
}
}
class OperationStats {
final String operationName;
final int count;
final Duration averageTime;
final Duration minTime;
final Duration maxTime;
OperationStats({
required this.operationName,
required this.count,
required this.averageTime,
required this.minTime,
required this.maxTime,
});
@override
String toString() {
return 'OperationStats(name: $operationName, count: $count, '
'avg: ${averageTime.inMilliseconds}ms, '
'min: ${minTime.inMilliseconds}ms, '
'max: ${maxTime.inMilliseconds}ms)';
}
}
Complete Error Reference
Error Code Quick Reference
| Code | Category | Description | Severity |
|---|---|---|---|
| VKG-DB-001 | Database | Connection failed | Critical |
| VKG-DB-002 | Database | Already open | Warning |
| VKG-DB-003 | Database | Not open | Error |
| VKG-DB-004 | Database | Invalid object type | Error |
| VKG-TX-001 | Transaction | Already active | Warning |
| VKG-TX-002 | Transaction | Concurrency conflict | Error |
| VKG-TX-003 | Transaction | Timeout | Error |
| VKG-TX-004 | Transaction | Rollback failed | Critical |
| VKG-SEC-001 | Security | Authentication failed | Critical |
| VKG-SEC-002 | Security | Authorization failed | Error |
| VKG-SEC-003 | Security | Rate limit exceeded | Warning |
| VKG-SEC-004 | Security | Tenant limit exceeded | Warning |
| VKG-VEC-001 | Vector | Dimension mismatch | Error |
| VKG-VEC-002 | Vector | Invalid data | Error |
| VKG-VEC-003 | Vector | Space not found | Error |
| VKG-GRP-001 | Graph | Node not found | Error |
| VKG-GRP-002 | Graph | Circular reference | Error |
| VKG-SCH-001 | Schema | Parse error | Critical |
| VKG-SCH-002 | Schema | Model not found | Error |
| VKG-NET-001 | Network | Connection timeout | Error |
| VKG-NET-002 | Network | Service unavailable | Critical |
Exception Hierarchy
VektagrafException
├── VektagrafDatabaseException
│ ├── VektagrafConnectionException
│ ├── VektagrafTransactionException
│ └── VektagrafConcurrencyException
├── VektagrafSecurityException
│ ├── VektagrafAuthenticationException
│ ├── VektagrafAuthorizationException
│ └── VektagrafRateLimitException
├── VektagrafValidationException
│ ├── VektagrafSchemaException
│ └── VektagrafInputException
├── VektagrafNetworkException
│ ├── VektagrafTimeoutException
│ └── VektagrafServiceUnavailableException
└── VektagrafResourceException
├── VektagrafMemoryException
└── VektagrafStorageException
Summary
This chapter provided comprehensive error handling and troubleshooting guidance for Vektagraf:
- Error Classification: Systematic categorization of all error types
- Specific Error Codes: Detailed documentation of common errors and resolutions
- Troubleshooting Procedures: Step-by-step diagnostic and resolution workflows
- Performance Troubleshooting: Memory, query, and resource optimization
- Automated Diagnostics: Tools for systematic problem identification
- Error Prevention: Strategies for avoiding common issues
- Monitoring and Alerting: Proactive error detection and performance monitoring
Use this reference to quickly identify, diagnose, and resolve issues in your Vektagraf applications. The systematic approach and comprehensive error coverage will help you maintain reliable, high-performance systems.
Next Steps
- Chapter 26: Migration and Upgrade Guides - Version compatibility and upgrades
- Appendix I: Complete API Reference - Detailed API documentation
- Appendix II: Configuration Reference - Comprehensive configuration options
- Part IV: Advanced Topics - Performance tuning and optimization
- Part V: Enterprise Deployment - Production deployment patterns
No Comments