在 2023 年 10 月中旬左右,crates.io 团队被一位用户通知,他们 crate 的一个 shields.io 徽章停止工作。问题报告者非常友善地已经调试了问题,并发现 shields.io 发送到 crates.io 的 API 请求很可能是问题所在。以下是原始问题中的引述:
这个 crate 大量使用特性标志,这会膨胀 API 的响应负载。
显然,这个特定 crate 的 API 响应已经突破了 20 MB 的大关,而 shields.io 对此并不满意。有趣的是,这个 crate 在那时只发布了 9 个版本。但是,你如何在仅发布 9 个版本的情况下达到 20 MB 呢?
正如上面的引述已经提到的,这个 crate 使用了很多特性……非常多的特性……几乎 23,000 个!😱
哪个 crate 需要这么多特性?好吧,这个 crate 为基于 Rust 的 Web 应用程序提供 SVG 图标……它为每个图标使用一个特性,以便最终 WebAssembly 包的负载大小保持较小。
乍一看,这应该没有问题。从 crate 作者的角度来看,这似乎是一件合理的事情,并且 cargo 和 crates.io 都没有显示任何关于此的警告。不幸的是,一些内部机制对如此大量的特性并不太满意…
crate 作者已经发现的第一个问题是:来自 crates.io 的 API 响应变得非常大。雪上加霜的是,crates.io API 目前没有对已发布版本的列表进行分页。更改这一点显然是一个破坏性更改,因此我们的团队一直有点不情愿在这方面更改 API 的行为,尽管这种情况表明我们可能需要在不久的将来解决这个问题。
下一个问题是,这个 crate 的 索引文件也变得很大。发布了 9 个版本后,它已经包含 11 MB 的数据。就像 crates.io API 一样,当前软件包索引文件格式中也没有内置分页。
现在你可能会问,为什么软件包索引和 cargo 需要了解特性?好吧,简单的答案是:为了依赖项解析。特性可以启用可选的依赖项,因此当使用依赖项特性时,它可能会影响依赖项解析。我们最初的想法是,我们至少可以从索引文件中删除所有空的特性声明(例如 foo = []
),但 cargo 团队告知我们,cargo 也依赖于它们的存在,因此出于向后兼容性的原因,这不是一个选择。
好的一面是,现在大多数 Rust 用户都默认使用稀疏包索引,它只下载实际正在使用的包的索引文件。换句话说:只有这个图标 crate 的用户才需要为下载所有元数据付出代价。另一方面,这意味着仍然使用基于 git 的索引的用户都在为这个使用了 23,000 个特性的 crate 付出代价。
那么,我们接下来该怎么办?🤔
虽然我们认为支持如此大量的特性在概念上是有效的请求,但由于 crates.io 和 cargo 当前的实现细节,我们无法支持这一点。在分析了单个 crate 拥有如此多特性所产生的全部下游影响后,我们意识到我们需要对 crates.io 施加某种形式的限制,以防止系统崩溃。
现在到了重要的部分:**在 2023-10-16,crates.io 团队部署了一项更改,将一个 crate 可以拥有的特性数量限制为 300,适用于任何新发布的 crate/版本。**
……暂时如此,或者至少在我们找到上述问题的解决方案之前。
我们知道一些 crate 也有合理的理由拥有 300 个以上的特性,我们已经为此类 crate 授予了适当的例外,但我们希望大家注意我们当前系统的这些限制。
我们也邀请大家参与寻找上述问题的解决方案。讨论想法的最佳场所是 crates.io Zulip 流,一旦一个想法更加成熟,它将被转化为一个 RFC。
最后,我们要感谢 Charles Edward Gagnon 让我们意识到了这个问题。我们还要重申,作者和他们的 crate 不应为此受到指责。在开发 crate 时很难知道这些 crates.io 的实现细节,所以如果有任何错误,那也应该归咎于我们 crates.io 团队,因为我们没有早点限制这一点。无论如何,我们现在有了这些限制,现在大家都知道原因了!👋