UNPKG

@ahmedhegazee/nestjs-telescope

Version:

Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling

243 lines 8.97 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var BufferedEntryManagerService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.BufferedEntryManagerService = void 0; const common_1 = require("@nestjs/common"); let BufferedEntryManagerService = BufferedEntryManagerService_1 = class BufferedEntryManagerService { constructor(config = {}, flushHandler) { this.flushHandler = flushHandler; this.logger = new common_1.Logger(BufferedEntryManagerService_1.name); this.buffer = []; this.flushTimer = null; this.isFlushInProgress = false; this.config = { enabled: true, maxBufferSize: 1000, flushInterval: 5000, maxBatchSize: 100, priorityFlushThreshold: 50, retryAttempts: 3, retryDelay: 1000, ...config, }; this.stats = { currentBufferSize: 0, totalEntries: 0, totalFlushes: 0, totalFailures: 0, lastFlushTime: 0, averageFlushSize: 0, bufferUtilization: 0, }; if (this.config.enabled) { this.startFlushTimer(); } this.logger.debug('Buffered Entry Manager initialized', this.config); } async addEntry(entry, priority = 1) { if (!this.config.enabled) { try { await this.flushHandler([entry]); } catch (error) { this.logger.error('Failed to flush entry immediately:', error); throw error; } return; } try { const bufferedEntry = { entry, timestamp: Date.now(), priority, retryCount: 0, }; this.buffer.push(bufferedEntry); this.stats.totalEntries++; this.stats.currentBufferSize = this.buffer.length; this.updateBufferUtilization(); if (this.shouldFlushImmediately()) { await this.flushBuffer(); } } catch (error) { this.logger.error('Failed to add entry to buffer:', error); throw error; } } async addBatch(entries, priority = 1) { if (!this.config.enabled) { try { await this.flushHandler(entries); } catch (error) { this.logger.error('Failed to flush batch immediately:', error); throw error; } return; } try { const timestamp = Date.now(); const bufferedEntries = entries.map((entry) => ({ entry, timestamp, priority, retryCount: 0, })); this.buffer.push(...bufferedEntries); this.stats.totalEntries += entries.length; this.stats.currentBufferSize = this.buffer.length; this.updateBufferUtilization(); if (this.shouldFlushImmediately()) { await this.flushBuffer(); } } catch (error) { this.logger.error('Failed to add batch to buffer:', error); throw error; } } async flush() { await this.flushBuffer(); } getStats() { return { ...this.stats }; } getBufferSize() { return this.buffer.length; } isHealthy() { return this.stats.bufferUtilization < 0.9 && !this.isFlushInProgress; } clearBuffer() { const clearedCount = this.buffer.length; this.buffer.splice(0); this.stats.currentBufferSize = 0; this.updateBufferUtilization(); this.logger.warn(`Buffer cleared: ${clearedCount} entries removed`); } onDestroy() { this.shutdown(); } async shutdown() { if (this.flushTimer) { clearInterval(this.flushTimer); this.flushTimer = null; } if (this.buffer.length > 0) { this.logger.debug(`Flushing ${this.buffer.length} remaining entries on shutdown`); await this.flushBuffer(); } this.logger.debug('Buffered Entry Manager shutdown complete'); } shouldFlushImmediately() { if (this.buffer.length >= this.config.maxBufferSize) { return true; } const highPriorityCount = this.buffer.filter((entry) => entry.priority >= 5).length; if (highPriorityCount >= this.config.priorityFlushThreshold) { return true; } const hasCriticalEntries = this.buffer.some((entry) => entry.priority >= 8); if (hasCriticalEntries) { return true; } return false; } startFlushTimer() { this.flushTimer = setInterval(async () => { if (this.buffer.length > 0 && !this.isFlushInProgress) { await this.flushBuffer(); } }, this.config.flushInterval); } async flushBuffer() { if (this.isFlushInProgress || this.buffer.length === 0) { return; } this.isFlushInProgress = true; const startTime = Date.now(); try { this.buffer.sort((a, b) => { if (a.priority !== b.priority) { return b.priority - a.priority; } return a.timestamp - b.timestamp; }); while (this.buffer.length > 0) { const batchSize = Math.min(this.config.maxBatchSize, this.buffer.length); const batch = this.buffer.splice(0, batchSize); await this.processBatch(batch); } this.stats.totalFlushes++; this.stats.lastFlushTime = Date.now(); this.stats.currentBufferSize = this.buffer.length; this.updateBufferUtilization(); this.updateAverageFlushSize(); const duration = Date.now() - startTime; this.logger.debug(`Buffer flushed successfully in ${duration}ms`); } catch (error) { this.logger.error('Buffer flush failed:', error); this.stats.totalFailures++; } finally { this.isFlushInProgress = false; } } async processBatch(batch) { const entries = batch.map((bufferedEntry) => bufferedEntry.entry); try { await this.flushHandler(entries); } catch (error) { await this.handleFailedBatch(batch, error); } } async handleFailedBatch(batch, error) { this.logger.warn(`Batch flush failed, attempting retry:`, error.message); const retryableBatch = []; for (const bufferedEntry of batch) { if (bufferedEntry.retryCount < this.config.retryAttempts) { bufferedEntry.retryCount++; retryableBatch.push(bufferedEntry); } else { this.logger.error(`Entry failed after ${this.config.retryAttempts} attempts, dropping:`, { entryId: bufferedEntry.entry.id, type: bufferedEntry.entry.type, }); } } if (retryableBatch.length > 0) { await this.sleep(this.config.retryDelay * retryableBatch[0].retryCount); this.buffer.unshift(...retryableBatch); } } updateBufferUtilization() { this.stats.bufferUtilization = this.buffer.length / this.config.maxBufferSize; } updateAverageFlushSize() { if (this.stats.totalFlushes > 0) { this.stats.averageFlushSize = this.stats.totalEntries / this.stats.totalFlushes; } } sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } }; exports.BufferedEntryManagerService = BufferedEntryManagerService; exports.BufferedEntryManagerService = BufferedEntryManagerService = BufferedEntryManagerService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object, Function]) ], BufferedEntryManagerService); //# sourceMappingURL=buffered-entry-manager.service.js.map