import type { QueuePlugin } from "../interfaces/plugin.ts"; /** * Simple logger interface for ECS Protection Manager. */ export interface EcsProtectionLogger { log: (message: string) => void; warn: (message: string) => void; error: (message: string, error?: unknown) => void; } /** * Configuration options for ECS Protection Manager. */ export interface EcsProtectionManagerOptions { /** * Custom fetch function for HTTP requests (useful for testing). * Defaults to global fetch if not provided. */ fetch?: typeof fetch; /** * Custom ECS Agent URI override. * Defaults to process.env.ECS_AGENT_URI if not provided. */ ecsAgentUri?: string; /** * Custom logger for ECS protection events. * Defaults to console if not provided. */ logger?: EcsProtectionLogger; } /** * Manages ECS Task Protection state for the container. * Handles communication with the ECS agent for acquiring and releasing protection. * * **IMPORTANT: Only create ONE instance per application/container.** * * ECS Task Protection is a container-level feature. Multiple manager instances would: * - Compete for protection control (causing acquisition/release conflicts) * - Have inconsistent draining state * - Result in protection conflicts between queues * * The manager itself is stateless regarding job tracking - each plugin instance * maintains its own reference count. The manager only tracks: * - Whether protection is currently acquired (`protected`) * - Whether ECS is draining the task (`draining`) * * Create a single instance and share it across all queues in your application: * * ```typescript * // ✅ CORRECT: Single instance for the entire application * const protectionManager = new EcsProtectionManager(); * * const emailQueue = new FileQueue({ * plugins: [ecsTaskProtection(protectionManager)] * }); * * const imageQueue = new SqsQueue({ * plugins: [ecsTaskProtection(protectionManager)] // Same instance * }); * * // ❌ WRONG: Multiple instances will conflict * const emailProtection = new EcsProtectionManager(); * const imageProtection = new EcsProtectionManager(); // Don't do this! * ``` * * For testing, you can create separate instances for different test cases * since each test runs in isolation. */ export declare class EcsProtectionManager { private protected; private draining; private mutex; private fetchFn; private agentUri; logger: EcsProtectionLogger; constructor(options?: EcsProtectionManagerOptions); /** * Called before polling for jobs. * Tries to acquire protection before getting a job. * Returns true if protection is acquired or not needed, false if draining. */ attemptProtect(ttrSeconds: number): Promise; /** * Returns true if ECS is draining the task and no new jobs should be processed. */ isDraining(): boolean; /** * Manually mark the task as draining (for testing or external triggers). */ markDraining(): void; /** * Called when a job completes processing. * Decrements active job counter and releases protection. */ attemptRelease(): Promise; /** * Acquire task protection from ECS agent. * Returns true if successful, false if ECS is draining. */ private acquire; /** * Release task protection from ECS agent. */ private release; /** * Cleanup method for graceful shutdown. */ cleanup(): Promise; } /** * ECS Task Protection plugin factory. * * This plugin prevents job loss during ECS container termination by: * 1. Acquiring task protection before polling for jobs * 2. Maintaining protection while any jobs are active (reference counting) * 3. Extending protection automatically for long-running jobs based on TTR * 4. Releasing protection only when all jobs complete * 5. Detecting when ECS is draining and stopping new job processing * * Users must provide an EcsProtectionManager instance. **Use the same instance * across all queues in your application** to ensure proper coordination. * * Benefits of explicit instantiation: * - Clear control over the protection manager lifecycle * - Easier testing with dedicated instances per test * - No hidden global state * * @param manager The EcsProtectionManager instance to use * @returns QueuePlugin instance * * @example Basic usage: * ```typescript * import { FileQueue } from 'adapter-queue'; * import { EcsProtectionManager, ecsTaskProtection } from 'adapter-queue/plugins/ecs-protection-manager'; * * // Create protection manager (can be shared across multiple queues) * const protectionManager = new EcsProtectionManager(); * * const queue = new FileQueue({ * name: 'my-queue', * path: './queue', * plugins: [ecsTaskProtection({ manager: protectionManager })] * }); * * await queue.run(true, 3); * ``` * * @example With custom logger: * ```typescript * import pino from 'pino'; * * const logger = pino(); * const protectionManager = new EcsProtectionManager({ * logger: { * log: (message) => logger.info(message), * warn: (message) => logger.warn(message), * error: (message, error) => logger.error({ error }, message) * } * }); * ``` * * @example Multiple queues sharing the same protection manager: * ```typescript * const protectionManager = new EcsProtectionManager(); * * // Each plugin instance tracks its own active jobs * const emailPlugin = ecsTaskProtection({ manager: protectionManager }); * const imagePlugin = ecsTaskProtection({ * manager: protectionManager, * defaultProtectionTimeout: 900 // 15 minutes for longer jobs * }); * * const emailQueue = new FileQueue({ * name: 'email-queue', * path: './email-queue', * plugins: [emailPlugin] * }); * * const imageQueue = new FileQueue({ * name: 'image-queue', * path: './image-queue', * plugins: [imagePlugin] * }); * * // Each queue's plugin tracks its own jobs independently * // Protection is released only when ALL jobs across ALL queues complete * await Promise.all([ * emailQueue.run(true, 3), * imageQueue.run(true, 3) * ]); * ``` */ export declare function ecsTaskProtection(opts: { manager: EcsProtectionManager; defaultProtectionTimeout?: number; }): QueuePlugin;