我们的一位客户在通过管理界面上传本地 Discourse 备份时遇到了一个可复现的问题。
备份上传器使用固定的 5 MB 分块,在足够快的连接下,可能会生成足够多的请求,从而超过 Discourse 自身的应用层请求速率限制。当这种情况发生时,服务器会返回 HTTP 429,但分块上传器不会重试 429 响应,也不会遵守 Retry-After 请求头。结果,整个备份上传都会失败。
非 2xx 响应会被转换为 错误:
if (ev.target.status < 200 || ev.target.status >= 300) {
const error = new Error("Non 2xx");
error.source = ev.target;
reject(error);
return;
}
重试 逻辑如下:
_shouldRetry(err) {
if (err.source && typeof err.source.status === "number") {
const { status } = err.source;
return (
status === 0 ||
status === 409 ||
status === 423 ||
(status >= 500 && status < 600)
);
}
return false;
}
因此,HTTP 429 明确被标记为不可重试。
只要出现一次 429 响应,分块上传的 Promise 就会失败,最终导致整个备份上传触发 upload-error。
此外,也没有对 Retry-After 响应头进行处理。
我找到了一个 2021 年的主题,虽然很快跑题了,但看起来正是同样的问题