Skip to content

provider: 后台模型刷新启动时立即报告进行状态 - #1092

Closed
tsuixl wants to merge 1 commit into
yetone:mainfrom
tsuixl:codex/models-refresh-race
Closed

tsuixl wants to merge 1 commit into
yetone:mainfrom
tsuixl:codex/models-refresh-race

Conversation

@tsuixl

@tsuixl tsuixl commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

此 PR 已由合并的 #1147 替代,按已被上游解决关闭。

#1147 于 2026-10-13 合入 main,提交 dbcf791e,解决相同的 Providers 后台模型刷新竞态:页面在后台调用返回后立即读取 FetchingNew() 时,能获知模型列表仍在路上。

上游同时避免无待抓取任务时仍启动并计数空任务导致页面一直轮询,覆盖账户选择/合并调用期间的状态、CLI 身份查询保持异步,以及 FetchNew 的 missing 返回值与失败重试。它包含 TestFetchingNewOnceStarted、TestFetchingNewForgottenWhileListed 和 TestFetchNewListsAgainForAnAnswerWhileListing 等回归,故不再将本 PR 的计数入口应用到新主线。

收尾验证

感谢 @waitingsnow 完成更全面的修复和回归覆盖。

waitingsnow added a commit to waitingsnow/magpie that referenced this pull request Oct 13, 2026
… on its way, as a fetch started behind it is counted before it returns

FetchNewBehind and FetchNewSoon started FetchNew in a goroutine, and
newRunning, which FetchingNew reads, was counted only once it ran:
providersState read FetchingNew straight after, was told no fetch was on
its way, and the page stopped asking with the list still to come.

