补丁迟迟不能合入,问题一定出在最终提交环节吗?Jimmy Angelakos 与 Andreas Scherbaum 在 PGConf.dev 2026 分享的 Commitfest 分析,给出了一个值得开发者注意的答案:在他们观察的数据里,更明显的等待发生在评审之前,而不是补丁已经准备好、只差提交的时候。
这场分析覆盖 2014 年 12 月至 2026 年 4 月的 58 次 Commitfest、6,678 个补丁和 899 位作者。它不是给贡献者或 committer 打分,也不是对 PostgreSQL 开发流程作故障诊断,而是把可观察的数字摆出来,让社区讨论。
补丁入口扩大,合入工作仍然集中
2015 年,Commitfest 记录了来自 125 位作者的 418 个补丁;2025 年,这两个数字分别达到 272 位作者和 885 个补丁。贡献规模大约翻了一倍。
但分析期内,总共只有 38 位不同的 committer 出现在记录中,每年实际执行提交的人数中位数为 26.5。前五位 committer 完成了 55% 的提交,前十位完成了 74%。
这说明工作集中,但不能直接说明谁效率低。Committer 提供的是高度专业化的时间和判断;人数也不等于可用工时。不过,补丁入口持续扩大而合入侧人数没有同步增长,确实提出了一个容量问题:新增工作由哪个环节吸收?
已合入补丁从提交到合入的耗时,可以用三个数字描述:
| 指标 | 耗时 |
|---|---|
| 中位数 | 80 天 |
| 第 90 百分位 | 256 天 |
| 第 99 百分位 | 711 天 |
作者使用中位数和百分位,而不是只看平均值,是为了避免少数超长等待的补丁主导结果。还有一个重要边界:这些耗时只描述最终合入的补丁,不代表所有提交的补丁。
真正显眼的队列:还没有人认领评审
直觉上,瓶颈可能是 Ready for Committer:补丁已经审完,只等有权限的人合入。然而测量时,处于这个状态的 54 个补丁,其状态停留时间中位数只有 3 天,第 90 百分位为 30 天。
相比之下,331 个活跃补丁里,有 196 个没有登记 reviewer,占 59.2%。这组补丁的等待时间中位数为 54 天,第 90 百分位为 244 天,最长达到 892 天。
历史数据也呈现类似现象:处于 Needs review 的补丁中,72.1% 没有分配 reviewer;已经获得 reviewer 的补丁中,72% 只有一位。
因此,至少在 Commitfest 的可见范围内,评审认领比最终提交等待更值得优先检查。但“没有登记 reviewer”不能自动等同于“完全没有发生评审”:邮件列表上的讨论可能没有进入应用记录。
贡献者回访也值得关注。2015 年首次提交补丁的作者中,73% 后来再次提交;2024 年的对应比例为 37%。整个样本中,42.6% 的作者只提交过一个补丁。
这些数字不能直接证明等待导致了流失。不同年份的作者拥有不同长度的后续观察窗口,2025 和 2026 年尤其不适合过早判断。它们更适合作为进一步调查的线索,而不是因果结论。
可以这样实践:给自己的评审队列做一次体检
下面不是 Commitfest 官方接口,也不复现演讲的完整统计方法。它是一个最小实践:假设你能把项目的补丁队列整理成 CSV,用 Python 标准库检查无人认领的活跃补丁。
运行前,把示例行替换为自己的数据。submitted_at 使用 YYYY-MM-DD;多个 reviewer 用分号分隔;active 用 1 表示仍在处理。示例计算的是“提交至快照日期的年龄”,不是准确的状态停留时间。要测量后者,需要状态变更历史。
cat > patches.csv <<'CSV'
id,submitted_at,status,reviewers,active
101,2026-01-10,Needs review,,1
102,2026-03-01,Needs review,alice,1
103,2026-03-20,Ready for Committer,bob;carol,1
104,2025-12-01,Committed,alice,0
CSV
cat > queue_metrics.py <<'PY'
import csv
import math
import sys
from datetime import date
from statistics import median
path = sys.argv[1]
snapshot = date.fromisoformat(sys.argv[2])
with open(path, newline='', encoding='utf-8') as f:
rows = list(csv.DictReader(f))
active = [r for r in rows if r['active'].strip() == '1']
unassigned = [r for r in active if not r['reviewers'].strip()]
ages = sorted(
(snapshot - date.fromisoformat(r['submitted_at'])).days
for r in unassigned
)
if any(age < 0 for age in ages):
raise SystemExit('Snapshot precedes a patch submission date')
print(f'Active patches: {len(active)}')
if active:
print(f'Without reviewer: {len(unassigned) / len(active):.1%}')
if ages:
p90 = ages[math.ceil(0.90 * len(ages)) - 1]
print(f'Unassigned age median: {median(ages):g} days')
print(f'Unassigned age P90: {p90} days (nearest rank)')
print(f'Oldest unassigned: {max(ages)} days')
else:
print('No active patches without a reviewer')
PY
python3 queue_metrics.py patches.csv 2026-04-01
这个脚本的价值不是生成一张漂亮报表,而是让团队回答三个具体问题:哪些补丁没有负责人?哪些等待已经足够久,需要主动联系作者?哪些条目应该明确结束,而不是继续滚动?
若要进一步分析,建议记录状态进入时间、reviewer 认领时间,以及补丁条目、邮件线程和提交 ID 之间的关联。否则,“没人评审”可能只是记录缺失,“等待十年”也可能混合了长期不活跃与真实处理时间。
把指标当作路标,不要当作判决书
这份分析最大的限制是覆盖范围。作者指出,2021—2026 年的 git commits 中,只有约 17% 出现在某次 Commitfest 中;约 80% 的补丁只存在于 pgsql-hackers 邮件列表。两者统计对象不同,不能简单拼成一个完整漏斗。
Commitfest 也没有提供提交 ID、邮件线程和条目之间的完整交叉引用。因此,这是一扇观察窗口,而不是 PostgreSQL 开发活动的全景。
演讲刻意没有把观察包装成修复方案。文章随后提出的温和建议,则包括让正式关闭更容易、让 reviewer 路由更明确,以及把分诊视为一项有名字的工作。这些方向可以在其他项目里小规模试验:
- 明确分诊责任:指定轮值人员检查新补丁,而不是默认“总有人会看”。
- 显式匹配评审者:按模块、经验和当前负载邀请 reviewer,避免只填一个名字却没有实际认领。
- 给长期条目一个结论:明确关闭理由,并说明什么条件下可以重新提交。
- 同时看速度与质量:等待缩短不应以草率评审、维护者过载或技术标准下降为代价。
PGConf.dev 的特点也呼应了这个问题:2026 年的会议安排了现场 Commitfest、社区答疑和 unconference,让开发者能直接参与工作,而不只是听报告。对希望参与 PostgreSQL 的人来说,贡献不只有写补丁这一条路;帮助补丁获得可执行的反馈,同样是在推进项目。