Skip to content

fix(amsg-client): subscribePush() 认出并重订死 endpoint,不再返回一个推不通的订阅 - #48

Merged
Tosd0 merged 1 commit into
mainfrom
fix/client-zombie-push-endpoint
Aug 6, 2026
Merged

fix(amsg-client): subscribePush() 认出并重订死 endpoint,不再返回一个推不通的订阅#48
Tosd0 merged 1 commit into
mainfrom
fix/client-zombie-push-endpoint

Conversation

@Tosd0

@Tosd0 Tosd0 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

问题

unsubscribe() 过又马上 subscribe() 时,Chromium 那边旧订阅的待删除标记还没清干净。这段窗口期里它不去推送服务要新地址,而是直接给一个 https://permanently-removed.invalid/... 的占位订阅——.invalid 是 RFC 2606 保留顶级域,全球 DNS 永不解析。

这种订阅结构上完全正常:有 endpoint、有密钥、getSubscription() 也认得。之前 subscribePush() 拿到什么返回什么,于是这个死地址会被一路登记到服务端。表现是最难查的那一种——用户看到「订阅成功」,服务端也存下了,两边都没有任何报错,只是到点什么都收不到。

改后

之前 现在
第一次就拿到活 endpoint 返回 返回(只调一次 subscribe(),无额外等待,行为不变)
拿到 permanently-removed.invalid 占位订阅 原样返回,调用方登记一个死地址 退掉它、等浏览器清完标记再重订,最多三次(间隔 800ms、1600ms)
三次仍是占位订阅 —— 抛错,err.code === 'PUSH_ENDPOINT_ZOMBIE'err.details{ attempts, endpoint }
pushManager.subscribe() 自己抛错 往外抛 原样往外抛,不重试(权限 / 能力问题,重试只会把同一个错误多抛两遍)

subscribePush() 的契约由此变成「给你一个 endpoint 活着的订阅」,拿到就能直接登记。

只给机制,不给文案

这是通用 SDK,错误里不塞面向终端用户的提示语——那句话该怎么说、用什么语言、要不要引导用户换浏览器,跟具体产品和用户群有关,由接入方自己决定。这里只给一个可程序化判定的 err.code,让下游能把「这是僵尸 endpoint」和「别的订阅失败」分开处理。

测试

packages/rei-standard-amsg/client/test/push-subscription.test.mjs 补四条回归守卫:首次即活只订一次;首次占位二次活的返回活的且占位那条被退订;三次全占位抛 PUSH_ENDPOINT_ZOMBIE 且退避为 800/1600;subscribe() 抛错时只调一次。另加一条「占位那条退订失败也照样重试」。

退避走可注入的 sleep seam(沿用 agentic-fire.js_agenticSleep 写法),测试不吃真实等待。

全 workspace npm test:743 通过 / 0 失败。

https://claude.ai/code/session_01T6Z3bzeKPu6jHyVuM3SuyW

刚 unsubscribe() 过又马上 subscribe() 时,Chromium 那边旧订阅的待删除标记还没清
干净,这段窗口期里它不去推送服务要新地址,而是给一个
https://permanently-removed.invalid/... 的占位订阅。.invalid 是 RFC 2606 保留顶级
域,永不解析。这种订阅有 endpoint、有密钥、getSubscription() 也认得,之前会被原样
返回并登记到服务端:用户看到「订阅成功」,两边都不报错,到点推送全挂。

- 拿到占位订阅就退掉它,等浏览器清完标记再重订,最多三次(间隔 800ms、1600ms)
- 三次仍是占位订阅时抛 err.code === 'PUSH_ENDPOINT_ZOMBIE',err.details 带
  { attempts, endpoint }。SDK 不产出面向终端用户的提示文案,交给接入方决定
- pushManager.subscribe() 自己抛的错原样往外抛、不重试:那是权限 / 能力问题
- 第一次就拿到活 endpoint 时只调一次 subscribe()、不产生额外等待,与之前一致

回归测试钉住四条:首次即活只订一次;首次占位二次活的返回活的且占位那条被退订;
三次全占位抛 PUSH_ENDPOINT_ZOMBIE 且退避为 800/1600;subscribe() 抛错时只调一次。
退避走可注入的 sleep seam,测试不吃真实等待。

Claude-Session: https://claude.ai/code/session_01T6Z3bzeKPu6jHyVuM3SuyW
@Tosd0
Tosd0 merged commit a2a6dc6 into main Aug 6, 2026
1 check passed
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