为可折叠详情添加最大嵌套深度

我建议为内置的 [details] 功能添加一个最大嵌套深度限制,或者引入一个站点设置,允许管理员配置此限制。

问题描述

最近,我论坛上的一个用户创建了一篇包含 24 层嵌套 [details] 部分 的帖子。

这导致了几个问题:

  1. 浏览器性能: 用户报告说,展开多个层级会导致 Firefox 变得极其缓慢或无响应。
  2. Markdown 端点超时: 访问该主题的 .md 端点变得极其缓慢。我们观察到请求耗时分别为 19.8 秒和 31.1 秒。
  3. 服务器错误: Markdown 端点有时返回“Oops”错误页面。Discourse 日志在 Markdown 转换期间也显示了 Pitchfork 工作进程超时警告。
  4. 潜在的资源耗尽: 由于搜索引擎和 AI 爬虫频繁请求 .md 端点,深度嵌套的帖子可能会反复触发昂贵的处理过程并消耗服务器资源。

在删除问题帖子后,Markdown 端点返回 HTTP 200,响应时间约为 1.7 秒。

受影响的主题为:

https://meta.appinn.net/t/topic/87672

(问题回复已被移除。)

建议的改进

我认为以下任一措施都会很有用:

  • 将 [details] 的嵌套限制在合理的深度,例如 2 或 3 层。
  • 添加一个站点设置,例如 details_max_nesting_depth,允许管理员配置最大嵌套深度。

为了向后兼容,该设置的默认值可以设为无限制,同时允许管理员在需要时强制实施限制。

如果用户超过配置的限制,Discourse 可以拒绝该帖子并显示清晰的验证消息。

临时解决方案:一个插件

我创建了一个小插件,将 [details] 的嵌套限制为最多 2 层:

GitHub - scavin/discourse-details-depth-limit · GitHub

该插件在服务器端验证帖子,并拒绝包含超过两层嵌套可折叠部分的帖子。

我已在本地 Discourse 开发环境中成功测试了该插件。

对于遇到类似问题的管理员,在官方解决方案可用之前,此插件可作为一个临时解决方案。

我认为,在内置的 Details 插件中提供可配置的限制,将是防止意外或过度嵌套的有用保障措施。

2 个赞

你好 @scavin,感谢你的报告以及提供的复现细节。

我一直在调查你描述的 Markdown 端点变慢的问题,并在 MarkdownEndpoint::CookedProcessor#replace_details 中发现了一个问题:嵌套的 <details> 元素被反复转换,导致处理时间呈指数级增长。

我已在上游提交了一个包含修复方案的草稿 PR:

在八层嵌套的情况下,该修复将递归转换次数从 255 次减少到 8 次,在我的本地基准测试中实现了约 31 倍的提速。该 PR 已通过 GitHub CI 的初始检查。

同时,我也正在内置的 Details 插件中开发你提议的 details_max_nesting_depth 站点设置,使用 0 表示无限制嵌套,以保留现有行为。初始实现已经完成,但集成测试仍在进行中。

性能修复解决了服务器端的 Markdown 转换问题;可配置的深度限制将提供额外的保护,特别是针对浏览器端的性能。

欢迎你对该提议的设置提供反馈,并告知是将其包含在同一个 PR 中还是单独提交更好。

1 个赞

感谢快速排查并修复!
我认为将它们拆分为两个 PR 会更好,这样性能修复就可以独立审查和合并,而无需等待新配置项。
我也支持可配置的深度限制,默认值设为 0 以保持向后兼容性。

谢谢 @scavin。我能看出将它们分开的好处,尽管这两处更改都针对的是你报告的同一个根本问题,而且现在都已实现并通过 CI 测试。我在所有 PR 上都启用了维护者编辑权限,所以我很乐意遵循 Discourse 团队的偏好,无论是将这两个提交放在一起审查,还是将它们分开。

1 个赞