How WorkerService Handles Background Initialization Without Blocking HTTP Requests in Claude-Mem

Claude-Mem’s WorkerService starts its HTTP server immediately and defers all heavyweight initialization to a background promise, using a guard middleware to queue or reject API requests until startup completes.

The WorkerService in Claude-Mem acts as the core orchestrator responsible for bootstrapping the database, Chroma vector store, and MCP connections. To ensure the service remains responsive to health checks and orchestration probes, it must prevent these lengthy startup tasks from blocking the HTTP request pipeline. The implementation achieves this through a carefully sequenced startup flow and a promise-based synchronization mechanism defined in src/services/worker-service.ts.

The Initialization Challenge in Claude-Mem

When Claude-Mem starts, it must perform several I/O-intensive operations before it can serve meaningful API traffic: loading the database manager, selecting the operational mode, starting the Chroma server, and establishing MCP connections. If these steps executed synchronously before the HTTP server bound to its port, orchestrators like Kubernetes would see the container as "not ready" and potentially trigger unnecessary restarts. The WorkerService solves this by separating "server availability" from "service readiness."

How WorkerService Implements Non-Blocking Startup

The non-blocking behavior relies on three coordinated mechanisms: a deferred promise for tracking initialization state, immediate server binding, and asynchronous background tasks.

Promise-Based Initialization Tracking

Inside the WorkerService constructor, the service creates a Promise<void> that acts as a gate for incoming requests. The constructor stores both the promise and its resolver function, allowing the background process to signal completion later.

// Inside src/services/worker-service.ts
private initializationComplete: Promise<void>;
private resolveInitialization!: () => void;
private initializationCompleteFlag: boolean = false;

constructor() {
  // Create the promise that will resolve when init finishes
  this.initializationComplete = new Promise<void>((resolve) => {
    this.resolveInitialization = resolve;
  });
}

The initializationCompleteFlag boolean provides a fast synchronous check, while the promise supports asynchronous waiting with timeout capabilities.

Immediate Server Startup

The start() method in WorkerService binds the HTTP server to its configured port and host before any heavy initialization begins. This ensures the operating system marks the port as occupied and orchestrators receive successful TCP health checks immediately.

async start(): Promise<void> {
  const port = getWorkerPort();
  const host = getWorkerHost();

  // 1. HTTP server starts first
  await this.server.listen(port, host);

  // 2. PID file written after listen succeeds
  writePidFile({ 
    pid: process.pid, 
    port, 
    startedAt: new Date().toISOString() 
  });

  logger.info('SYSTEM', 'Worker started', { host, port, pid: process.pid });

  // 3. Heavy init runs in background without blocking
  this.initializeBackground().catch(err => {
    logger.error('SYSTEM', 'Background initialization failed', {}, err);
  });
}

By not await-ing the initializeBackground() call, the start() method returns control to the caller immediately, leaving the background tasks to run concurrently.

Background Initialization Workflow

The initializeBackground() method performs the actual heavyweight setup: loading the mode configuration, starting the Chroma server, initializing the database manager, and establishing MCP connections. Only after all these steps complete does it resolve the initialization promise.

private async initializeBackground(): Promise<void> {
  // ... load mode, start Chroma, init DB, connect MCP ...
  await this.dbManager.initialize();

  // Mark init as done and release waiting requests
  this.initializationCompleteFlag = true;
  this.resolveInitialization();  // Resolves the promise used by guard middleware
  
  logger.info('SYSTEM', 'Background initialization complete');
}

Protecting API Routes During Initialization

With the server accepting connections immediately but the service not yet ready, WorkerService must prevent premature API requests from executing. It accomplishes this through a guard middleware that pauses or rejects requests based on the initialization state.

The Guard Middleware Implementation

