()
| 197 | } |
| 198 | |
| 199 | func (channel *Channel) GetNextEnabledKey() (string, int, *types.NewAPIError) { |
| 200 | // If not in multi-key mode, return the original key string directly. |
| 201 | if !channel.ChannelInfo.IsMultiKey { |
| 202 | return channel.Key, 0, nil |
| 203 | } |
| 204 | |
| 205 | // Obtain all keys (split by \n) |
| 206 | keys := channel.GetKeys() |
| 207 | if len(keys) == 0 { |
| 208 | // No keys available, return error, should disable the channel |
| 209 | return "", 0, types.NewError(errors.New("no keys available"), types.ErrorCodeChannelNoAvailableKey) |
| 210 | } |
| 211 | |
| 212 | lock := GetChannelPollingLock(channel.Id) |
| 213 | lock.Lock() |
| 214 | defer lock.Unlock() |
| 215 | |
| 216 | statusList := channel.ChannelInfo.MultiKeyStatusList |
| 217 | // helper to get key status, default to enabled when missing |
| 218 | getStatus := func(idx int) int { |
| 219 | if statusList == nil { |
| 220 | return common.ChannelStatusEnabled |
| 221 | } |
| 222 | if status, ok := statusList[idx]; ok { |
| 223 | return status |
| 224 | } |
| 225 | return common.ChannelStatusEnabled |
| 226 | } |
| 227 | |
| 228 | // Collect indexes of enabled keys |
| 229 | enabledIdx := make([]int, 0, len(keys)) |
| 230 | for i := range keys { |
| 231 | if getStatus(i) == common.ChannelStatusEnabled { |
| 232 | enabledIdx = append(enabledIdx, i) |
| 233 | } |
| 234 | } |
| 235 | // If no specific status list or none enabled, return an explicit error so caller can |
| 236 | // properly handle a channel with no available keys (e.g. mark channel disabled). |
| 237 | // Returning the first key here caused requests to keep using an already-disabled key. |
| 238 | if len(enabledIdx) == 0 { |
| 239 | return "", 0, types.NewError(errors.New("no enabled keys"), types.ErrorCodeChannelNoAvailableKey) |
| 240 | } |
| 241 | |
| 242 | switch channel.ChannelInfo.MultiKeyMode { |
| 243 | case constant.MultiKeyModeRandom: |
| 244 | // Randomly pick one enabled key |
| 245 | selectedIdx := enabledIdx[rand.Intn(len(enabledIdx))] |
| 246 | return keys[selectedIdx], selectedIdx, nil |
| 247 | case constant.MultiKeyModePolling: |
| 248 | // Use channel-specific lock to ensure thread-safe polling |
| 249 | |
| 250 | channelInfo, err := CacheGetChannelInfo(channel.Id) |
| 251 | if err != nil { |
| 252 | return "", 0, types.NewError(err, types.ErrorCodeGetChannelFailed, types.ErrOptionWithSkipRetry()) |
| 253 | } |
| 254 | defer func() { |
| 255 | if common.DebugEnabled { |
| 256 | logger.LogDebug(nil, "channel %d polling index: %d", channel.Id, channel.ChannelInfo.MultiKeyPollingIndex) |
no test coverage detected