Skip to content

Show live channel concurrency in admin tables - #735

Merged
think-back merged 2 commits into
mainfrom
feat/channel-realtime-concurrency
Aug 17, 2026
Merged

Show live channel concurrency in admin tables#735
think-back merged 2 commits into
mainfrom
feat/channel-realtime-concurrency

Conversation

@think-back

Copy link
Copy Markdown
Collaborator

目标

渠道管理表实时显示每个限并发渠道的当前并发占用(模仿 sub2api 账号池的容量徽章),配合 #730 已上线的渠道并发限制使用:管理员在调整 max_concurrency 时能直接看到 活跃/上限、排队数和冷却状态。

实现

后端:新增 GET /api/channel/concurrency?ids=1,2,3(AdminAuth),返回每个限并发渠道的 {active, waiting, max_concurrency, cooling_down}

  • 复用 GetChannelConcurrencyLoads——轮询与路由共享同一套 200ms 快照缓存 + singleflight + 50 渠道分批(Cut channel concurrency Redis pressure for bounded rollout #730 的全部保护),不新增第二种 Redis 读取模式
  • max_concurrency <= 0 的渠道直接跳过,保持零 Redis 路径
  • ids 上限 200,非法 id 忽略

前端(两套管理台都做了):

  • classic(Semi UI):并发上限列的输入框上方加徽章 active/max (+N等待),灰=空闲 / 黄=有占用 / 红=打满,冷却中另有蓝色标记;useChannelsData 里 10s 轮询,当前页没有限并发渠道时完全不轮询
  • default(React Query):同样的徽章(Tailwind),useQuery refetchInterval: 10senabled 由当前页限并发渠道列表控制,状态经 table meta 传入 MaxConcurrencyCell
  • i18n:classic 9 语言补 等待/冷却中,default 8 语言补 Cooling

压力预算

每个打开渠道页的管理员会话:每 10s 一次请求 → 命中快照缓存时零 Redis 命令;未命中也是一次共享抓取(≤ 每渠道 4 命令、50 一批),多管理员同开被 singleflight 合并。路由本身就在以同样方式读这些计数器,轮询增量近似为零。

验证

  • go test ./controller -run GetChannelConcurrencyStatus:空 ids、不限渠道跳过、超量 400、非法 id 忽略,全绿
  • go vet / gofmt 干净
  • default tsc 0 新增错误(存量 rsbuild.config process 报错与本 PR 无关);classic 改动文件 eslint 干净
  • 未做:浏览器内视觉验证(徽章按两套前端各自既有样式令牌写)

徽章形态

3/5 黄色 = 上限 5 占用 3;5/5 +2等待 红色 = 打满且 2 个排队;蓝色 冷却中 标签;空闲灰色 0/5

Add GET /api/channel/concurrency returning active/waiting/cooldown counters for requested bounded channels, and render a sub2-style live badge (active/max, waiting count, cooldown state, gray/amber/red coloring) above the max-concurrency editor in both classic and default admin channel tables, polling every 10s only while the current page contains channels with a concurrency limit.

Constraint: Reads reuse GetChannelConcurrencyLoads, so polling shares the routing path's snapshot cache, singleflight, and pipeline batching instead of adding a second Redis read pattern; unlimited channels stay on the zero-Redis path and pages without bounded channels poll nothing.

Rejected: Embedding counters into the channel list query | would put Redis reads on every table load and search regardless of whether any channel is bounded.

Confidence: high

Scope-risk: narrow

Tested: go test ./controller -run GetChannelConcurrencyStatus (empty ids, unlimited skipped, oversized rejected, malformed ignored); go vet ./controller ./router; gofmt clean.

Tested: web/default tsc typecheck clean apart from pre-existing rsbuild.config process errors; web/classic eslint clean on touched files.

Not-tested: visual check of the badge in a running console; polling behavior verified by code review only.
…-concurrency

# Conflicts:
#	web/default/src/i18n/locales/en.json
#	web/default/src/i18n/locales/es.json
#	web/default/src/i18n/locales/fr.json
#	web/default/src/i18n/locales/ja.json
#	web/default/src/i18n/locales/pt.json
#	web/default/src/i18n/locales/ru.json
#	web/default/src/i18n/locales/vi.json
#	web/default/src/i18n/locales/zh.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant