Repository navigation
fix(cloud): 落盘前把路由表里在线的主机记成此刻在线,最近在线与核心版本随落盘前进 - #197
Merged
Merged
Conversation
Owlbay
force-pushed
the
fix/cloud-source-last-seen
branch
from
November 2, 2026 20:58
c0e2d48 to
b2963ae
Compare
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更摘要 / Summary
中文:修 #190。
根因:
sources.last_seen_at_ms/core_version只由relay:presence的上线、下线事件经presenceRecorder.Observe累计、每 5 分钟落盘。事件只在状态变化时发一次,主机一直连着就再没有事件,「最近在线」停在连上的那一刻(vps-43 测试部署上连了近一天的主机显示约 23.7 小时前)。修法:
control.RouteLister.Online(ctx):路由表可选的枚举能力,Memory、Redis(SCAN route:src:*后成批读值,跳过期间消失与损坏的键)、PostgreSQL(relay_routes里未过期的行)都实现,合同测试同步。presenceRecorder.Flush先读一遍路由表,把当前持有隧道的源记成「此刻在线」并带上路由里的核心版本,再按原有逻辑落盘(只往后推、多实例取较大值、失败的条目留到下一轮)。newPresenceRecorder的依赖改成store.SourceStore、路由表与时钟,便于单测。范围外:个人中转的
personal/store.go有同样的「只在事件里记」的结构,但 #190 只报了 SaaS,本 PR 不动它。English: Fixes #190.
Root cause:
sources.last_seen_at_ms/core_versionwere only accumulated from the online/offline events onrelay:presence(presenceRecorder.Observe) and flushed every 5 minutes. An event is emitted once per state change, so a host that stays connected emits nothing and its "last seen" stays at the connect time (a host connected for almost a day on the vps-43 test deployment showed ~23.7 h ago).Fix:
control.RouteLister.Online(ctx): an optional capability of the route table, implemented by Memory, Redis (SCAN route:src:*then batched reads, skipping keys that vanished or are corrupt) and PostgreSQL (unexpired rows ofrelay_routes), with contract tests.presenceRecorder.Flushfirst reads the route table and stamps every source that currently holds a tunnel as seen now, with the core version in its route, then flushes as before (forward-only, larger value wins across instances, failed entries retried next round).newPresenceRecordernow takesstore.SourceStore, the route table and a clock, which keeps it unit-testable.Out of scope: the personal relay's
personal/store.gohas the same events-only structure, but #190 only reports SaaS, so this PR leaves it alone.关联 issue / Related issues
Closes #190
改动范围 / Scope
go/)saas-m1-implementation.md、CHANGELOG)契约与协议变化 / Contract and protocol changes
数据库迁移 / Database migrations
无 / None(只读
relay_routes,不改表结构 / readsrelay_routesonly, no schema change)测试 / Testing
先写测试再修:临时去掉
stampOnline调用后,下列用例全部失败,恢复后通过。 / Tests first: with thestampOnlinecall temporarily removed these cases fail, and pass once restored.cloud/server(单测):TestPresenceFlushStampsConnectedSources(一直在线的主机两次落盘之间last_seen前进、核心版本跟随,下线后停住)、TestPresenceOfflineStopsAtOfflineTime、TestPresenceFlushMovesForwardOnly、TestPresenceFlushDegrades(路由表读不到、写库失败留到下一轮)、TestPresenceWithoutLister。cloud/server(真实 Redis + PostgreSQL):TestRedisRelayPresenceStampsConnectedHost。control:Memory 的Online;controltest合同新增「Online 列出全部未过期的路由,别的节点写的也看得到」,Redis 与 PostgreSQL 两种实现各跑。单元测试 / Unit tests:
pnpm go:check(gofmt、vet、staticcheck、go test -race、生成物新鲜度)通过,带本地一次性 PostgreSQL 17 与 Redis 8 容器,cloud/server、control/redis、control/postgres都实际跑了,没有跳过。集成测试 / Integration tests: 未跑
pnpm test:integration跨仓端到端 / Cross-repo e2e: 未跑
镜像 / Images: 未跑
pnpm check与 / andpnpm repo:test检查清单 / Checklist
msg为简体中文短句、变量进字段 / No apps, protocol package, migrations or dependencies touched; log messages are short Simplified Chinese sentences with variables in fieldspersonal缺省装配不依赖 PostgreSQL / Redis(只有 SaaS 的 Redis 控制面用到Online)/ the defaultpersonalassembly still does not depend on PostgreSQL / Redis (only the SaaS Redis control plane usesOnline)CHANGELOG.md的 Unreleased / Recorded under Unreleased inCHANGELOG.md需要维护者提供的外部条件 / External prerequisites
无 / None
已知限制 / Known limitations
Sources.Update;主机数很大时这是每 5 分钟的批量写,必要时可再改成按差异写。 / Each flush does oneSources.Updateper connected host; with very many hosts this is a batch write every 5 minutes and could later be reduced to writing only on change.CHANGELOG.md顶部新增## Unreleased,与另一个修复 PR(fix(relay): 边缘 PONG 写队列无上限、空闲判定只看整帧、节点间溢出不即刻断开 / Edge PONG queue unbounded, idle check only on full frames, inter-node overflow not closed at once #191)会在这里冲突,后合并的需要手动合并两条。 / This PR adds## Unreleasedat the top ofCHANGELOG.md, which will conflict with the other fix PR (fix(relay): 边缘 PONG 写队列无上限、空闲判定只看整帧、节点间溢出不即刻断开 / Edge PONG queue unbounded, idle check only on full frames, inter-node overflow not closed at once #191); whichever merges second needs to combine the two entries.