In registerRoutes(), the service installs an Express middleware for all /api/* routes (with specific exemptions for health checks). This middleware checks the initializationCompleteFlag synchronously for fast-path continuation, or awaits the initialization promise if the flag is false.

// Inside registerRoutes() in src/services/worker-service.ts
this.server.app.use('/api', async (req, res, next) => {
  // Fast path: init already complete
  if (this.initializationCompleteFlag) {
    return next();
  }

  const timeoutMs = 30_000;
  const timeout = new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error('Database initialization timeout')), timeoutMs)
  );

  try {
    // Wait for background init or timeout
    await Promise.race([this.initializationComplete, timeout]);
    next();  // Proceed to actual handler
  } catch {
    logger.error('HTTP', `Request to ${req.method} ${req.path} rejected — DB not initialized`);
    res.status(503).json({
      error: 'Service initializing',
      message: 'Database is still initializing, please retry'
    });
  }
});

Handling Timeouts and 503 Responses

If the background initialization takes longer than 30 seconds, the middleware rejects the request with HTTP 503 Service Unavailable. This signals to clients and load balancers that the service is temporarily unavailable but actively recovering, distinguishing it from a hard failure. Clients receive a JSON payload explaining the initialization state.

Readiness Endpoint Integration

The src/services/server/Server.ts file exposes a /api/readiness endpoint that mirrors the initialization flag logic. While the guard middleware protects individual routes, the readiness probe provides a lightweight check for orchestrators.

// Conceptual implementation in Server.ts
app.get('/api/readiness', (req, res) => {
  if (workerService.isInitializationComplete()) {
    res.status(200).json({ status: 'ready' });
  } else {
    res.status(503).json({ status: 'initializing' });
  }
});

This dual mechanism—middleware for request protection and explicit endpoint for health checks—ensures robust handling of the initialization window.

Summary

  • Immediate server binding: WorkerService.start() binds the HTTP server before any heavy initialization, ensuring the port is active for health probes.
  • Promise-based synchronization: A deferred Promise<void> and boolean flag track initialization state, allowing asynchronous coordination between the main thread and background workers.
  • Non-blocking background tasks: initializeBackground() runs database, Chroma, and MCP setup without blocking the start() method return.
  • Guard middleware protection: All /api/* routes (except health checks) pass through middleware that waits for initialization or returns 503 Service Unavailable after a 30-second timeout.
  • Readiness endpoint: /api/readiness in src/services/server/Server.ts provides explicit health state for orchestrators, reflecting the same initialization flag used by the middleware.

Frequently Asked Questions

How does WorkerService prevent database initialization from blocking incoming HTTP requests?

WorkerService separates the HTTP server startup from the database initialization sequence. In src/services/worker-service.ts, the start() method calls await this.server.listen() to bind the port immediately, then invokes this.initializeBackground() without awaiting it. This allows the server to accept connections while the database, Chroma server, and MCP connections initialize in the background. A guard middleware then holds API requests until the initialization promise resolves.

What happens to API requests received before WorkerService finishes initializing?

Requests to /api/* routes hit a guard middleware that checks this.initializationCompleteFlag. If initialization is incomplete, the middleware races the initializationComplete promise against a 30-second timeout. If the background work finishes first, the request proceeds normally. If the timeout fires first, the client receives an HTTP 503 Service Unavailable response with a JSON error message indicating the service is still initializing.

How does the readiness endpoint differ from the guard middleware?

The readiness endpoint at /api/readiness (defined in src/services/server/Server.ts) provides an explicit health check for orchestrators like Kubernetes, returning 200 OK when initializationCompleteFlag is true and 503 when false. The guard middleware performs the same flag check but operates transparently on functional API routes, automatically queueing or rejecting requests without requiring clients to manually poll a health endpoint first.

Why does WorkerService use both a boolean flag and a promise for initialization tracking?

The boolean initializationCompleteFlag provides a fast synchronous check used by the guard middleware to immediately pass through requests when initialization is already complete, avoiding unnecessary promise overhead. The initializationComplete promise enables asynchronous waiting for the rare cases where requests arrive during the initialization window, allowing the middleware to suspend the request until the background work signals completion via resolveInitialization().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →