fix(relay): 修复客户端断开时误伤渠道健康评分问题
客户端断开连接(context.Canceled / DeadlineExceeded)时,原来的行为: 1. MaterializeSelection 调用 getChannelKey(ctx, ...) 因 ctx 已取消而失败 2. 失败被包装为 ErrChannelUnavailable(掩盖真实原因) 3. reportMaterializeFailure 对渠道记了一次健康分惩罚 实际上渠道本身完全正常,是客户端主动断开导致的。 修复: - MaterializeSelection:key 查询前后各加一次 ctx.Err() 检查, 有 ctx 错误时直接返回 ctx.Err() 而非 ErrChannelUnavailable - relay_handler.go 调度循环:MaterializeSelection 返回 context 错误时 静默退出(不记 channelErrors、不调 reportMaterializeFailure), 用 context.WithoutCancel 退还预扣款项后直接返回,渠道健康分不受影响
This commit is contained in:
1 parent
6683ca6d40
commit
60373a373d
2 files changed
+18
No files matched your search
@@ -866,6 +866,12 @@ func InitHealthScore(ctx context.Context, channelID int64) error {
|
||||
// MaterializeSelection 由新调度引擎的决策构造 ChannelSelection(阶段 3):
|
||||
// 转发元数据来自目录内存快照(零 DB),渠道 Key 按 keyID 解密(keyID=0 回退取首个 active Key)。
|
||||
func (p *DataProviderImpl) MaterializeSelection(ctx context.Context, channelID, keyID int64, modelName string) (*common.ChannelSelection, error) {
|
||||
// 优先检查上下文:客户端已断开时直接返回 ctx.Err(),而非 ErrChannelUnavailable,
|
||||
// 让上层能区分"客户端断开"与"真实渠道无可用 Key",避免误伤渠道健康评分
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
catalog := dispatchadapter.CatalogInstance()
|
||||
if catalog == nil {
|
||||
return nil, common.ErrChannelUnavailable
|
||||
@@ -883,6 +889,10 @@ func (p *DataProviderImpl) MaterializeSelection(ctx context.Context, channelID,
|
||||
apiKey, err = getChannelKey(ctx, channelID)
|
||||
}
|
||||
if err != nil || apiKey == "" {
|
||||
// Key 查询失败:再次检查 ctx,区分"客户端断开导致查询中断"与"真实无可用 Key"
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
return nil, ctxErr
|
||||
}
|
||||
return nil, common.ErrChannelUnavailable
|
||||
}
|
||||
|
||||
|
||||
@@ -480,6 +480,14 @@ func RelayHandler(ctx context.Context, body []byte, path string, headers http.He
|
||||
|
||||
selection, mErr := provider.MaterializeSelection(ctx, d.Channel.ID, d.KeyID, v.lookupModel)
|
||||
if mErr != nil {
|
||||
// 客户端已断开(context.Canceled / DeadlineExceeded):Key 查询被中断不代表渠道有问题,
|
||||
// 静默退出,不记入 channelErrors,不触发渠道健康惩罚,退还预扣款项后返回
|
||||
if errors.Is(mErr, context.Canceled) || errors.Is(mErr, context.DeadlineExceeded) {
|
||||
if billing != nil && preDeductAmount > 0 {
|
||||
_ = billing.SettleFailed(context.WithoutCancel(ctx), rc.TenantID, rc.RequestID, preDeductAmount)
|
||||
}
|
||||
return nil, v.billingResult, mErr
|
||||
}
|
||||
// Key 解密失败 / 目录元数据缺失:按渠道级致命上报换渠道,不发起上游请求
|
||||
channelErrors = append(channelErrors, fmt.Sprintf("attempt=%d channel=%d materialize_error=[%v]", attempt, d.Channel.ID, mErr))
|
||||
if decision := reportMaterializeFailure(ctx, sess, mErr); decision == dispatch.DecisionAbort {
|
||||
|
||||
Reference in new issue
Block a user