11 KiB
Worker-Only Database Implementation for Web Platform
Overview
This implementation fixes the double migration issue in the TimeSafari web platform by implementing worker-only database access, similar to the Capacitor platform architecture.
Problem Solved
Before: Web platform had dual database contexts:
- Worker thread:
registerSQLWorker.js
โAbsurdSqlDatabaseService.initialize()
โ migrations run - Main thread:
WebPlatformService.dbQuery()
โdatabaseService.query()
โ migrations run AGAIN
After: Single database context:
- Worker thread: Handles ALL database operations and initializes once
- Main thread: Sends messages to worker, no direct database access
Architecture Changes
1. Message-Based Communication
// Main Thread (WebPlatformService)
await this.sendWorkerMessage<QueryResult>({
type: "query",
sql: "SELECT * FROM users",
params: []
});
// Worker Thread (registerSQLWorker.js)
onmessage = async (event) => {
const { id, type, sql, params } = event.data;
if (type === "query") {
const result = await databaseService.query(sql, params);
postMessage({ id, type: "success", data: { result } });
}
};
2. Type-Safe Worker Messages
// src/interfaces/worker-messages.ts
export interface QueryRequest extends BaseWorkerMessage {
type: "query";
sql: string;
params?: unknown[];
}
export type WorkerRequest =
| QueryRequest
| ExecRequest
| GetOneRowRequest
| InitRequest
| PingRequest;
3. Circular Dependency Resolution
๐ฅ Critical Fix: Stack Overflow Prevention
Problem: Circular module dependency caused infinite recursion:
WebPlatformService
constructor โ creates Worker- Worker loads
registerSQLWorker.js
โ importsdatabaseService
- Module resolution creates circular dependency โ Stack Overflow
Solution: Lazy Loading in Worker
// Before (caused stack overflow)
import databaseService from "./services/AbsurdSqlDatabaseService";
// After (fixed)
let databaseService = null;
async function getDatabaseService() {
if (!databaseService) {
// Dynamic import prevents circular dependency
const { default: service } = await import("./services/AbsurdSqlDatabaseService");
databaseService = service;
}
return databaseService;
}
Key Changes for Stack Overflow Fix:
- โ Removed top-level import of database service
- โ Added lazy loading with dynamic import
- โ
Updated all handlers to use
await getDatabaseService()
- โ Removed auto-initialization that triggered immediate loading
- โ Database service only loads when first database operation occurs
Implementation Details
1. WebPlatformService Changes
- Removed direct database imports
- Added worker message handling
- Implemented timeout and error handling
- All database methods now proxy to worker
2. Worker Thread Changes
- Added message-based operation handling
- Implemented lazy loading for database service
- Added proper error handling and response formatting
- Fixed circular dependency with dynamic imports
3. Main Thread Changes
- Removed duplicate worker creation in
main.web.ts
- WebPlatformService now manages single worker instance
- Added Safari compatibility with
initBackend()
Files Modified
-
src/interfaces/worker-messages.ts (NEW)
- Type definitions for worker communication
- Request and response message interfaces
-
src/registerSQLWorker.js (MAJOR REWRITE)
- Message-based operation handling
- Fixed circular dependency with lazy loading
- Proper error handling and response formatting
-
src/services/platforms/WebPlatformService.ts (MAJOR REWRITE)
- Worker-only database access
- Message sending and response handling
- Timeout and error management
-
src/main.web.ts (SIMPLIFIED)
- Removed duplicate worker creation
- Simplified initialization flow
-
WORKER_ONLY_DATABASE_IMPLEMENTATION.md (NEW)
- Complete documentation of changes
Benefits
โ Fixes Double Migration Issue
- Database migrations now run only once in worker thread
- No duplicate initialization between main thread and worker
โ Prevents Stack Overflow
- Circular dependency resolved with lazy loading
- Worker loads immediately without triggering database import
- Database service loads on-demand when first operation occurs
โ Improved Performance
- Single database connection
- No redundant operations
- Better resource utilization
โ Better Error Handling
- Centralized error handling in worker
- Type-safe message communication
- Proper timeout handling
โ Consistent Architecture
- Matches Capacitor platform pattern
- Single-threaded database access
- Clear separation of concerns
Testing Verification
After implementation, you should see:
-
Worker Loading:
[SQLWorker] Worker loaded, ready to receive messages
-
Database Initialization (only on first operation):
[SQLWorker] Starting database initialization... [SQLWorker] Database initialization completed successfully
-
No Stack Overflow: Application starts without infinite recursion
-
Single Migration Run: Database migrations execute only once
-
Functional Database: All queries, inserts, and updates work correctly
Migration from Previous Implementation
If upgrading from the dual-context implementation:
- Remove Direct Database Imports: No more
import databaseService
in main thread - Update Database Calls: Use platform service methods instead of direct database calls
- Handle Async Operations: All database operations are now async message-based
- Error Handling: Update error handling to work with worker responses
Security Considerations
- Worker thread isolates database operations
- Message validation prevents malformed requests
- Timeout handling prevents hanging operations
- Type safety reduces runtime errors
Performance Notes
- Initial worker creation has minimal overhead
- Database operations have message passing overhead (negligible)
- Single database connection is more efficient than dual connections
- Lazy loading reduces startup time
Migration Execution Flow
Before (Problematic)
โโโโโโโโโโโโโโโ โโโโ โโโโโโโโโโโโโโโโโโโ
โ Main Thread โ โ Worker Thread โ
โ โ โ โ
โ WebPlatformServiceโ โregisterSQLWorkerโ
โ โ โ โ โ โ
โ databaseService โ โ databaseService โ
โ (Instance A) โ โ (Instance B) โ
โ โ โ โ โ โ
โ [Run Migrations] โ โ[Run Migrations] โ โ DUPLICATE!
โโโโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโโโโโโ
After (Fixed)
โโโโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโโโโโโ
โ Main Thread โ โ Worker Thread โ
โ โ โ โ
โ WebPlatformService โโโโโโregisterSQLWorkerโ
โ โ โ โ โ
โ [Send Messages] โ โ databaseService โ
โ โ โ(Single Instance)โ
โ โ โ โ โ
โ โ โ[Run Migrations] โ โ ONCE ONLY!
โโโโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโโโโโโ
Security Considerations
1. Message Validation
- All worker messages validated for required fields
- Unknown message types rejected with errors
- Proper error responses prevent information leakage
2. Timeout Protection
- 30-second timeout prevents hung operations
- Automatic cleanup of pending messages
- Worker health checks via ping/pong
3. Error Sanitization
- Error messages logged but not exposed raw to main thread
- Stack traces included only in development
- Graceful handling of worker failures
Testing Considerations
1. Unit Tests Needed
- Worker message handling
- WebPlatformService worker communication
- Error handling and timeouts
- Migration execution (should run once only)
2. Integration Tests
- End-to-end database operations
- Worker lifecycle management
- Cross-browser compatibility (especially Safari)
3. Performance Tests
- Message passing overhead
- Database operation throughput
- Memory usage with worker communication
Browser Compatibility
1. Modern Browsers
- Chrome/Edge: Full SharedArrayBuffer support
- Firefox: Full SharedArrayBuffer support (with headers)
- Safari: Uses IndexedDB fallback via
initBackend()
2. Required Headers
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Deployment Notes
1. Development
- Enhanced logging shows worker message flow
- Clear separation between worker and main thread logs
- Easy debugging via browser DevTools
2. Production
- Reduced logging overhead
- Optimized message passing
- Proper error reporting without sensitive data
Future Enhancements
1. Potential Optimizations
- Message batching for bulk operations
- Connection pooling simulation
- Persistent worker state management
2. Additional Features
- Database backup/restore via worker
- Schema introspection commands
- Performance monitoring hooks
Rollback Plan
If issues arise, rollback involves:
- Restore original
WebPlatformService.ts
- Restore original
registerSQLWorker.js
- Restore original
main.web.ts
- Remove
worker-messages.ts
interface
Commit Messages
git add src/interfaces/worker-messages.ts
git commit -m "Add worker message interface for type-safe database communication
- Define TypeScript interfaces for worker request/response messages
- Include query, exec, getOneRow, init, and ping message types
- Provide type safety for web platform worker messaging"
git add src/registerSQLWorker.js
git commit -m "Implement message-based worker for single-point database access
- Replace simple auto-init with comprehensive message handler
- Add support for query, exec, getOneRow, init, ping operations
- Implement proper error handling and response management
- Ensure single database initialization point to prevent double migrations"
git add src/services/platforms/WebPlatformService.ts
git commit -m "Migrate WebPlatformService to worker-only database access
- Remove direct databaseService import to prevent dual context issue
- Implement worker-based messaging for all database operations
- Add worker lifecycle management with initialization tracking
- Include message timeout and error handling for reliability
- Add Safari compatibility with initBackend call"
git add src/main.web.ts
git commit -m "Remove duplicate worker creation from main.web.ts
- Worker initialization now handled by WebPlatformService
- Prevents duplicate worker creation and database contexts
- Simplifies main thread initialization"
git add WORKER_ONLY_DATABASE_IMPLEMENTATION.md
git commit -m "Document worker-only database implementation
- Comprehensive documentation of architecture changes
- Explain problem solved and benefits achieved
- Include security considerations and testing requirements"