大约在 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 用户现在使用的是默认情况下使用稀疏包索引的 cargo 版本,它只下载实际使用的包的索引文件。换句话说:只有这个图标 crate 的用户需要为下载所有元数据付出代价。另一方面,这意味着仍然使用基于 git 的索引的用户都为这个使用 23,000 个特性的 crate 付出了代价。
那么,我们下一步该怎么做?🤔
虽然我们认为支持如此大量的特性在概念上是一个有效的请求,但在 crates.io 和 cargo 的当前实现细节中,我们无法支持这一点。在分析了单个 crate 具有如此多特性带来的所有这些下游影响后,我们意识到我们需要在 crates.io 上进行某种形式的限制,以防止系统崩溃。
现在是重要部分:2023 年 10 月 16 日,crates.io 团队部署了一项更改,将 crate 可以拥有的特性数量限制为 300,适用于任何新发布的 crate/版本。
……暂时,或者至少在我们找到上述问题的解决方案之前。
我们知道一些 crate 也具有拥有超过 300 个特性的正当理由,我们已经为它们提供了适当的例外,但我们希望每个人都能注意我们当前系统的这些限制。
我们还邀请大家参与寻找上述问题的解决方案。讨论想法的最佳场所是 crates.io Zulip 流,一旦某个想法更加完善,它将被转化为一个 RFC。
最后,我们要感谢 Charles Edward Gagnon 使我们意识到这个问题。我们还想重申,作者和他们的 crate 并不对此负责。在开发 crate 时很难了解这些 crates.io 实现细节,因此,如果有什么责任,那就是我们,crates.io 团队,因为没有早点对这方面进行限制。无论如何,我们现在有了限制,现在你们都知道原因了!👋