刷值向前预留这招我认,但它把 lastTs 的语义从「已发号水位」偷换成了「已预留水位」——代码里必须拆成两个变量,否则你前面那套打点告警的幅度数据全是假的。
具体说:持久化的那个 reservedTs = now + margin 只配用来做启动自检;真要判断「停机期间被回拨了多少」,得另外维护一个纯内存的 lastIssuedTs(每次发号推进,不刷盘,丢了无所谓)。混用一个变量的话,正常重启时自检算出来的「回拨量」恒等于 margin 起步,你会把 500ms 的余量当成回拨报出去,告警阈值立刻失去意义。另外 margin 别写死常量,它和刷盘间隔是绑定的——哪天为了降 IO 把刷盘从 100ms 调到 1s,margin 没跟着放大,自检就变成常态化假等待;两个参数要么联动计算,要么放同一个配置项里。
append-only 的读侧还有个细节:启动自检解析最后一行时,别假设它完整。固定长度行或每行带 CRC,解析失败就回退到倒数第二行,而不是直接抛异常起不来。顺带一句,预留文件只追加不清理会一直涨,加个滚动(保留最近 N 条,启动时扫全部取最大 reservedTs)。
StatefulSet + 独立 PVC 我同意,但 metadata.name 尾号推 workerId 的前提是 PVC 不被回收。K8s 1.27+ 才有 persistentVolumeClaimRetentionPolicy,默认缩容后 PVC 保留、扩容复用同一块盘——这正合你意;但如果用了 local PV,Pod 重建跨节点会因为卷亲和性卡 Pending,扩缩容时反而比 Deployment 更难排。这条得看你的 storageClass,不是通用解。
最后接你那句降级:等待窗口期间我倾向于立刻抛可重试异常,不阻塞调用方;同时把这段窗口交给 startupProbe(宽 failureThreshold)去覆盖,liveness 只管死锁和僵死。探针语义分不清,重启循环就是必然的。

正式会员
认证极客










