Error Handling
Proper error handling is crucial for building robust applications with QuantaDB. This section covers various error handling scenarios and best practices.
Basic Error Handling
1. Try-Catch Blocks
The most basic form of error handling is using try-catch blocks:
try {
// Try to get a non-existent key
await db.get('non-existent-key');
} catch (e) {
print('Expected error for non-existent key: $e');
}
2. Type-Specific Error Handling
QuantaDB throws specific types of exceptions that you can catch and handle appropriately:
try {
// Try to put invalid data
await db.put('invalid-key', Object());
} catch (e) {
if (e is QuantaDBException) {
print('Database error: ${e.message}');
} else {
print('Unexpected error: $e');
}
}
Common Error Scenarios
1. Non-Existent Keys
try {
final value = await db.get('non-existent-key');
if (value == null) {
print('Key does not exist');
}
} catch (e) {
print('Error accessing key: $e');
}
2. Invalid Data Types
try {
// Try to store an invalid object
await db.put('invalid-key', Object());
} catch (e) {
print('Error storing invalid data: $e');
}
3. Invalid Queries
try {
final queryEngine = QueryEngine(db.storage);
await queryEngine.query<User>(
Query<User>()
.where((user) => user.email.length > 100), // Invalid but type-safe
);
} catch (e) {
print('Error executing query: $e');
}
Error Handling in Transactions
1. Transaction Rollback
try {
await db.storage.transaction((txn) async {
await txn.put('key1', 'value1');
throw Exception('Operation failed');
// Transaction will be rolled back
});
} catch (e) {
print('Transaction failed: $e');
// Verify rollback
final value = await db.get('key1');
print('Value after rollback: $value'); // Should be null
}
2. Nested Error Handling
try {
await db.storage.transaction((txn) async {
try {
await txn.put('key1', 'value1');
await txn.put('key2', 'value2');
} catch (e) {
print('Error within transaction: $e');
// Transaction will still be rolled back
rethrow; // Re-throw to trigger transaction rollback
}
});
} catch (e) {
print('Transaction failed: $e');
}
Error Handling in Queries
1. Query Validation
try {
final queryEngine = QueryEngine(db.storage);
await queryEngine.query<User>(
Query<User>()
.where((user) => user.email.contains('@')) // Valid query
.sortBy((user) => user.name),
);
} catch (e) {
print('Query error: $e');
}
2. Type Safety in Queries
try {
final queryEngine = QueryEngine(db.storage);
await queryEngine.query<User>(
Query<User>()
.where((user) => user.isActive)
.where((user) => user.email.length > 100), // Invalid condition
);
} catch (e) {
print('Type safety error in query: $e');
}
Error Handling in Reactive Queries
1. Stream Error Handling
final activeUsersStream = queryEngine.watch<User, User>(
Query<User>().where((user) => user.isActive),
);
final subscription = activeUsersStream.listen(
(user) => print('Active user updated: ${user.name}'),
onError: (error) {
print('Error in active users stream: $error');
// Implement retry logic or error recovery
},
cancelOnError: false, // Continue listening after errors
);
2. Aggregate Query Error Handling
final userStatsStream = queryEngine.watch<User, Map<String, dynamic>>(
Query<User>().aggregate((users) {
try {
return {
'total': users.length,
'active': users.where((u) => u.isActive).length,
'domains': users.map((u) => u.email.split('@').last).toSet().length,
};
} catch (e) {
print('Error in aggregate function: $e');
return {'error': e.toString()};
}
}),
);
Best Practices
-
Always Use Try-Catch
try {
await db.put('key', value);
} catch (e) {
// Handle error appropriately
print('Error: $e');
} -
Specific Error Types
try {
await db.get('key');
} catch (e) {
if (e is QuantaDBException) {
// Handle database-specific errors
} else if (e is TypeError) {
// Handle type errors
} else {
// Handle unexpected errors
}
} -
Error Recovery
Future<void> retryOperation() async {
int attempts = 0;
while (attempts < 3) {
try {
await db.put('key', value);
break;
} catch (e) {
attempts++;
if (attempts == 3) rethrow;
await Future.delayed(Duration(seconds: attempts));
}
}
} -
Logging and Monitoring
try {
await db.put('key', value);
} catch (e, stackTrace) {
print('Error: $e');
print('Stack trace: $stackTrace');
// Log to monitoring service
}
Next Steps
- Learn about Transactions for atomic operations
- Explore Batch Operations for efficient bulk data operations
- Check out Query Operations for retrieving data