koatty_serve
Version:
Provide http1/2/3, websocket, gRPC server for Koatty.
1,819 lines (1,813 loc) • 327 kB
JavaScript
import { Server, ServerCredentials } from '@grpc/grpc-js';
import { DefaultLogger } from 'koatty_logger';
import { performance } from 'perf_hooks';
import { randomUUID, randomBytes } from 'crypto';
import { Helper } from 'koatty_lib';
import fs, { readFileSync, existsSync } from 'fs';
import * as path from 'path';
import { Socket } from 'net';
import { TLSSocket } from 'tls';
import { constants, createSecureServer } from 'http2';
import * as WS from 'ws';
import { createServer } from 'http';
import { EventEmitter } from 'events';
import { createServer as createServer$1 } from 'https';
import { OnEvent, AppEvent, Component, KoattyApplication } from 'koatty_core';
/*!
* @Author: richen
* @Date: 2026-04-24 08:20:32
* @License: BSD (3-Clause)
* @Copyright (c) - <richenlin(at)gmail.com>
* @HomePage: https://koatty.org/
*/
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
function deepEqual(obj1, obj2, visited = /* @__PURE__ */ new WeakSet()) {
if (obj1 === obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (typeof obj1 !== typeof obj2) return false;
if (typeof obj1 === "object") {
if (visited.has(obj1)) return true;
visited.add(obj1);
const isArray1 = Array.isArray(obj1);
const isArray2 = Array.isArray(obj2);
if (isArray1 !== isArray2) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
const result = keys1.every((key) => deepEqual(obj1[key], obj2[key], visited));
visited.delete(obj1);
return result;
}
return false;
}
__name(deepEqual, "deepEqual");
function generateShortId() {
try {
return randomUUID().replace(/-/g, "").substring(0, 12);
} catch {
const bytes = randomBytes(16);
bytes[6] = bytes[6] & 15 | 64;
bytes[8] = bytes[8] & 63 | 128;
return bytes.toString("hex").substring(0, 12);
}
}
__name(generateShortId, "generateShortId");
function generateTraceId() {
return `trace_${generateShortId()}`;
}
__name(generateTraceId, "generateTraceId");
function generateServerId(protocol) {
return `${protocol}_server_${generateShortId()}`;
}
__name(generateServerId, "generateServerId");
// src/utils/logger.ts
var StructuredLogger = class _StructuredLogger {
static {
__name(this, "StructuredLogger");
}
static instance;
globalContext = {};
constructor() {
}
static getInstance() {
if (!_StructuredLogger.instance) {
_StructuredLogger.instance = new _StructuredLogger();
}
return _StructuredLogger.instance;
}
/**
* Set global context for all logs
* @param context Global context to merge with all log entries
*/
setGlobalContext(context) {
this.globalContext = {
...this.globalContext,
...context
};
}
/**
* Get current global context
*/
getGlobalContext() {
return {
...this.globalContext
};
}
/**
* Clear global context
*/
clearGlobalContext() {
this.globalContext = {};
}
/**
* Format log message with context
* @param message Log message
* @param context Additional context
* @param data Additional data to log
* @returns Formatted message string
*/
formatMessage(message, context, data) {
const mergedContext = context ? {
...this.globalContext,
...context
} : this.globalContext;
const parts = [];
const moduleUpper = mergedContext.module?.toUpperCase();
const protocolUpper = mergedContext.protocol?.toUpperCase();
if (moduleUpper && protocolUpper && moduleUpper === protocolUpper) {
parts.push(`[${protocolUpper}]`);
} else {
if (mergedContext.module && moduleUpper !== "KOATTYSERVER") {
parts.push(`[${moduleUpper}]`);
}
if (mergedContext.protocol) {
parts.push(`[${protocolUpper}]`);
}
}
if (mergedContext.connectionId) {
parts.push(`[conn:${mergedContext.connectionId}]`);
}
if (mergedContext.requestId) {
parts.push(`[req:${mergedContext.requestId}]`);
}
if (mergedContext.traceId) {
parts.push(`[trace:${mergedContext.traceId}]`);
}
let finalMessage = parts.length > 0 ? `${parts.join(" ")} ${message}` : message;
if (data) {
const contextKeys = Object.keys(mergedContext);
const additionalData = Object.entries(data).filter(([key]) => !contextKeys.includes(key)).reduce((acc, [key, value]) => ({
...acc,
[key]: value
}), {});
if (Object.keys(additionalData).length > 0) {
finalMessage += ` | Data: ${JSON.stringify(additionalData)}`;
}
}
return finalMessage;
}
/**
* Log debug message
* @param message Log message
* @param context Additional context
* @param data Additional data
*/
debug(message, context, data) {
const formattedMessage = this.formatMessage(message, context, data);
DefaultLogger.Debug(formattedMessage);
}
/**
* Log info message
* @param message Log message
* @param context Additional context
* @param data Additional data
*/
info(message, context, data) {
const formattedMessage = this.formatMessage(message, context, data);
DefaultLogger.Info(formattedMessage);
}
/**
* Log warning message
* @param message Log message
* @param context Additional context
* @param data Additional data
*/
warn(message, context, data) {
const formattedMessage = this.formatMessage(message, context, data);
DefaultLogger.Warn(formattedMessage);
}
/**
* Log error message
* @param message Log message
* @param context Additional context
* @param error Error object or additional data
*/
error(message, context, error) {
let errorData = error;
if (error instanceof Error) {
errorData = {
name: error.name,
message: error.message,
stack: error.stack
};
}
const formattedMessage = this.formatMessage(message, context, errorData);
DefaultLogger.Error(formattedMessage);
}
/**
* Create a child logger with merged context
* @param context Context to merge
* @returns New logger instance with merged context
*/
child(context) {
const childLogger = new _StructuredLogger();
childLogger.setGlobalContext({
...this.globalContext,
...context
});
return childLogger;
}
/**
* Start performance measurement
* @param label Performance measurement label
* @returns Performance metrics object
*/
startPerformanceMeasurement(label) {
const startTime = performance.now();
const memoryUsage = process.memoryUsage();
return {
startTime,
memoryUsage,
label
};
}
/**
* End performance measurement and log result
* @param metrics Performance metrics from startPerformanceMeasurement
* @param context Additional context
*/
endPerformanceMeasurement(metrics, context) {
const endTime = performance.now();
const duration = endTime - metrics.startTime;
const endMemoryUsage = process.memoryUsage();
const memoryDelta = {
rss: endMemoryUsage.rss - (metrics.memoryUsage?.rss || 0),
heapTotal: endMemoryUsage.heapTotal - (metrics.memoryUsage?.heapTotal || 0),
heapUsed: endMemoryUsage.heapUsed - (metrics.memoryUsage?.heapUsed || 0),
external: endMemoryUsage.external - (metrics.memoryUsage?.external || 0)
};
this.debug(`Performance: ${metrics.label || "Operation"} completed`, context, {
duration: `${duration.toFixed(2)}ms`,
memoryDelta: {
rss: `${(memoryDelta.rss / 1024 / 1024).toFixed(2)}MB`,
heapUsed: `${(memoryDelta.heapUsed / 1024 / 1024).toFixed(2)}MB`
}
});
}
/**
* Measure and log performance of async operation
* @param label Performance measurement label
* @param operation Async operation to measure
* @param context Additional context
* @returns Result of the operation
*/
async measureAsync(label, operation, context) {
const metrics = this.startPerformanceMeasurement(label);
try {
const result = await operation();
this.endPerformanceMeasurement(metrics, context);
return result;
} catch (error) {
this.endPerformanceMeasurement(metrics, context);
throw error;
}
}
/**
* Measure and log performance of sync operation
* @param label Performance measurement label
* @param operation Sync operation to measure
* @param context Additional context
* @returns Result of the operation
*/
measureSync(label, operation, context) {
const metrics = this.startPerformanceMeasurement(label);
try {
const result = operation();
this.endPerformanceMeasurement(metrics, context);
return result;
} catch (error) {
this.endPerformanceMeasurement(metrics, context);
throw error;
}
}
};
function createLogger(context) {
const logger5 = StructuredLogger.getInstance();
if (context) {
return logger5.child(context);
}
return logger5;
}
__name(createLogger, "createLogger");
StructuredLogger.getInstance();
// src/utils/timer-manager.ts
var TimerFrequency = /* @__PURE__ */ (function(TimerFrequency2) {
TimerFrequency2[TimerFrequency2["HIGH"] = 5e3] = "HIGH";
TimerFrequency2[TimerFrequency2["MEDIUM"] = 3e4] = "MEDIUM";
TimerFrequency2[TimerFrequency2["LOW"] = 6e4] = "LOW";
return TimerFrequency2;
})({});
var TimerManager = class _TimerManager {
static {
__name(this, "TimerManager");
}
timers = /* @__PURE__ */ new Map();
timerIdCounter = 0;
logger = createLogger({
module: "timer_manager"
});
// Phase 3 优化功能
optimizerConfig;
taskQueues = /* @__PURE__ */ new Map();
consolidatedTimers = /* @__PURE__ */ new Map();
performanceMetrics = {
totalTasks: 0,
executedTasks: 0,
averageExecutionTime: 0,
lastOptimization: Date.now()
};
constructor(config = {}) {
this.optimizerConfig = {
enableConsolidation: true,
enableAdaptiveFrequency: true,
maxTimersPerFrequency: 10,
loadThreshold: 0.7,
...config
};
Object.values(TimerFrequency).forEach((frequency) => {
if (typeof frequency === "number") {
this.taskQueues.set(frequency, []);
}
});
this.logger.debug("Timer manager initialized with optimization", {}, {
config: this.optimizerConfig,
supportedFrequencies: Object.values(TimerFrequency).filter((f) => typeof f === "number")
});
}
/**
* 添加定时器 (传统方式)
* @param name 定时器名称
* @param callback 回调函数
* @param interval 间隔时间(毫秒)
* @returns 定时器ID
*/
/**
* 添加定时器 - 统一使用优化模式
* @param name 定时器名称
* @param callback 回调函数
* @param interval 间隔时间(毫秒)
* @returns 定时器ID
*/
addTimer(name, callback, interval) {
return this.addOptimizedTimer({
name,
callback,
interval,
priority: this.determinePriority(interval),
protocol: this.extractProtocol(name)
});
}
/**
* 添加优化定时器 - Phase 3
* @param task 定时器任务
* @returns 任务ID
*/
addOptimizedTimer(task) {
const taskId = `${task.name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const optimizedTask = {
...task,
id: taskId,
lastExecuted: 0,
executionCount: 0
};
const frequency = this.selectOptimalFrequency(task.interval);
const taskQueue = this.taskQueues.get(frequency);
if (taskQueue) {
taskQueue.push(optimizedTask);
this.performanceMetrics.totalTasks++;
this.optimizeTimers();
}
return taskId;
}
/**
* 创建物理定时器 - 用于合并定时器的底层实现
*/
createPhysicalTimer(name, callback, interval) {
const timerId = `${name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const wrappedCallback = /* @__PURE__ */ __name(() => {
try {
callback();
} catch (error) {
this.logger.error("Timer callback error", {
name,
timerId,
error: error instanceof Error ? error.message : String(error)
});
}
}, "wrappedCallback");
const timer = setInterval(wrappedCallback, interval);
if (typeof timer.unref === "function") {
timer.unref();
}
const timerInfo = {
id: timerId,
name,
interval,
callback: wrappedCallback,
timer,
createdAt: Date.now(),
lastExecuted: Date.now()
};
this.timers.set(timerId, timerInfo);
return timerId;
}
/**
* 清理指定定时器 - 支持逻辑定时器清理
* @param timerIdOrName 定时器ID或名称
* @returns 是否成功清理
*/
clearTimer(timerIdOrName) {
for (const [_frequency, tasks] of this.taskQueues) {
const taskIndex = tasks.findIndex((task) => task.id === timerIdOrName || task.name === timerIdOrName);
if (taskIndex !== -1) {
this.performanceMetrics.totalTasks--;
this.optimizeTimers();
return true;
}
}
const timerInfo = this.timers.get(timerIdOrName);
if (timerInfo) {
clearInterval(timerInfo.timer);
this.timers.delete(timerIdOrName);
return true;
}
this.logger.warn(`Timer '${timerIdOrName}' not found for clearing`);
return false;
}
/**
* 清理所有定时器 - 支持逻辑和物理定时器清理
*/
clearAllTimers() {
const logicalTimerCount = this.performanceMetrics.totalTasks;
const physicalTimerCount = this.timers.size;
this.logger.debug(`Clearing all timers`, {}, {
logicalTimers: logicalTimerCount,
physicalTimers: physicalTimerCount
});
this.taskQueues.clear();
this.performanceMetrics.totalTasks = 0;
this.performanceMetrics.executedTasks = 0;
Object.values(TimerFrequency).forEach((frequency) => {
if (typeof frequency === "number") {
this.taskQueues.set(frequency, []);
}
});
for (const [timerId, timerInfo] of this.timers) {
try {
clearInterval(timerInfo.timer);
} catch (error) {
this.logger.error(`Error clearing timer '${timerId}':`, {}, error);
}
}
this.timers.clear();
this.consolidatedTimers.clear();
this.logger.debug(`All timers cleared successfully`, {}, {
clearedLogicalTimers: logicalTimerCount,
clearedPhysicalTimers: physicalTimerCount
});
}
/**
* 获取活跃定时器数量 - 返回逻辑定时器数量
*/
getActiveTimerCount() {
return this.performanceMetrics.totalTasks;
}
/**
* 获取所有定时器名称 - 返回逻辑定时器名称
*/
getTimerNames() {
const names = [];
for (const tasks of this.taskQueues.values()) {
names.push(...tasks.map((task) => task.name));
}
return names;
}
/**
* 获取定时器详细信息
*/
getTimerInfo(timerId) {
return this.timers.get(timerId);
}
/**
* 获取所有定时器统计信息 - 返回逻辑定时器统计
*/
getTimerStats() {
const now = Date.now();
const timers = [];
for (const tasks of this.taskQueues.values()) {
for (const task of tasks) {
timers.push({
id: task.id,
name: task.name,
interval: task.interval,
uptime: now - (task.lastExecuted || now),
lastExecuted: task.lastExecuted,
priority: task.priority,
executionCount: task.executionCount || 0
});
}
}
return {
totalTimers: this.performanceMetrics.totalTasks,
timers
};
}
/**
* 检查是否存在指定定时器 - 支持逻辑和物理定时器
*/
hasTimer(timerId) {
for (const tasks of this.taskQueues.values()) {
if (tasks.some((task) => task.id === timerId)) {
return true;
}
}
return this.timers.has(timerId);
}
/**
* 确定定时器优先级 - Phase 3 优化
*/
determinePriority(interval) {
if (interval <= 5e3) return "high";
if (interval <= 3e4) return "medium";
return "low";
}
/**
* 从定时器名称提取协议 - Phase 3 优化
*/
extractProtocol(name) {
const protocolMatch = name.match(/^(http|https|http2|grpc|websocket|ws)/i);
return protocolMatch ? protocolMatch[1].toLowerCase() : void 0;
}
/**
* 选择最佳频率 - Phase 3 优化
*/
selectOptimalFrequency(interval) {
const frequencies = [
5e3,
3e4,
6e4
];
for (const freq of frequencies) {
if (interval <= freq) {
return freq;
}
}
return 6e4;
}
/**
* 优化定时器 - Phase 3 核心优化逻辑
*/
optimizeTimers() {
this.clearConsolidatedTimers();
for (const [frequency, tasks] of this.taskQueues) {
if (tasks.length > 0) {
this.createConsolidatedTimer(frequency, tasks);
}
}
this.performanceMetrics.lastOptimization = Date.now();
}
/**
* 创建合并定时器 - Phase 3 优化
*/
createConsolidatedTimer(frequency, tasks) {
const timerName = `consolidated_${frequency}ms`;
const timerId = this.createPhysicalTimer(timerName, () => {
this.executeTaskBatch(frequency, tasks);
}, frequency);
this.consolidatedTimers.set(frequency, timerId);
}
/**
* 执行任务批次 - Phase 3 优化
*/
executeTaskBatch(frequency, tasks) {
let executedCount = 0;
const sortedTasks = tasks.sort((a, b) => {
const priorityOrder = {
high: 3,
medium: 2,
low: 1
};
return priorityOrder[b.priority] - priorityOrder[a.priority];
});
for (const task of sortedTasks) {
try {
const taskStartTime = Date.now();
if (this.shouldExecuteTask(task, frequency)) {
task.callback();
task.lastExecuted = Date.now();
task.executionCount = (task.executionCount || 0) + 1;
executedCount++;
const taskExecutionTime = Date.now() - taskStartTime;
this.updateAverageExecutionTime(taskExecutionTime);
}
} catch (error) {
this.logger.error("Task execution error", {}, {
taskId: task.id,
taskName: task.name,
error: error instanceof Error ? error.message : String(error)
});
}
}
this.performanceMetrics.executedTasks += executedCount;
}
/**
* 判断是否应该执行任务(自适应频率)- Phase 3 优化
*/
shouldExecuteTask(task, frequency) {
if (!this.optimizerConfig.enableAdaptiveFrequency) {
return true;
}
if (task.priority === "high") {
return true;
}
const intervalRatio = task.interval / frequency;
if (intervalRatio <= 1) {
return true;
}
const timeSinceLastExecution = Date.now() - (task.lastExecuted || 0);
return timeSinceLastExecution >= task.interval;
}
/**
* 更新平均执行时间 - Phase 3 优化
*/
updateAverageExecutionTime(executionTime) {
const alpha = 0.1;
this.performanceMetrics.averageExecutionTime = this.performanceMetrics.averageExecutionTime * (1 - alpha) + executionTime * alpha;
}
/**
* 清理合并定时器 - Phase 3 优化
*/
clearConsolidatedTimers() {
for (const timerId of this.consolidatedTimers.values()) {
this.clearTimer(timerId);
}
this.consolidatedTimers.clear();
}
/**
* 获取优化统计信息 - Phase 3 优化
*/
getOptimizationStats() {
const tasksByFrequency = /* @__PURE__ */ new Map();
const tasksByPriority = {
high: 0,
medium: 0,
low: 0
};
for (const [frequency, tasks] of this.taskQueues) {
tasksByFrequency.set(frequency, tasks.length);
for (const task of tasks) {
tasksByPriority[task.priority]++;
}
}
return {
performance: this.performanceMetrics,
consolidation: {
activeTimers: this.consolidatedTimers.size,
totalTasks: this.performanceMetrics.totalTasks,
tasksByFrequency: Object.fromEntries(tasksByFrequency),
tasksByPriority
},
config: this.optimizerConfig
};
}
/**
* 演示Phase 3优化功能 - 创建优化版本的TimerManager实例
*/
static createOptimizedInstance() {
return new _TimerManager({
enableConsolidation: true,
enableAdaptiveFrequency: true,
maxTimersPerFrequency: 10,
loadThreshold: 0.7
});
}
/**
* 演示定时器优化效果
*/
demonstrateOptimization() {
const traditionalIntervals = [
5e3,
5e3,
3e4,
3e4,
3e4,
6e4,
6e4
];
const optimizedFrequencies = [
5e3,
3e4,
6e4
];
const reduction = ((traditionalIntervals.length - optimizedFrequencies.length) / traditionalIntervals.length * 100).toFixed(1);
return {
before: {
timerCount: traditionalIntervals.length,
intervals: traditionalIntervals
},
after: {
consolidatedTimers: optimizedFrequencies.length,
frequencies: optimizedFrequencies,
estimatedReduction: `${reduction}% timer reduction`
}
};
}
/**
* 安全销毁管理器
*/
destroy() {
this.logger.debug("TimerManager destroying", {}, {
activeTimers: this.timers.size,
totalTasks: this.performanceMetrics.totalTasks,
executedTasks: this.performanceMetrics.executedTasks,
activeConsolidatedTimers: this.consolidatedTimers.size
});
this.clearConsolidatedTimers();
this.taskQueues.clear();
this.clearAllTimers();
this.logger.debug("TimerManager destroyed successfully");
}
};
new TimerManager({
enableConsolidation: true,
enableAdaptiveFrequency: true,
maxTimersPerFrequency: 10,
loadThreshold: 0.7
});
// src/utils/ring_buffer.ts
var RingBuffer = class {
static {
__name(this, "RingBuffer");
}
buffer;
head = 0;
tail = 0;
count = 0;
capacity;
/**
* Create a ring buffer with fixed capacity
* @param capacity Maximum number of items to store
*/
constructor(capacity) {
if (capacity <= 0) {
throw new Error("Ring buffer capacity must be greater than 0");
}
this.capacity = capacity;
this.buffer = new Array(capacity);
}
/**
* Add an item to the buffer
* If buffer is full, overwrites the oldest item
* @param item Item to add
*/
push(item) {
this.buffer[this.head] = item;
this.head = (this.head + 1) % this.capacity;
if (this.count < this.capacity) {
this.count++;
} else {
this.tail = (this.tail + 1) % this.capacity;
}
}
/**
* Get all items in insertion order (oldest to newest)
* @returns Array of items
*/
toArray() {
if (this.count === 0) {
return [];
}
const result = new Array(this.count);
let index = this.tail;
for (let i = 0; i < this.count; i++) {
result[i] = this.buffer[index];
index = (index + 1) % this.capacity;
}
return result;
}
/**
* Get a sorted copy of the buffer contents
* @param compareFn Optional comparison function
* @returns Sorted array
*/
toSortedArray(compareFn) {
return this.toArray().sort(compareFn);
}
/**
* Clear all items from the buffer
*/
clear() {
this.head = 0;
this.tail = 0;
this.count = 0;
}
/**
* Get the number of items currently in the buffer
* @returns Current item count
*/
get length() {
return this.count;
}
/**
* Get the maximum capacity of the buffer
* @returns Buffer capacity
*/
get size() {
return this.capacity;
}
/**
* Check if buffer is empty
* @returns True if empty
*/
isEmpty() {
return this.count === 0;
}
/**
* Check if buffer is full
* @returns True if full
*/
isFull() {
return this.count === this.capacity;
}
/**
* Get an item at a specific index (0 = oldest, length-1 = newest)
* @param index Index to retrieve
* @returns Item at index or undefined if out of range
*/
get(index) {
if (index < 0 || index >= this.count) {
return void 0;
}
const actualIndex = (this.tail + index) % this.capacity;
return this.buffer[actualIndex];
}
/**
* Get the oldest item without removing it
* @returns Oldest item or undefined if buffer is empty
*/
peek() {
if (this.count === 0) {
return void 0;
}
return this.buffer[this.tail];
}
/**
* Get the newest item
* @returns Newest item or undefined if buffer is empty
*/
peekLast() {
if (this.count === 0) {
return void 0;
}
const lastIndex = (this.head - 1 + this.capacity) % this.capacity;
return this.buffer[lastIndex];
}
/**
* Calculate percentile from buffer contents (e.g., 0.5 for median, 0.95 for P95)
* @param percentile Percentile value between 0 and 1
* @returns Percentile value or undefined if buffer is empty
*/
getPercentile(percentile) {
if (this.count === 0 || percentile < 0 || percentile > 1) {
return void 0;
}
const sorted = this.toSortedArray((a, b) => a - b);
const index = Math.floor(sorted.length * percentile);
return sorted[Math.min(index, sorted.length - 1)];
}
/**
* Get average of numeric buffer contents
* @returns Average value or undefined if buffer is empty
*/
getAverage() {
if (this.count === 0) {
return void 0;
}
let sum = 0;
for (let i = 0; i < this.count; i++) {
const value = this.get(i);
sum += Number(value) || 0;
}
return sum / this.count;
}
/**
* Iterate over buffer contents (oldest to newest)
* @param callback Function to call for each item
*/
forEach(callback) {
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0) {
callback(item, i);
}
}
}
/**
* Map buffer contents to a new array
* @param callback Function to transform each item
* @returns New array of transformed items
*/
map(callback) {
const result = new Array(this.count);
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0) {
result[i] = callback(item, i);
}
}
return result;
}
/**
* Filter buffer contents
* @param predicate Function to test each item
* @returns New array of items that pass the test
*/
filter(predicate) {
const result = [];
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0 && predicate(item, i)) {
result.push(item);
}
}
return result;
}
/**
* Reduce buffer contents to a single value
* @param callback Reducer function
* @param initialValue Initial value for the accumulator
* @returns Reduced value
*/
reduce(callback, initialValue) {
let accumulator = initialValue;
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0) {
accumulator = callback(accumulator, item, i);
}
}
return accumulator;
}
};
var DynamicRingBuffer = class {
static {
__name(this, "DynamicRingBuffer");
}
buffer;
head = 0;
tail = 0;
count = 0;
// Capacity management
currentCapacity;
initialCapacity;
maxCapacity;
minCapacity;
// Auto-resize configuration
autoResize;
resizeThreshold;
shrinkThreshold;
resizeFactor;
// Resize tracking
lastResizeTime = 0;
resizeCooldown;
resizeCount = 0;
logger = createLogger({
module: "DynamicRingBuffer"
});
/**
* Create a dynamic ring buffer with auto-resize capability
* @param initialCapacity Starting capacity
* @param options Configuration options
*/
constructor(initialCapacity, options = {}) {
if (initialCapacity <= 0) {
throw new Error("Initial capacity must be greater than 0");
}
this.initialCapacity = initialCapacity;
this.currentCapacity = initialCapacity;
this.maxCapacity = options.maxCapacity || Math.max(initialCapacity * 10, 1e4);
this.minCapacity = options.minCapacity || Math.max(Math.floor(initialCapacity / 2), 10);
this.autoResize = options.autoResize ?? true;
this.resizeThreshold = options.resizeThreshold ?? 0.85;
this.shrinkThreshold = options.shrinkThreshold ?? 0.3;
this.resizeFactor = options.resizeFactor ?? 2;
this.resizeCooldown = options.resizeCooldown ?? 5e3;
this.buffer = new Array(this.currentCapacity);
}
/**
* Add an item to buffer with auto-resize
* If buffer is full, triggers resize or overwrites oldest item
* @param item Item to add
*/
push(item) {
this.buffer[this.head] = item;
this.head = (this.head + 1) % this.currentCapacity;
if (this.count < this.currentCapacity) {
this.count++;
} else {
if (this.autoResize && this.shouldResizeUp()) {
this.resizeUp();
} else {
this.tail = (this.tail + 1) % this.currentCapacity;
}
}
if (this.autoResize && this.shouldResizeDown()) {
this.resizeDown();
}
}
/**
* Check if buffer should expand
*/
shouldResizeUp() {
const now = Date.now();
const timeSinceLastResize = now - this.lastResizeTime;
return this.count / this.currentCapacity >= this.resizeThreshold && this.currentCapacity < this.maxCapacity && timeSinceLastResize >= this.resizeCooldown;
}
/**
* Check if buffer should shrink
*/
shouldResizeDown() {
const now = Date.now();
const timeSinceLastResize = now - this.lastResizeTime;
return this.count / this.currentCapacity <= this.shrinkThreshold && this.currentCapacity > this.minCapacity && timeSinceLastResize >= this.resizeCooldown;
}
/**
* Expand buffer capacity
*/
resizeUp() {
const newCapacity = Math.min(Math.floor(this.currentCapacity * this.resizeFactor), this.maxCapacity);
this.logger.info("Expanding ring buffer", {
oldCapacity: this.currentCapacity,
newCapacity,
itemCount: this.count
});
const newBuffer = new Array(newCapacity);
for (let i = 0; i < this.count; i++) {
newBuffer[i] = this.get(i);
}
this.buffer = newBuffer;
this.currentCapacity = newCapacity;
this.head = this.count;
this.tail = 0;
this.lastResizeTime = Date.now();
this.resizeCount++;
}
/**
* Shrink buffer capacity
*/
resizeDown() {
const newCapacity = Math.max(Math.floor(this.currentCapacity / this.resizeFactor), this.minCapacity);
this.logger.info("Shrinking ring buffer", {
oldCapacity: this.currentCapacity,
newCapacity,
itemCount: this.count
});
const newBuffer = new Array(newCapacity);
for (let i = 0; i < this.count; i++) {
newBuffer[i] = this.get(i);
}
this.buffer = newBuffer;
this.currentCapacity = newCapacity;
this.head = this.count;
this.tail = 0;
this.lastResizeTime = Date.now();
this.resizeCount++;
}
/**
* Get all items in insertion order (oldest to newest)
* @returns Array of items
*/
toArray() {
if (this.count === 0) {
return [];
}
const result = new Array(this.count);
let index = this.tail;
for (let i = 0; i < this.count; i++) {
result[i] = this.buffer[index];
index = (index + 1) % this.currentCapacity;
}
return result;
}
/**
* Get a sorted copy of buffer contents
* @param compareFn Optional comparison function
* @returns Sorted array
*/
toSortedArray(compareFn) {
return this.toArray().sort(compareFn);
}
/**
* Clear all items from buffer
*/
clear() {
this.head = 0;
this.tail = 0;
this.count = 0;
if (this.autoResize && this.currentCapacity !== this.initialCapacity) {
this.currentCapacity = this.initialCapacity;
this.buffer = new Array(this.currentCapacity);
}
}
/**
* Get number of items currently in buffer
* @returns Current item count
*/
get length() {
return this.count;
}
/**
* Get current capacity of buffer
* @returns Current capacity
*/
get size() {
return this.currentCapacity;
}
/**
* Get initial capacity
* @returns Initial capacity
*/
get initialSize() {
return this.initialCapacity;
}
/**
* Get maximum capacity
* @returns Maximum capacity
*/
get maxSize() {
return this.maxCapacity;
}
/**
* Get minimum capacity
* @returns Minimum capacity
*/
get minSize() {
return this.minCapacity;
}
/**
* Check if buffer is empty
* @returns True if empty
*/
isEmpty() {
return this.count === 0;
}
/**
* Check if buffer is full
* @returns True if full
*/
isFull() {
return this.count === this.currentCapacity;
}
/**
* Get utilization ratio (0-1)
* @returns Utilization ratio
*/
get utilization() {
return this.count / this.currentCapacity;
}
/**
* Get an item at a specific index (0 = oldest, length-1 = newest)
* @param index Index to retrieve
* @returns Item at index or undefined if out of range
*/
get(index) {
if (index < 0 || index >= this.count) {
return void 0;
}
const actualIndex = (this.tail + index) % this.currentCapacity;
return this.buffer[actualIndex];
}
/**
* Get oldest item without removing it
* @returns Oldest item or undefined if buffer is empty
*/
peek() {
if (this.count === 0) {
return void 0;
}
return this.buffer[this.tail];
}
/**
* Get newest item
* @returns Newest item or undefined if buffer is empty
*/
peekLast() {
if (this.count === 0) {
return void 0;
}
const lastIndex = (this.head - 1 + this.currentCapacity) % this.currentCapacity;
return this.buffer[lastIndex];
}
/**
* Calculate percentile from buffer contents (e.g., 0.5 for median, 0.95 for P95)
* @param percentile Percentile value between 0 and 1
* @returns Percentile value or undefined if buffer is empty
*/
getPercentile(percentile) {
if (this.count === 0 || percentile < 0 || percentile > 1) {
return void 0;
}
const sorted = this.toSortedArray((a, b) => a - b);
const index = Math.floor(sorted.length * percentile);
return sorted[Math.min(index, sorted.length - 1)];
}
/**
* Get average of numeric buffer contents
* @returns Average value or undefined if buffer is empty
*/
getAverage() {
if (this.count === 0) {
return void 0;
}
let sum = 0;
for (let i = 0; i < this.count; i++) {
const value = this.get(i);
sum += Number(value) || 0;
}
return sum / this.count;
}
/**
* Iterate over buffer contents (oldest to newest)
* @param callback Function to call for each item
*/
forEach(callback) {
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0) {
callback(item, i);
}
}
}
/**
* Map buffer contents to a new array
* @param callback Function to transform each item
* @returns New array of transformed items
*/
map(callback) {
const result = new Array(this.count);
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0) {
result[i] = callback(item, i);
}
}
return result;
}
/**
* Filter buffer contents
* @param predicate Function to test each item
* @returns New array of items that pass test
*/
filter(predicate) {
const result = [];
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0 && predicate(item, i)) {
result.push(item);
}
}
return result;
}
/**
* Reduce buffer contents to a single value
* @param callback Reducer function
* @param initialValue Initial value for accumulator
* @returns Reduced value
*/
reduce(callback, initialValue) {
let accumulator = initialValue;
for (let i = 0; i < this.count; i++) {
const item = this.get(i);
if (item !== void 0) {
accumulator = callback(accumulator, item, i);
}
}
return accumulator;
}
/**
* Manually trigger resize up
* @param factor Optional resize factor override
*/
resizeUpManual(factor) {
const oldFactor = this.resizeFactor;
if (factor !== void 0) {
this.resizeFactor = factor;
}
if (this.shouldResizeUp()) {
this.resizeUp();
}
this.resizeFactor = oldFactor;
}
/**
* Manually trigger resize down
* @param factor Optional resize factor override
*/
resizeDownManual(factor) {
const oldFactor = this.resizeFactor;
if (factor !== void 0) {
this.resizeFactor = factor;
}
if (this.shouldResizeDown()) {
this.resizeDown();
}
this.resizeFactor = oldFactor;
}
/**
* Get resize statistics
*/
getStats() {
return {
resizeCount: this.resizeCount,
lastResizeTime: this.lastResizeTime,
currentCapacity: this.currentCapacity,
utilization: this.utilization,
resizeThreshold: this.resizeThreshold,
shrinkThreshold: this.shrinkThreshold
};
}
};
// src/pools/pool.ts
var ConnectionPoolStatus = /* @__PURE__ */ (function(ConnectionPoolStatus2) {
ConnectionPoolStatus2["HEALTHY"] = "healthy";
ConnectionPoolStatus2["DEGRADED"] = "degraded";
ConnectionPoolStatus2["OVERLOADED"] = "overloaded";
ConnectionPoolStatus2["UNAVAILABLE"] = "unavailable";
return ConnectionPoolStatus2;
})({});
var ConnectionPoolEvent = /* @__PURE__ */ (function(ConnectionPoolEvent2) {
ConnectionPoolEvent2["CONNECTION_ADDED"] = "connection_added";
ConnectionPoolEvent2["CONNECTION_REMOVED"] = "connection_removed";
ConnectionPoolEvent2["CONNECTION_TIMEOUT"] = "connection_timeout";
ConnectionPoolEvent2["CONNECTION_ERROR"] = "connection_error";
ConnectionPoolEvent2["POOL_LIMIT_REACHED"] = "pool_limit_reached";
ConnectionPoolEvent2["HEALTH_STATUS_CHANGED"] = "health_status_changed";
return ConnectionPoolEvent2;
})({});
var ConnectionPoolManager = class {
static {
__name(this, "ConnectionPoolManager");
}
logger = createLogger({
module: "connection_pool"
});
config;
protocol;
startTime = Date.now();
eventListeners = /* @__PURE__ */ new Map();
eventListenerErrors = /* @__PURE__ */ new Map();
// 连接池核心数据
connections = /* @__PURE__ */ new Map();
connectionMetadata = /* @__PURE__ */ new Map();
waitingQueue = [];
// 统计和健康状态
metrics;
currentHealth;
// 性能监控 - 使用环形缓冲区提高性能
latencyBuffer;
errorWindow;
lastMetricsUpdate = Date.now();
// 定时器引用 - 用于清理
healthCheckInterval;
cleanupInterval;
constructor(protocol, config = {}) {
this.protocol = protocol;
this.config = this.validateAndNormalizeConfig(config);
this.logger = createLogger({
module: "connection_pool",
protocol: this.protocol
});
this.latencyBuffer = new RingBuffer(1e3);
this.errorWindow = new RingBuffer(500);
this.metrics = this.initializeMetrics();
this.currentHealth = this.initializeHealth();
this.logger.info("Connection pool manager initialized", {}, {
protocol: this.protocol,
config: this.config
});
this.startPeriodicTasks();
}
/**
* 初始化指标
*/
initializeMetrics() {
return {
protocol: this.protocol,
activeConnections: 0,
totalConnections: 0,
connectionsPerSecond: 0,
averageLatency: 0,
errorRate: 0,
poolConfig: this.config,
health: this.currentHealth,
performance: {
throughput: 0,
latency: {
p50: 0,
p95: 0,
p99: 0
},
memoryUsage: 0,
cpuUsage: 0
},
uptime: 0
};
}
/**
* 初始化健康状态
*/
initializeHealth() {
return {
status: "healthy",
utilizationRatio: 0,
activeConnections: 0,
maxConnections: this.config.maxConnections || 1e3,
rejectedConnections: 0,
averageResponseTime: 0,
errorRate: 0,
message: "Connection pool is healthy",
lastUpdated: Date.now()
};
}
/**
* Validate and normalize configuration
*/
validateAndNormalizeConfig(config) {
const normalized = {
maxConnections: config.maxConnections || 1e3,
connectionTimeout: config.connectionTimeout || 3e4,
keepAliveTimeout: config.keepAliveTimeout || 5e3,
requestTimeout: config.requestTimeout || 3e4,
headersTimeout: config.headersTimeout || 1e4,
...config
};
if (normalized.maxConnections && normalized.maxConnections <= 0) {
throw new Error("maxConnections must be positive");
}
if (normalized.connectionTimeout && normalized.connectionTimeout <= 0) {
throw new Error("connectionTimeout must be positive");
}
return normalized;
}
/**
* 申请连接
*/
async requestConnection(options = {}) {
const startTime = Date.now();
const timeout = options.timeout || this.config.connectionTimeout || 3e4;
try {
if (!this.canAcceptConnection()) {
this.currentHealth.rejectedConnections++;
this.emitEvent("pool_limit_reached", {
currentConnections: this.getActiveConnectionCount(),
maxConnections: this.config.maxConnections
});
return {
connection: null,
success: false,
error: new Error("Connection pool limit reached"),
waitTime: Date.now() - startTime
};
}
const availableConnection = await this.getAvailableConnection();
if (availableConnection) {
return {
connection: availableConnection.connection,
success: true,
waitTime: Date.now() - startTime,
connectionId: availableConnection.id
};
}
const newConnection = await this.createNewConnection(options);
if (newConnection) {
await this.addConnection(newConnection.connection, newConnection.metadata);
return {
connection: newConnection.connection,
success: true,
waitTime: Date.now() - startTime,
connectionId: newConnection.id
};
}
return new Promise((resolve2, reject) => {
const timeoutHandle = setTimeout(() => {
const index = this.waitingQueue.findIndex((item) => item.resolve === resolve2);
if (index >= 0) {
this.waitingQueue.splice(index, 1);
}
resolve2({
connection: null,
success: false,
error: new Error("Connection request timeout"),
waitTime: Date.now() - startTime
});
}, timeout);
const queueItem = {
resolve: /* @__PURE__ */ __name((result) => {
clearTimeout(timeoutHandle);
resolve2(result);
}, "resolve"),
reject: /* @__PURE__ */ __name((error) => {
clearTimeout(timeoutHandle);
reject(error);
}, "reject"),
options,
timestamp: startTime
};
const priorityWeight = {
low: 1,
normal: 2,
high: 3
};
const newPriority = priorityWeight[options.priority || "normal"];
let insertIndex = this.waitingQueue.length;
for (let i = 0; i < this.waitingQueue.length; i++) {
const existingPriority = priorityWeight[this.waitingQueue[i].options.priority || "normal"];
if (newPriority > existingPriority) {
insertIndex = i;
break;
}
}
this.waitingQueue.splice(insertIndex, 0, queueItem);
});
} catch (error) {
this.recordConnectionEvent("error", {
error
});
return {
connection: null,
success: false,
error,
waitTime: Date.now() - startTime
};
}
}
/**
* 释放连接
*/
async releaseConnection(connection, options = {}) {
const traceId = generateTraceId();
try {
const connectionId = this.findConnectionId(connection);
if (!connectionId) {
this.logger.warn("Attempting to release unknown connection", {
traceId
});
return false;
}
if (options.destroy || options.error) {
await this.removeConnection(connection, options.error?.message || "Explicitly destroyed");
this.logger.debug("Connection destroyed", {
traceId
}, {
connectionId
});
} else {
this.markConnectionAvailable(connectionId);
this.logger.debug("Connection released and marked available", {
traceId
}, {
connectionId
});
}
await this.processWaitingQueue();
return true;
} catch (error) {
this.logger.error("Failed to release connection", {
traceId
}, error);
return false;
}
}
/**
* Add connection to the pool
*/
async addConnection(connection, metadata = {}) {
const connectionId = this.generateConnectionId();
try {
if (!this.validateConnection(connection)) {
throw new Error("Invalid connection");
}
this.connections.set(connectionId, connection);
this.connectionMetadata.set(connectionId, {
...metadata,
id: connectionId,
createdAt: Date.now(),
lastUsed: Date.now(),
available: true
});
this.recordConnectionEvent("added", {
connectionId,
metadata
});
this.emitEvent("connection_added", {
connectionId,
connection
});
this.logger.debug("Connection added to pool", {}, {
connectionId
});
return true;
} catch (error) {
this.logger.error("Failed to add connection to pool", {}, error);
return false;
}
}
/**
* Remove connection from pool
*/
async removeConnection(connection, reason) {
const connectionId = this.findConnectionId(connection);
if (!connectionId) return;
try {
await this.cleanupConnection(connection);
this.connections.delete(connectionId);
this.connectionMetadata.delete(connectionId);
this.recordConnectionEvent("removed", {
connectionId,
reason
});
this.emitEvent("connection_removed", {
connectionId,
reason
});
this.logger.debug("Connection removed from pool", {}, {
connectionId,
reason
});
} catch (error) {
this.logger.error("Error removing connection from pool", {}, error);
}
}
/**
* Get active connection count
*/
getActiveConnectionCount() {
return this.connections.size;
}
/**
* Close all connections
*/
async closeAllConnections(timeout = 5e3) {
const traceId = generateTraceId();
this.logger.info("Closing all connections", {
traceId
}, {
activeConnections: this.connections.size
});
const closePromises = [];
for (const [connectionId, connection] of this.connections) {
closePromises.push(this.removeConnection(connection, "Pool shutdown").catch((error) => {
this.logger.error("Error closing connection", {
traceId
}, {
connectionId,
error
});
}));
}
try {
await Promise.race([
Promise.all(closePromises),
new Promise((_, reject) => setTimeout(() => reject(new Error("Close timeout")), timeout))
]);
} catch (error) {
this.logger.warn("Some connections failed to close gracefully", {
traceId
}, error);
}
this.connections.clear();
this.connectionMetadata.clear();
this.logger.info("All connections closed", {
traceId
});
}
/**
* 创建新连接
*/
async createNewConnection(options) {
const result = await this.createProtocolConnection(options);
if (result) {
return {
connection: result.connection,
id: this.generateConnectionId(),
metadata: result.metadata
};
}
return null;
}
/**
* Check if new connections can be accepted
*/
canAcceptConnection() {
const maxConnections = this.config.maxConnections;
if (!maxConnections) return true;
const currentConnections = this.getActiveConnectionCount();
return currentConnections < maxConnections;
}
/**
* Update connection pool health status
*/
updateHealthStatus() {
const activeConnections = this.getActiveConnectionCount();
const maxConnections = this.config.maxConnections || Infinity;
const utilizationRatio = maxConnections === Infinity ? 0 : activeConnections / maxConnections;
let status = "healthy";
let message = "Connection pool is healthy";
if (utilizationRatio > 0.95) {
status = "overloaded";
message = "Connection pool is overloaded";
} else if (utilizationRatio > 0.8) {
status = "degraded";
message = "Connection pool is under high load";
}
const oldStatus = this.currentHealth.status;
this.currentHealth = {
...this.currentHealth,
status,
utilizationRatio,
activeConnections,
maxConnections: maxConnections === Infinity ? 0 : maxConnections,
message,
lastUpdated: Date.now()
};
this.metrics.health = this.currentHealth;
if (oldStatus !== status) {
this.emitEvent("health_status_changed", {
oldStatus,
newStatus: status,
health: this.currentHealth
});
}
}
/**
* Get connection pool health status
*/
getHealth() {
this.updateHealthStatus();
return {
...this.currentHealth
};
}
/**
* Get connection pool metrics
*/
getMetrics() {
const uptime = Date.now() - this.startTime;
this.updatePerformanceMetrics();
return {
...this.metrics,
uptime,
health: this.getHealth(),
activeConnections: this.getActiveConnectionCount()
};
}
/**
* Get connection pool configuration
*/
getConfig() {
return {
...this.config
};
}
/**
* Update connection pool configuration
*/
async updateConfig(newConfig) {
const traceId = generateTraceId();
try {
this.logger.info("Updating connection pool configuration", {
traceId
}, {
oldConfig: this.config,
newConfig
});
const updatedConfig = this.validateAndNormalizeConfig({
...this.config,
...newConfig
});
Object.assign(this.config, updatedConfig);
this.metrics.poolConfig = this.config;
this.logger.info("Connection pool configuration updated successfully", {
traceId
});
return true;
} catch (error) {
this.logger.error("Failed to update connection pool configuration", {
traceId
}, error);
return false;
}
}
/**
* Add event listener
*/
on(event, listener) {
if (!this.eventListeners.has(event)) {
this.eventListeners.set(event, /* @__PURE__ */ new Set());
}
this.eventListeners.get(event).add(listener);
}
/**
* Remove event listener
*/
off(event, listener) {