/** * GossipRetryService - Manages retry logic for unacknowledged message deliveries. * * Tracks pending deliveries, handles acknowledgments, and retries unacknowledged * messages with exponential backoff. When max retries are exhausted, marks * unacknowledged recipients as Failed and emits MESSAGE_FAILED events. * * @see Requirements 5.1, 5.2, 5.3, 5.4, 5.5 */ import { DeliveryStatus } from '../../enumerations/messaging/deliveryStatus'; import { DeliveryAckMetadata, IGossipService, MessageDeliveryMetadata } from '../../interfaces/availability/gossipService'; import type { BlockId } from '../../interfaces/branded/primitives/blockId'; import { IMessageMetadata } from '../../interfaces/messaging/messageMetadata'; /** * Minimal event emitter interface for message events. * * This decouples the retry service from the WebSocket-dependent * EventNotificationSystem in brightchain-api-lib. */ export interface IMessageEventEmitter { emit(type: 'message:stored' | 'message:received' | 'message:delivered' | 'message:failed', metadata: IMessageMetadata): void; } /** * Minimal interface for updating delivery status in the metadata store. * * This decouples the retry service from the full IMessageMetadataStore * which uses the DeliveryStatus enum. */ export interface IDeliveryStatusStore { updateDeliveryStatus(messageId: string, recipientId: string, status: DeliveryStatus): Promise; } /** * Configuration for retry behavior. * * @see Requirements 5.1, 5.2, 5.3 */ export interface RetryConfig { /** Initial timeout before first retry in milliseconds (default: 30000) */ initialTimeoutMs: number; /** Multiplier for exponential backoff (default: 2) */ backoffMultiplier: number; /** Maximum number of retries before marking as failed (default: 5) */ maxRetries: number; /** Maximum backoff delay in milliseconds (default: 240000) */ maxBackoffMs: number; } /** * Tracks the state of a pending message delivery including per-recipient status. */ export interface PendingDelivery { /** The message ID being tracked */ messageId: string; /** Block IDs containing the message content */ blockIds: BlockId[]; /** The original delivery metadata for re-announcement */ metadata: MessageDeliveryMetadata; /** Per-recipient delivery status */ recipientStatuses: Map; /** Number of retry attempts made */ retryCount: number; /** When the next retry should occur */ nextRetryAt: Date; /** When this delivery was first tracked */ createdAt: Date; } /** * Service that manages retry logic for gossip-based message delivery. * * Tracks pending deliveries, processes acknowledgments, and automatically * retries unacknowledged messages with exponential backoff. * * @example * ```typescript * const retryService = new GossipRetryService(gossipService, metadataStore, eventSystem); * retryService.start(); * * // Track a new delivery * retryService.trackDelivery(messageId, blockIds, metadata); * * // Handle incoming acks * retryService.handleAck(ackMetadata); * * // Stop when done * retryService.stop(); * ``` * * @see Requirements 5.1, 5.2, 5.3, 5.4, 5.5 */ export declare class GossipRetryService { private readonly gossipService; private readonly metadataStore; private readonly eventSystem; private readonly config; private readonly pendingDeliveries; private retryTimer; constructor(gossipService: IGossipService, metadataStore: IDeliveryStatusStore, eventSystem: IMessageEventEmitter, config?: Partial); /** * Register a new pending delivery for tracking. * * Sets all recipients to Announced status and calculates the first retry time * based on the configured initial timeout. * * @param messageId - The message ID to track * @param blockIds - Block IDs containing the message content * @param metadata - The delivery metadata for re-announcement * * @see Requirement 5.1 */ trackDelivery(messageId: string, blockIds: BlockId[], metadata: MessageDeliveryMetadata): void; /** * Handle a delivery acknowledgment from a recipient. * * Updates the recipient's delivery status. If all recipients have been * delivered (or read), emits MESSAGE_DELIVERED and removes from tracking. * * @param ack - The delivery acknowledgment metadata * * @see Requirements 4.4, 4.5 */ handleAck(ack: DeliveryAckMetadata): void; /** * Start the periodic retry timer. * * The timer checks for pending deliveries that need retry at regular intervals. * * @see Requirement 5.1 */ start(): void; /** * Stop the periodic retry timer. */ stop(): void; /** * Check all pending deliveries and retry or fail as needed. * * This method is public to allow direct invocation in tests without * relying on real timers. * * @see Requirements 5.1, 5.2, 5.4, 5.5 */ checkRetries(): void; /** * Get a pending delivery by message ID (for testing/inspection). */ getPendingDelivery(messageId: string): PendingDelivery | undefined; /** * Get the count of pending deliveries (for testing/inspection). */ getPendingCount(): number; /** * Get the current retry configuration. */ getConfig(): RetryConfig; /** * Calculate the backoff delay for a given retry attempt. * * Uses exponential backoff: initialTimeout * backoffMultiplier^(retryCount-1), * capped at maxBackoffMs. * * Retry schedule with defaults: * - Retry 1: 30s * - Retry 2: 60s * - Retry 3: 120s * - Retry 4: 240s * - Retry 5: 240s (capped) * * @param retryCount - The 1-indexed retry attempt number * @returns The backoff delay in milliseconds * * @see Requirement 5.2 */ private calculateBackoff; /** * Handle the case when max retries are exhausted for a delivery. * * Marks all unacknowledged recipients as Failed and emits MESSAGE_FAILED. * * @see Requirements 5.4, 5.5 */ private handleMaxRetriesExhausted; /** * Check if all recipients have been delivered or read. */ private isFullyDelivered; /** * Map an ack status string to a DeliveryStatus enum value. */ private mapAckStatusToDeliveryStatus; /** * Emit a MESSAGE_DELIVERED event for a fully delivered message. */ private emitMessageDelivered; /** * Emit a MESSAGE_FAILED event for a message that exhausted retries. */ private emitMessageFailed; /** * Create a minimal IMessageMetadata object for event emission. * * Since the retry service doesn't have full message metadata, * we construct a minimal version with the information we have. */ private createMessageMetadataForEvent; } //# sourceMappingURL=gossipRetryService.d.ts.map