All files / server/services compile-gatekeeper.ts

90% Statements 18/20
87.5% Branches 7/8
100% Functions 6/6
90% Lines 18/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99                      22x         22x     22x   22x           22x     22x                     42x 42x                                   195x 195x             1x 1x 1x                         1x 1x             31x     111x 111x      
/**
 * Compile Gatekeeper - Backward Compatibility Layer
 * 
 * Delegates to UnifiedGatekeeper for centralized concurrency management.
 * This maintains backward compatibility while using the new unified system internally.
 */
 
import { Logger } from "@shared/logger";
import { getUnifiedGatekeeper, TaskPriority } from "./unified-gatekeeper";
 
class CompileGatekeeper {
  private readonly logger = new Logger("CompileGatekeeper");
  private readonly maxConcurrent: number;
 
  constructor(maxConcurrent?: number) {
    // In worker threads, disable gatekeeper since the worker pool controls concurrency
    const isWorkerThread = process.env.COMPILE_GATEKEEPER_DISABLED === "true";
    
    // Allow explicit bypass via env var (for E2E test debugging)
    const isDisabledViaEnv = process.env.DISABLE_COMPILE_GATEKEEPER === "true";
    
    Iif (isWorkerThread || isDisabledViaEnv) {
      this.maxConcurrent = Infinity;
      this.logger.debug(
        `CompileGatekeeper disabled - gatekeeper passes through immediately`,
      );
    } else {
      this.maxConcurrent =
        maxConcurrent || Number.parseInt(process.env.COMPILE_MAX_CONCURRENT || "4", 10);
 
      this.logger.info(
        `CompileGatekeeper initialized with max ${this.maxConcurrent} concurrent compiles`,
      );
    }
  }
 
  /**
   * Acquire a compile slot with HIGH priority (for interactive simulations)
   * Ensures user-initiated actions get prompt access
   */
  async acquireHighPriority(): Promise<() => void> {
    const unified = getUnifiedGatekeeper(this.maxConcurrent);
    return await unified.acquireCompileSlotHighPriority("simulation-start");
  }
 
  /**
   * Acquire a compile slot with NORMAL priority (backward compatibility)
   * If no slots available, this returns a Promise that resolves when a slot becomes free.
   * 
   * Usage:
   * ```typescript
   * const release = await gatekeeper.acquire();
   * try {
   *   await compiler.compile(code);
   * } finally {
   *   release();
   * }
   * ```
   */
  async acquire(): Promise<() => void> {
    const unified = getUnifiedGatekeeper(this.maxConcurrent);
    return await unified.acquireCompileSlot(TaskPriority.NORMAL, 30000, "compile-gatekeeper");
  }
 
  /**
   * Get current gatekeeper statistics for monitoring/debugging
   */
  getStats() {
    const unified = getUnifiedGatekeeper(this.maxConcurrent);
    const unifiedStats = unified.getStats();
    return {
      maxConcurrent: unifiedStats.maxConcurrentCompiles,
      available: unifiedStats.availableSlots,
      active: unifiedStats.activeCompiles,
      queued: unifiedStats.queuedCompiles,
    };
  }
 
  /**
   * Gracefully drain the queue and wait for all active compiles to finish.
   * Useful for shutdown scenarios.
   */
  async drain(): Promise<void> {
    const unified = getUnifiedGatekeeper(this.maxConcurrent);
    await unified.drain();
  }
}
 
/**
 * Global singleton instance
 */
let gatekeeperInstance: CompileGatekeeper | null = null;
 
export function getCompileGatekeeper(maxConcurrent?: number): CompileGatekeeper {
  gatekeeperInstance ??= new CompileGatekeeper(maxConcurrent);
  return gatekeeperInstance;
}