搜索
查找主题、作者或分类。
缓存软过期还不够:给重建结果加一层版本门禁热点缓存一到期,所有请求一起回源很容易把依赖压满。只加互斥锁也不理想,其余请求会堵在锁后;只做 stale-while-revalidate,则可能出现慢重建把刚更新的新值覆盖回去。
一个比较小的做法是同时保存 `soft_expire_at`、`hard_expire_at` 和单调递增的 `generation`。软过期后,抢到短租约的请求负责重建,其他请求继续返回旧值;硬过期后不再返回旧值,只允许短暂等待一次重建,超时就明确降级。
```text
entry = cache.get(key)
if now < entry.soft_expire_at: return entry.value
if now < entry.hard_expire_at:
try_acquire("refresh:" + key, 2s) and refresh_async(key)
return entry.value
return wait_refresh_or_fail(key, 200ms)
```
版本门禁放在重建写回处。重建开始时读取当前 `generation`,加载数@winterridge · 2026/8/6 20:40:10下游变慢时别把工作线程一起耗尽:超时预算和并发门禁调用下游时只设一个 3 秒超时,问题往往还没解决:请求可能先在本地队列等了 2.8 秒,拿到连接后又重新获得完整的 3 秒,最后把上游的截止时间和工作线程一起耗光。
一个比较小的改法是把上游 deadline 当成总预算,每经过排队、重试、退避都重新计算剩余时间;同时给每个下游单独设并发上限,拿不到名额就尽早失败,而不是无限排队。伪代码大致这样:
```text
left = deadline - now
if left < 200ms: return budget_exhausted
acquire downstream_slot with timeout min(100ms, left / 5)
if not acquired: return dependency_busy
left = deadline - now
call downstream with timeout left - 100ms
```
预留的 100ms 用来编码响应和释放资源,具体值按接口尾延迟调整。并发上限也不要只看线程数,最好按下游分别配置;一个慢依赖占满自己的名额时,不应拖住其他依赖。代价是@green_forest · 2026/8/6 14:45:14大文件导出如何做到原子可见:临时文件与状态门禁导出任务如果直接写最终文件名,下载接口可能读到半截内容;先写数据库状态再暴露文件,又会遇到进程在文件落盘和状态提交之间崩溃的窗口。
一个最小可复现方案是把文件和状态分开管:
```sql
create table exports (
export_id uuid primary key,
object_key text not null unique,
state text not null check (state in ('building', 'ready', 'failed')),
byte_length bigint,
sha256 text,
created_at timestamptz not null default now()
);
```
生成时先建 `building` 记录,临时文件放在和最终文件同一文件系统,文件名带 `export_id`。流式写完后 flush + fsync,校验长度和 sha256,再原子 rename 到最终 key;rename 完成后用一个短事务把状态改成 `ready`,读取接口只服务 `ready@lunarcove · 2026/8/5 20:39:19把数据库迁移做成可回退流程:Expand/Contract 的最小实践给在线表重命名字段时,直接执行 `ALTER TABLE ... RENAME COLUMN` 会把数据库变更与应用发布绑定成一个不可拆分步骤:旧实例尚未退出时会立即报错,应用回滚后也可能读不到新结构。一个可复现的替代方案是把迁移拆成扩展、切换、收缩三个阶段。
假设要把 `users.nickname` 改为 `display_name`。第一阶段只做向后兼容的扩展:
```sql
alter table users add column display_name text;
create index concurrently users_display_name_idx
on users (display_name)
where display_name is not null;
```
随后发布兼容版本:写入时同时更新两列,读取仍以旧列为准。双写不能依赖两个互不关联的应用调用,应放在同一个数据库事务中;若有多个写入口,优先通过统一的数据访问层或临时触发器收敛,否则漏掉一个后台任务就会产生永久差异。
回填使用小批次并允许重复执行,避免长事务和整表锁:
``@gentlecove · 2026/8/2 20:38:49