feat: publish v1.0.0-rc.36-custom.2
Fixed-group tokens with a backup group now take precedence over the session affinity skip-retry-on-failure rule, so retryable upstream errors (503) fall through to the backup group instead of stopping on the primary channel. Non-retryable errors, explicit channel pins, and auto groups are unchanged. Adds controller regression tests for the affinity override and a build-time Go test gate.
This commit is contained in:
1 parent
3a7e8f3b54
commit
aa6c3f50f4
6 files changed
+108
-4
No files matched your search
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/pkg/cachex"
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
@@ -627,6 +628,17 @@ func ShouldSkipRetryAfterChannelAffinityFailure(c *gin.Context) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
// Explicit fixed-token fallback takes precedence over cache affinity.
|
||||
primary := common.GetContextKeyString(c, constant.ContextKeyTokenGroup)
|
||||
if primary != "" && primary != "auto" && common.GetContextKeyBool(c, constant.ContextKeyTokenCrossGroupRetry) {
|
||||
if groups, ok := common.GetContextKeyType[[]string](c, constant.ContextKeyTokenAutoGroups); ok {
|
||||
for _, group := range FilterUserTokenAutoGroups(common.GetContextKeyString(c, constant.ContextKeyUserGroup), groups) {
|
||||
if group != primary {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v, ok := c.Get(ginKeyChannelAffinitySkipRetry)
|
||||
if ok {
|
||||
b, ok := v.(bool)
|
||||
|
||||
@@ -193,3 +193,35 @@ func TestCacheGetRandomSatisfiedChannelSkipsUnavailableFixedPrimary(t *testing.T
|
||||
assert.Equal(t, "vip", selectedGroup)
|
||||
assert.Equal(t, "vip", common.GetContextKeyString(ctx, constant.ContextKeyAutoGroup))
|
||||
}
|
||||
|
||||
func TestFixedBackupAfterAffinityFailureWithOneRetry(t *testing.T) {
|
||||
db := setupChannelSelectAutoGroupsTest(t)
|
||||
common.RetryTimes = 1
|
||||
const modelName = "fixed-backup-affinity"
|
||||
createChannelSelectAutoGroupsChannel(t, db, 13, "default", modelName)
|
||||
createChannelSelectAutoGroupsChannel(t, db, 14, "vip", modelName)
|
||||
model.InitChannelCache()
|
||||
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
common.SetContextKey(ctx, constant.ContextKeyUserGroup, "default")
|
||||
common.SetContextKey(ctx, constant.ContextKeyTokenGroup, "default")
|
||||
common.SetContextKey(ctx, constant.ContextKeyTokenAutoGroups, []string{"vip"})
|
||||
common.SetContextKey(ctx, constant.ContextKeyTokenCrossGroupRetry, true)
|
||||
ctx.Set(ginKeyChannelAffinitySkipRetry, true)
|
||||
require.False(t, ShouldSkipRetryAfterChannelAffinityFailure(ctx))
|
||||
|
||||
// The distributor's affinity hit bypasses the ordered group selector.
|
||||
param := &RetryParam{Ctx: ctx, TokenGroup: "default", ModelName: modelName}
|
||||
param.SetRetry(1)
|
||||
first, _, err := CacheGetRandomSatisfiedChannel(param)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, first)
|
||||
require.Equal(t, 13, first.Id)
|
||||
require.Equal(t, 0, param.GetRetry())
|
||||
param.IncreaseRetry()
|
||||
backup, group, err := CacheGetRandomSatisfiedChannel(param)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, backup)
|
||||
require.Equal(t, 14, backup.Id)
|
||||
require.Equal(t, "vip", group)
|
||||
require.Equal(t, "vip", common.GetContextKeyString(ctx, constant.ContextKeyAutoGroup))
|
||||
}
|
||||
Reference in new issue
Block a user