Database Cleanup and Shutdown
The ReactiveDatabase provides a shutdown()
method to properly clean up resources and close connections when the database is no longer needed. The shutdown()
method is a crucial cleanup mechanism that ensures all database resources are properly released. It:
- Closes the database connection
- Cleans up event listeners
- Runs registered cleanup callbacks
- Prevents memory leaks and resource wastage
When to Use shutdown()
You should call shutdown()
when:
- Your application is closing/unmounting
- You no longer want to use the database
const db = new ReactiveDatabase(config)
// ... use database
await db.shutdown() // Clean up when done
How shutdown()
Works
Internal Cleanup Process
Set Shutdown Flag
- Sets
shuttingDown
flag to prevent unexpected behavior during cleanup
- Sets
Execute Cleanup Callbacks
- Runs all registered cleanup callbacks concurrently
- Callbacks can be registered by models and relationships
- Waits for all callbacks to complete
Close Database Connection
- Closes the Dexie database connection
- Disables auto-open to prevent reconnection
- Waits for the 'close' event confirmation
Clear Model Constructors
- Removes all model constructor references
- Helps prevent memory leaks
Remove Event Listeners
- Cleans up error bus listeners
- Removes all log event listeners
- Ensures no lingering event handlers
Best Practices
1. Always await shutdown()
The shutdown process is asynchronous, so always await its completion:
// Good
await db.shutdown()
// Bad - may lead to incomplete cleanup
db.shutdown()
2. Prevent Further Operations
After calling shutdown, the database instance should not be used:
const db = new ReactiveDatabase(config)
await db.shutdown()
// Don't do this - database is shut down
await db.model('users').find(1) // Will fail
3. Handle Cleanup in Components
In frontend frameworks, implement shutdown in cleanup/unmount hooks:
import { useEffect } from 'react'
import { ReactiveDatabase } from '@nhtio/web-re-active-record'
function MyComponent() {
useEffect(() => {
const db = new ReactiveDatabase(config)
return () => {
// Cleanup when component unmounts
await db.shutdown()
}
}, [])
return <div>My Component</div>
}
import { onUnmounted } from 'vue'
import { ReactiveDatabase } from '@nhtio/web-re-active-record'
export default {
setup() {
const db = new ReactiveDatabase(config)
onUnmounted(async () => {
// Cleanup when component unmounts
await db.shutdown()
})
return {}
}
}
import { ReactiveDatabase } from '@nhtio/web-re-active-record'
export default {
data() {
return {
db: new ReactiveDatabase(config)
}
},
beforeUnmount() {
// Cleanup when component unmounts
await this.db.shutdown()
}
}
import { Component, OnDestroy } from '@angular/core'
import { ReactiveDatabase } from '@nhtio/web-re-active-record'
@Component({
selector: 'app-my-component',
template: '<div>My Component</div>'
})
export class MyComponent implements OnDestroy {
private db: ReactiveDatabase
constructor() {
this.db = new ReactiveDatabase(config)
}
async ngOnDestroy() {
// Cleanup when component is destroyed
await this.db.shutdown()
}
}
4. Manage Multiple Instances
When working with multiple database instances, shut them down independently:
const db1 = new ReactiveDatabase(config1)
const db2 = new ReactiveDatabase(config2)
// Shutdown each instance
await Promise.all([
db1.shutdown(),
db2.shutdown()
])
Error Handling
The shutdown process is designed to be robust, but you should still handle potential errors:
try {
await db.shutdown()
} catch (error) {
console.error('Error during shutdown:', error)
// Implement fallback cleanup if needed
}
Impact on Running Operations
When shutdown is called:
- Ongoing operations are allowed to complete
- New operations are prevented
- Cleanup process begins after current operations finish
- Event listeners are maintained until cleanup is complete
Logging During Shutdown
The shutdown process generates various log events that can be monitored:
db.logger.on('debug', (msg) => console.debug('Shutdown debug:', msg))
db.logger.on('info', (msg) => console.info('Shutdown info:', msg))
db.logger.on('error', (msg) => console.error('Shutdown error:', msg))
await db.shutdown()
// Debug: Starting database shutdown
// Debug: Running cleanup callbacks
// Debug: Database connection closing
// Info: Database shutdown complete
Static Shutdown Method
ReactiveDatabase provides a static shutdown()
method that can close all database connections in the current context:
import { ReactiveDatabase } from '@nhtio/web-re-active-record'
// Shutdown all database instances
await ReactiveDatabase.shutdown()
This is particularly useful when:
- You have multiple database instances that need to be cleaned up
- You're implementing application-wide cleanup
- You need to ensure all database connections are closed during page unload
How Static Shutdown Works
The static shutdown method:
- Tracks all ReactiveDatabase instances created in the current context
- When called, attempts to shut down each instance
- Continues even if individual shutdowns fail
- Returns a Promise that resolves when all shutdowns are complete
Example usage in an application cleanup:
// Application cleanup
window.addEventListener('unload', () => {
// Ensure all database connections are closed
await ReactiveDatabase.shutdown()
})
Using with multiple databases:
const userDb = new ReactiveDatabase({
namespace: 'users',
// ... other config
})
const productDb = new ReactiveDatabase({
namespace: 'products',
// ... other config
})
// Later, shutdown all databases at once
await ReactiveDatabase.shutdown()
// Both userDb and productDb are now shut down