You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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 โ†’ imports databaseService
  • 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

  1. src/interfaces/worker-messages.ts (NEW)

    • Type definitions for worker communication
    • Request and response message interfaces
  2. src/registerSQLWorker.js (MAJOR REWRITE)

    • Message-based operation handling
    • Fixed circular dependency with lazy loading
    • Proper error handling and response formatting
  3. src/services/platforms/WebPlatformService.ts (MAJOR REWRITE)

    • Worker-only database access
    • Message sending and response handling
    • Timeout and error management
  4. src/main.web.ts (SIMPLIFIED)

    • Removed duplicate worker creation
    • Simplified initialization flow
  5. 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:

  1. Worker Loading:

    [SQLWorker] Worker loaded, ready to receive messages
    
  2. Database Initialization (only on first operation):

    [SQLWorker] Starting database initialization...
    [SQLWorker] Database initialization completed successfully
    
  3. No Stack Overflow: Application starts without infinite recursion

  4. Single Migration Run: Database migrations execute only once

  5. Functional Database: All queries, inserts, and updates work correctly

Migration from Previous Implementation

If upgrading from the dual-context implementation:

  1. Remove Direct Database Imports: No more import databaseService in main thread
  2. Update Database Calls: Use platform service methods instead of direct database calls
  3. Handle Async Operations: All database operations are now async message-based
  4. 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:

  1. Restore original WebPlatformService.ts
  2. Restore original registerSQLWorker.js
  3. Restore original main.web.ts
  4. 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"