Skip to content

Deep Health Check — Complete Implementation Guide

DodaTech Updated 2026-06-28 9 min read

In this tutorial, you will learn about Deep Health Check. We cover key concepts, practical examples, and best practices to help you master this topic.

Deep health checks verify that the service is not just running but functioning correctly by performing end-to-end operations like writing test data, validating responses, and measuring performance.

What You'll Learn

By the end of this tutorial, you will know how to implement deep health checks that verify database read/write operations, cache consistency, business logic correctness, and response performance.

Why It Matters

A service can pass basic and dependency checks but still be broken. Deep health checks catch logic errors, data corruption, and performance degradation that simpler checks miss.

Real-World Use

Durga Antivirus Pro's scanning API runs a deep health check that downloads a known test file, scans it, and verifies the result matches expectations. If the scan produces the wrong result, the service is marked unhealthy.

Deep Health Check Learning Path

flowchart LR
  A[Dependency Health] --> B[Deep Health Check]
  B --> C[Read/Write Test]
  B --> D[Business Logic]
  B --> E[Performance Check]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Database Read/Write Health Check

Verify the database is fully functional by writing and reading a test record.

class DatabaseReadWriteCheck {
  constructor(db) {
    this.db = db;
    this.testPrefix = "health_check_";
  }

  async check() {
    const testId = `${this.testPrefix}${Date.now()}`;
    const results = [];

    try {
      // Write
      const writeStart = Date.now();
      await this.db.query(
        "INSERT INTO health_checks (id, created_at) VALUES ($1, NOW())",
        [testId]
      );
      results.push({ operation: "write", latencyMs: Date.now() - writeStart });

      // Read
      const readStart = Date.now();
      const readResult = await this.db.query(
        "SELECT * FROM health_checks WHERE id = $1",
        [testId]
      );
      results.push({ operation: "read", latencyMs: Date.now() - readStart });

      // Cleanup
      await this.db.query("DELETE FROM health_checks WHERE id = $1", [testId]);

      const allSuccessful = results.every(r => r.latencyMs !== undefined);
      return {
        name: "database-deep",
        healthy: allSuccessful,
        operations: results,
        totalLatencyMs: results.reduce((s, r) => s + r.latencyMs, 0)
      };
    } catch (err) {
      return {
        name: "database-deep",
        healthy: false,
        error: err.message,
        operations: results
      };
    }
  }
}

const { Pool } = require("pg");
const pool = new Pool({ connectionString: "postgresql://localhost/test" });
const deepDb = new DatabaseReadWriteCheck(pool);
deepDb.check().then(r => console.log("Deep DB:", r.healthy ? "ok" : "FAIL"));

Business Logic Verification

Test that critical business operations produce the expected results.

class BusinessLogicHealthCheck {
  constructor(calculator) {
    this.calculator = calculator;
  }

  async check() {
    const tests = [
      { name: "calculate-total", fn: () => this.calculator.calculateTotal([10, 20, 30]), expected: 60 },
      { name: "apply-discount", fn: () => this.calculator.applyDiscount(100, 10), expected: 90 },
      { name: "validate-email", fn: () => this.calculator.validateEmail("test@example.com"), expected: true },
      { name: "validate-bad-email", fn: () => this.calculator.validateEmail("invalid"), expected: false }
    ];

    const results = await Promise.all(tests.map(async (test) => {
      try {
        const actual = await test.fn();
        const passed = actual === test.expected;
        return { name: test.name, passed, actual, expected: test.expected };
      } catch (err) {
        return { name: test.name, passed: false, error: err.message };
      }
    }));

    const allPassed = results.every(r => r.passed);
    return {
      name: "business-logic",
      healthy: allPassed,
      tests: results,
      passedCount: results.filter(r => r.passed).length,
      totalCount: results.length
    };
  }
}

const mockCalculator = {
  calculateTotal: async (items) => items.reduce((s, i) => s + i, 0),
  applyDiscount: async (price, percent) => price * (1 - percent / 100),
  validateEmail: async (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
};

const bizCheck = new BusinessLogicHealthCheck(mockCalculator);
bizCheck.check().then(r => {
  console.log("Business logic:", r.healthy ? "ok" : "FAIL");
  console.log(`Passed ${r.passedCount}/${r.totalCount} tests`);
});
// Business logic: ok
// Passed 4/4 tests

Performance Health Check

Verify that operations complete within acceptable time thresholds.

class PerformanceHealthCheck {
  constructor(thresholds) {
    this.thresholds = {
      databaseQueryMs: 100,
      cacheFetchMs: 20,
      apiResponseMs: 500,
      ...thresholds
    };
  }

  async checkPerformance(operation) {
    const start = Date.now();
    await operation();
    return Date.now() - start;
  }

  async check() {
    const checks = [];

    // Database query performance
    const dbLatency = await this.checkPerformance(async () => {
      await new Promise(r => setTimeout(r, 30));
    });
    checks.push({
      name: "database-performance",
      passed: dbLatency <= this.thresholds.databaseQueryMs,
      latencyMs: dbLatency,
      thresholdMs: this.thresholds.databaseQueryMs
    });

    // Cache performance
    const cacheLatency = await this.checkPerformance(async () => {
      await new Promise(r => setTimeout(r, 10));
    });
    checks.push({
      name: "cache-performance",
      passed: cacheLatency <= this.thresholds.cacheFetchMs,
      latencyMs: cacheLatency,
      thresholdMs: this.thresholds.cacheFetchMs
    });

    const allPassed = checks.every(c => c.passed);
    return {
      name: "performance",
      healthy: allPassed,
      checks,
      timestamp: new Date().toISOString()
    };
  }
}

const perfCheck = new PerformanceHealthCheck({
  databaseQueryMs: 100,
  cacheFetchMs: 20,
  apiResponseMs: 500
});

perfCheck.check().then(r => {
  console.log("Performance:", r.healthy ? "ok" : "degraded");
  r.checks.forEach(c => {
    console.log(`  ${c.name}: ${c.latencyMs}ms (threshold: ${c.thresholdMs}ms) ${c.passed ? "ok" : "SLOW"}`);
  });
});
// Performance: ok
//   database-performance: 30ms (threshold: 100ms) ok
//   cache-performance: 10ms (threshold: 20ms) ok

End-to-End API Flow Check

Simulate a complete user flow to verify the entire system works.

class EndToEndHealthCheck {
  constructor(apiClient) {
    this.client = apiClient;
  }

  async simulateUserFlow() {
    const steps = [];

    // Step 1: Create a resource
    try {
      const createRes = await this.client.post("/api/users", {
        name: "health-test",
        email: `health-${Date.now()}@test.com`
      });
      const userId = createRes.id;
      steps.push({ step: "create-user", passed: true, userId });

      // Step 2: Read the resource
      const readRes = await this.client.get(`/api/users/${userId}`);
      steps.push({ step: "read-user", passed: readRes.id === userId });

      // Step 3: Update the resource
      await this.client.patch(`/api/users/${userId}`, { name: "health-test-updated" });
      steps.push({ step: "update-user", passed: true });

      // Step 4: Delete the resource
      await this.client.delete(`/api/users/${userId}`);
      steps.push({ step: "delete-user", passed: true });

    } catch (err) {
      steps.push({ step: "e2e-flow", passed: false, error: err.message });
    }

    const allPassed = steps.every(s => s.passed);
    return {
      name: "end-to-end",
      healthy: allPassed,
      steps
    };
  }
}

const mockClient = {
  store: {},
  idCounter: 0,
  async post(path, data) {
    this.idCounter++;
    const id = this.idCounter;
    this.store[id] = { ...data, id };
    return { id };
  },
  async get(path) {
    const id = parseInt(path.split("/").pop());
    return this.store[id] || null;
  },
  async patch(path, data) {
    const id = parseInt(path.split("/").pop());
    if (this.store[id]) Object.assign(this.store[id], data);
  },
  async delete(path) {
    const id = parseInt(path.split("/").pop());
    delete this.store[id];
  }
};

const e2e = new EndToEndHealthCheck(mockClient);
e2e.simulateUserFlow().then(r => {
  console.log("E2E flow:", r.healthy ? "ok" : "FAIL");
  r.steps.forEach(s => console.log(`  ${s.step}: ${s.passed ? "ok" : "FAIL"}`));
});
// E2E flow: ok
//   create-user: ok
//   read-user: ok
//   update-user: ok
//   delete-user: ok

Scheduling Deep Health Checks

Deep health checks are expensive. Run them on a schedule, not on every probe.

class ScheduledDeepHealthCheck {
  constructor(checkFn, options = {}) {
    this.checkFn = checkFn;
    this.intervalMs = options.intervalMs || 60000;
    this.lastResult = null;
    this.lastRunTime = 0;
  }

  async getResult() {
    const now = Date.now();
    if (now - this.lastRunTime > this.intervalMs) {
      console.log("Running scheduled deep health check");
      this.lastResult = await this.checkFn();
      this.lastRunTime = now;
    }
    return this.lastResult;
  }

  async forceRun() {
    this.lastResult = await this.checkFn();
    this.lastRunTime = Date.now();
    return this.lastResult;
  }
}

const scheduled = new ScheduledDeepHealthCheck(
  async () => ({ healthy: true, checkedAt: new Date().toISOString() }),
  { intervalMs: 60000 }
);

scheduled.getResult().then(r => {
  console.log("Scheduled check result:", r.healthy);
});
// Running scheduled deep health check
// Scheduled check result: true

Common Mistakes

  1. Running deep checks on every probe -- Deep checks are expensive. Run them every 30-60 seconds, not every 5 seconds like basic checks.

  2. Not cleaning up test data -- Deep checks that write test data must clean up afterward. Accumulated test data can affect production queries and storage.

  3. Using deep check failures for liveness -- A deep check failure doesn't mean the Process needs restarting. Use deep checks for readiness, not liveness.

  4. Not randomizing test data -- Using the same test ID every time creates cache hits that don't test real code paths. Randomize test data.

  5. Making deep checks too deep -- A deep check that tests every feature is fragile and slow. Test critical paths only.

Practice Questions

  1. What is the difference between a dependency check and a deep check? A dependency check verifies connectivity (can I reach the database?). A deep check verifies functionality (can I write and read data?).

  2. Why should deep health checks include cleanup? To prevent test records from accumulating in production databases, affecting storage, queries, and data integrity.

  3. How often should deep health checks run? Every 30-60 seconds. They're too expensive to run on every probe but should run frequently enough to detect issues promptly.

  4. Challenge: Implement a deep health check that reports the latency of each operation and marks unhealthy if any operation exceeds a threshold.

class ThresholdBasedDeepCheck {
  constructor(operations, maxLatencyMs = 1000) {
    this.operations = operations;
    this.maxLatencyMs = maxLatencyMs;
  }

  async run() {
    const results = [];
    let anySlow = false;

    for (const op of this.operations) {
      const start = Date.now();
      try {
        await op.fn();
        const latency = Date.now() - start;
        const slow = latency > this.maxLatencyMs;
        if (slow) anySlow = true;
        results.push({ name: op.name, passed: !slow, latencyMs: latency, thresholdMs: this.maxLatencyMs });
      } catch (err) {
        results.push({ name: op.name, passed: false, error: err.message });
        anySlow = true;
      }
    }

    return { healthy: !anySlow, operations: results };
  }
}

const check = new ThresholdBasedDeepCheck([
  { name: "db-write", fn: async () => new Promise(r => setTimeout(r, 50)) },
  { name: "cache-read", fn: async () => new Promise(r => setTimeout(r, 10)) }
], 200);

check.run().then(r => console.log("Threshold check:", r.healthy));
// Threshold check: true

FAQ

Can deep health checks cause data corruption?

Only if the cleanup fails. Always use unique test IDs, wrap the check in a transaction, and ensure cleanup runs even if the check fails.

Should deep checks run against production data?

No. Deep checks should use test data or synthetic data. Running against real production data risks corruption.

What happens if a deep check times out?

The check is marked as failed. The service should not be considered healthy if critical operations time out.

How do I handle deep checks in a read-replica architecture?

Run write tests against the primary, read tests against replicas. This validates both paths.

Can deep checks trigger alerts?

Yes. Deep check failures should trigger alerts because they indicate functional problems, not just connectivity issues.

Mini Project

Build a deep health check module that performs database read/write tests, verifies critical business logic, measures operation latency, and reports results with pass/fail status per operation.

class DeepHealthModule {
  constructor() {
    this.testers = [];
  }

  addTester(name, tester) {
    this.testers.push({ name, run: tester });
  }

  async execute() {
    const results = [];

    for (const { name, run } of this.testers) {
      try {
        const result = await run();
        results.push({ tester: name, ...result });
      } catch (err) {
        results.push({ tester: name, passed: false, error: err.message });
      }
    }

    const allPassed = results.every(r => r.passed !== false);
    return {
      status: allPassed ? "healthy" : "unhealthy",
      results,
      timestamp: new Date().toISOString()
    };
  }
}

const module = new DeepHealthModule();
module.addTester("Database Read/Write", async () => ({ passed: true, latencyMs: 45 }));
module.addTester("Business Logic", async () => ({ passed: true, testsPassed: 4 }));
module.addTester("Performance", async () => ({ passed: true, maxLatencyMs: 89 }));
module.execute().then(r => console.log("Deep health:", r.status, "-", r.results.length, "tests"));

What's Next

Now that you understand deep health checks, implement a health check in Express.js. Then explore Django health check integration.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro