Skip to main content

TealCircuit

TealCircuit implements the circuit breaker pattern to prevent cascading failures in distributed systems.

Overview

TealCircuit provides:
  • Three-state circuit breaker (CLOSED, OPEN, HALF-OPEN)
  • Automatic failure detection and recovery
  • Decision contract for consistency
  • Correlation IDs for traceability

Class

class TealCircuit {
  constructor(config: TealCircuitConfig);
  
  execute<T>(fn: () => Promise<T>): Promise<T>;
  evaluate(context?: ExecutionContext): Decision;
  getState(): CircuitState;
  reset(): void;
}

Configuration

interface TealCircuitConfig {
  failureThreshold?: number;
  timeout?: number;
  halfOpenRequests?: number;
  onStateChange?: (newState: CircuitState, oldState: CircuitState) => void;
}

enum CircuitState {
  CLOSED = 'closed',
  OPEN = 'open',
  HALF_OPEN = 'half-open'
}

Creating a Circuit

Basic Configuration

import { TealCircuit } from '@tealtiger/sdk';

const circuit = new TealCircuit({
  failureThreshold: 5,
  timeout: 60000, // 60 seconds
  halfOpenRequests: 3
});

With State Change Callback

const circuit = new TealCircuit({
  failureThreshold: 5,
  timeout: 60000,
  onStateChange: (newState, oldState) => {
    console.log(`Circuit state changed: ${oldState}${newState}`);
  }
});

Executing Operations

Basic Execution

try {
  const result = await circuit.execute(async () => {
    return await externalService.call();
  });
  
  console.log('Operation succeeded:', result);
} catch (error) {
  if (error instanceof CircuitOpenError) {
    console.error('Circuit is open, service unavailable');
  } else {
    console.error('Operation failed:', error);
  }
}

With Retry Logic

async function callWithRetry(maxRetries: number = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await circuit.execute(async () => {
        return await externalService.call();
      });
    } catch (error) {
      if (error instanceof CircuitOpenError) {
        // Circuit is open, don't retry
        throw error;
      }
      
      if (i === maxRetries - 1) {
        throw error;
      }
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

Evaluating Circuit State

Check State Before Operation

import { ExecutionContext, DecisionAction } from '@tealtiger/sdk';

const context = ExecutionContext.create({
  actor: { id: 'service-a', type: 'service' }
});

const decision = circuit.evaluate(context);

if (decision.action === DecisionAction.DENY) {
  console.error('Circuit is open, service unavailable');
  console.log(`Risk score: ${decision.risk_score}`);
} else {
  // Proceed with operation
  await circuit.execute(async () => {
    return await externalService.call();
  });
}

Monitor Circuit State

const decision = circuit.evaluate(context);

console.log(`Circuit state: ${decision.metadata.circuit_state}`);
console.log(`Failures: ${decision.metadata.failures}`);
console.log(`Last failure: ${decision.metadata.last_failure_time}`);

Circuit States

CLOSED State

// Normal operation, requests pass through
const decision = circuit.evaluate(context);

console.log(decision.action); // ALLOW
console.log(decision.reason_codes); // [ReasonCode.POLICY_COMPLIANT]
console.log(decision.risk_score); // 0

OPEN State

// Circuit is tripped, requests fail immediately
const decision = circuit.evaluate(context);

console.log(decision.action); // DENY
console.log(decision.reason_codes); // [ReasonCode.CIRCUIT_OPEN]
console.log(decision.risk_score); // 100

HALF-OPEN State

// Testing if service has recovered
const decision = circuit.evaluate(context);

console.log(decision.action); // ALLOW
console.log(decision.reason_codes); // [ReasonCode.CIRCUIT_HALF_OPEN]
console.log(decision.risk_score); // 50

Manual Control

Reset Circuit

// Reset to CLOSED state
circuit.reset();

console.log(circuit.getState()); // CircuitState.CLOSED

Force Open

// Force circuit to OPEN state
circuit.forceOpen();

console.log(circuit.getState()); // CircuitState.OPEN

Force Close

// Force circuit to CLOSED state
circuit.forceClose();

console.log(circuit.getState()); // CircuitState.CLOSED

Integration with TealAudit

import { TealCircuit, TealAudit } from '@tealtiger/sdk';

const circuit = new TealCircuit({
  failureThreshold: 5,
  timeout: 60000,
  onStateChange: async (newState, oldState) => {
    await audit.log('circuit_state_change', {
      old_state: oldState,
      new_state: newState,
      timestamp: new Date().toISOString()
    });
  }
});

Error Handling

try {
  const result = await circuit.execute(async () => {
    return await externalService.call();
  });
} catch (error) {
  if (error instanceof CircuitOpenError) {
    // Circuit is open
    logger.error('Service unavailable (circuit open)', {
      circuit_state: circuit.getState(),
      error: error.message
    });
    
    // Return fallback response
    return fallbackResponse;
  } else {
    // Other error
    logger.error('Operation failed', { error });
    throw error;
  }
}

Performance

TealCircuit targets:
  • < 1ms overhead per operation (p99)
  • < 0.5ms for evaluate() (p99)
  • Thread-safe for concurrent operations

Best Practices

Use Appropriate Thresholds

// ✅ Good: Reasonable thresholds
const circuit = new TealCircuit({
  failureThreshold: 5, // Trip after 5 consecutive failures
  timeout: 60000, // Wait 60s before attempting recovery
  halfOpenRequests: 3 // Require 3 successes to close
});

Provide Fallback Responses

async function callWithFallback() {
  try {
    return await circuit.execute(async () => {
      return await externalService.call();
    });
  } catch (error) {
    if (error instanceof CircuitOpenError) {
      // Return cached or default response
      return getCachedResponse();
    }
    throw error;
  }
}

Monitor Circuit Health

// Periodic health check
setInterval(() => {
  const decision = circuit.evaluate(context);
  
  metrics.gauge('circuit.risk_score', decision.risk_score);
  metrics.gauge('circuit.failures', decision.metadata.failures);
  
  if (decision.action === DecisionAction.DENY) {
    alerts.trigger('circuit_open', {
      circuit_state: decision.metadata.circuit_state
    });
  }
}, 10000); // Every 10 seconds