Files
claude-relay-service/src/utils/statsHelper.js
DaydreamCoding 07633ddbf8 feat: enhance concurrency queue with health check and admin endpoints
- Add queue health check for fast-fail when overloaded (P90 > threshold)
  - Implement socket identity verification with UUID token
  - Add wait time statistics (P50/P90/P99) and queue stats tracking
  - Add admin endpoints for queue stats and cleanup
  - Add CLEAR_CONCURRENCY_QUEUES_ON_STARTUP config option
  - Update documentation with troubleshooting and proxy config guide
2025-12-12 14:32:09 +08:00

106 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 统计计算工具函数
* 提供百分位数计算、等待时间统计等通用统计功能
*/
/**
* 计算百分位数(使用 nearest-rank 方法)
* @param {number[]} sortedArray - 已排序的数组(升序)
* @param {number} percentile - 百分位数 (0-100)
* @returns {number} 百分位值
*
* 边界情况说明:
* - percentile=0: 返回最小值 (index=0)
* - percentile=100: 返回最大值 (index=len-1)
* - percentile=50 且 len=2: 返回第一个元素nearest-rank 向下取)
*
* 算法说明nearest-rank 方法):
* - index = ceil(percentile / 100 * len) - 1
* - 示例len=100, P50 → ceil(50) - 1 = 49第50个元素0-indexed
* - 示例len=100, P99 → ceil(99) - 1 = 98第99个元素
*/
function getPercentile(sortedArray, percentile) {
const len = sortedArray.length
if (len === 0) {
return 0
}
if (len === 1) {
return sortedArray[0]
}
// 边界处理percentile <= 0 返回最小值
if (percentile <= 0) {
return sortedArray[0]
}
// 边界处理percentile >= 100 返回最大值
if (percentile >= 100) {
return sortedArray[len - 1]
}
const index = Math.ceil((percentile / 100) * len) - 1
return sortedArray[index]
}
/**
* 计算等待时间分布统计
* @param {number[]} waitTimes - 等待时间数组(无需预先排序)
* @returns {Object|null} 统计对象,空数组返回 null
*
* 返回对象包含:
* - sampleCount: 样本数量(始终包含,便于调用方判断可靠性)
* - count: 样本数量(向后兼容)
* - min: 最小值
* - max: 最大值
* - avg: 平均值(四舍五入)
* - p50: 50百分位数中位数
* - p90: 90百分位数
* - p99: 99百分位数
* - sampleSizeWarning: 样本量不足时的警告信息(样本 < 10
* - p90Unreliable: P90 统计不可靠标记(样本 < 10
* - p99Unreliable: P99 统计不可靠标记(样本 < 100
*
* 可靠性标记说明(详见 design.md Decision 6
* - 样本 < 10: P90 和 P99 都不可靠
* - 样本 < 100: P99 不可靠P90 需要 10 个样本P99 需要 100 个样本)
* - 即使标记为不可靠,仍返回计算值供参考
*/
function calculateWaitTimeStats(waitTimes) {
if (!waitTimes || waitTimes.length === 0) {
return null
}
const sorted = [...waitTimes].sort((a, b) => a - b)
const sum = sorted.reduce((a, b) => a + b, 0)
const len = sorted.length
const stats = {
sampleCount: len, // 新增:始终包含样本数
count: len, // 向后兼容
min: sorted[0],
max: sorted[len - 1],
avg: Math.round(sum / len),
p50: getPercentile(sorted, 50),
p90: getPercentile(sorted, 90),
p99: getPercentile(sorted, 99)
}
// 渐进式可靠性标记(详见 design.md Decision 6
// 样本 < 10: P90 不可靠P90 至少需要 ceil(100/10) = 10 个样本)
if (len < 10) {
stats.sampleSizeWarning = 'Results may be inaccurate due to small sample size'
stats.p90Unreliable = true
}
// 样本 < 100: P99 不可靠P99 至少需要 ceil(100/1) = 100 个样本)
if (len < 100) {
stats.p99Unreliable = true
}
return stats
}
module.exports = {
getPercentile,
calculateWaitTimeStats
}