fetchNewGo counts a fetch before it returns, but only one with work: a
poll with nothing due starts and counts nothing, or each poll found the
last one's no-op still under way and the page asked for ever (yetone#1092 at
GOMAXPROCS=1). Which accounts are due is known at once unless a CLI is
left to ask who is signed in (never answered, being asked or about to be
asked by the next look): then the fetch is counted and lists, waits on
the CLI (yetone#1789) and fetches behind the page, which doesn't wait on a CLI
(yetone#541). FetchNewSoon lists nothing before it returns, so /api/state?last=1
still answers at once (yetone#1519). FetchingNew also says true while
fetchingNew is set, for a page whose FetchNewBehind found another still
picking the accounts due (tsuixl).

FetchNew keeps main's missing: an account that failed, one waiting out
newFetchRetry unless another's fetch listed it meanwhile, or a CLI that
didn't answer in time. The accounts are listed again when a CLI answered
while they were listed.
waitingsnow added a commit to waitingsnow/magpie that referenced this pull request Oct 13, 2026
… on its way, as a fetch started behind it is counted before it returns

FetchNewBehind and FetchNewSoon started FetchNew in a goroutine, and
newRunning, which FetchingNew reads, was counted only once it ran:
providersState read FetchingNew straight after, was told no fetch was on
its way, and the page stopped asking with the list still to come.

fetchNewGo counts a fetch before it returns, but only one with work: a
poll with nothing due starts and counts nothing, or each poll found the
last one's no-op still under way and the page asked for ever (yetone#1092 at
GOMAXPROCS=1). Which accounts are due is known at once unless a CLI is
left to ask who is signed in (never answered, being asked or about to be
asked by the next look): then the fetch is counted and lists, waits on
the CLI (yetone#1789) and fetches behind the page, which doesn't wait on a CLI
(yetone#541). FetchNewSoon lists nothing before it returns, so /api/state?last=1
still answers at once (yetone#1519). FetchingNew also says true while
fetchingNew is set, for a page whose FetchNewBehind found another still
picking the accounts due (tsuixl).

FetchNew keeps main's missing: an account that failed, one waiting out
newFetchRetry unless another's fetch listed it meanwhile, or a CLI that
didn't answer in time. The accounts are listed again when a CLI answered
while they were listed.
waitingsnow added a commit to waitingsnow/magpie that referenced this pull request Oct 13, 2026
… on its way, as a fetch started behind it is counted before it returns

FetchNewBehind and FetchNewSoon started FetchNew in a goroutine, and
newRunning, which FetchingNew reads, was counted only once it ran:
providersState read FetchingNew straight after, was told no fetch was on
its way, and the page stopped asking with the list still to come.

fetchNewGo counts a fetch before it returns, but only one with work: a
poll with nothing due starts and counts nothing, or each poll found the
last one's no-op still under way and the page asked for ever (yetone#1092 at
GOMAXPROCS=1). Which accounts are due is known at once unless a CLI is
left to ask who is signed in (never answered, being asked or about to be
asked by the next look): then the fetch is counted and lists, waits on
the CLI (yetone#1789) and fetches behind the page, which doesn't wait on a CLI
(yetone#541). FetchNewSoon lists nothing before it returns, so /api/state?last=1
still answers at once (yetone#1519). FetchingNew also says true while
fetchingNew is set, for a page whose FetchNewBehind found another still
picking the accounts due (tsuixl).

FetchNew keeps main's missing: an account that failed, one waiting out
newFetchRetry unless another's fetch listed it meanwhile, or a CLI that
didn't answer in time. The accounts are listed again when a CLI answered
while they were listed.
@tsuixl
tsuixl force-pushed the codex/models-refresh-race branch from 82896e7 to 6529eed Compare October 13, 2026 07:31
后台任务在发布状态和启动 goroutine 前登记计数,Soon 共用后台入口,保留同步调用计数和任务合并语义。

同步最新主线,保留 FetchNew 返回模型列表是否仍缺失的契约,供 TUI 与 headless gateway 重试。合并文档更新,不覆盖主线新增的模型和账户说明。

新增回归在最新主线生产代码上各 20/20 失败;修复后 CPU 1/2/4 各 20 轮通过,race 各 5 轮通过。Windows 和 Linux vet 与构建通过。验证范围和跳过项目见 PR。
@tsuixl
tsuixl force-pushed the codex/models-refresh-race branch from 6529eed to 7282093 Compare October 13, 2026 07:45
yetone pushed a commit that referenced this pull request Oct 13, 2026
… on its way, as a fetch started behind it is counted before it returns (#1147)

* providers: the Providers page keeps asking while an account's list is on its way, as a fetch started behind it is counted before it returns

FetchNewBehind and FetchNewSoon started FetchNew in a goroutine, and
newRunning, which FetchingNew reads, was counted only once it ran:
providersState read FetchingNew straight after, was told no fetch was on
its way, and the page stopped asking with the list still to come.

fetchNewGo counts a fetch before it returns, but only one with work: a
poll with nothing due starts and counts nothing, or each poll found the
last one's no-op still under way and the page asked for ever (#1092 at
GOMAXPROCS=1). Which accounts are due is known at once unless a CLI is
left to ask who is signed in (never answered, being asked or about to be
asked by the next look): then the fetch is counted and lists, waits on
the CLI (#1789) and fetches behind the page, which doesn't wait on a CLI
(#541). FetchNewSoon lists nothing before it returns, so /api/state?last=1
still answers at once (#1519). FetchingNew also says true while
fetchingNew is set, for a page whose FetchNewBehind found another still
picking the accounts due (tsuixl).

FetchNew keeps main's missing: an account that failed, one waiting out
newFetchRetry unless another's fetch listed it meanwhile, or a CLI that
didn't answer in time. The accounts are listed again when a CLI answered
while they were listed.

* providers: a CLI that answers while the accounts are listed has its account's list fetched in that pass, and one forgotten then sends the fetch behind the page

fetchNewHeld counts the CLIs answered before it lists the accounts and
lists them again if one answered by the time awaitIdentities returns,
in place of listForNew; TestFetchNewListsAgainForAnAnswerWhileListing
fails without it (and on main). fetchNewGo, which lists only with no CLI
left to ask, sends the fetch behind the page when one was forgotten
meanwhile (a sign-in finishing); TestFetchingNewForgottenWhileListed
fails without it. FetchNewSoon no longer counts itself before FetchNew
does: fetchingNew covers that. The CLI cases of
TestFetchingNewOnceStarted wait on a 5s look and allow 1s (yetone).
@tsuixl tsuixl closed this Oct 13, 2026
@yetone

yetone commented Oct 13, 2026

Copy link
Copy Markdown
Owner

抱歉这么久才来 review,也谢谢你和 @waitingsnow 找到并讲清了这个问题。

这个问题已经在 main 上修好了:#1147(dbcf791e4)合进了同一个修复,提交说明里提到了 #1092 和你。所以这个 PR 我先关掉,没有再合一次。

我在当前 main 上对你的代码做过 review 和测试:

  • TestFetchNew* 在 -count=10 -cpu 1,2,4 和 -race 下都通过;
  • 换回旧的 models.go 后,你新加的测试稳定变红(「background call returned before its fetch was registered」等),说明它们确实守住了这个问题;
  • ./internal/provider ./internal/gui 全部通过。

不过 rebase 时它和 #1147 在 models.go 里冲突了。#1147 的做法有一点不同:只在确实有账户要抓取时才计数,然后立即返回。没有事可做的轮询不会被计数,否则在 GOMAXPROCS=1 下,每次轮询都会看到上一次的空任务还「在进行」,页面就会一直问下去。另外,FetchingNew 在 fetchingNew 置位期间也会返回 true,覆盖了「另一个后台任务还在挑选账户」的那段时间。

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.

2 participants