
bug: eval preview tables run unbounded full-history ClickHouse queries, can exhaust memory
快速结论:在 Langfuse 的 eval 预览表格(ObservationsTable、TracesTable、EventsTable)中,当传入 externalFilterState 时,会覆盖掉包含 dateRangeFilter 的 combinedFilterState,导致 ClickHouse 查询缺少时间范围限制,扫描整个项目历史数据,可能引发 OOM。优先检查 web/src/components/table/use-cases/ 下的 externalFilterState || combinedFilterState 逻辑。
问题场景
用户在自部署的 Langfuse 实例中,进入 Evaluators → Set up evaluator,选择 Observations 或 Traces 作为评估目标时,预览表格的 generations.all / generations.countAll tRPC 请求缺少 startTime >= 过滤器,导致 ClickHouse 查询全量历史数据,引发内存溢出。问题存在于 v3.174.1 及更新的主分支版本。
报错原文
Code: 241. DB::Exception: (total) memory limit exceeded: would use 15.41 GiB
(attempt to allocate chunk of 6523591 bytes), current RSS: 18.92 GiB, maximum: 18.90 GiB.
OvercommitTracker decision: Query was selected to stop by OvercommitTracker:
While executing MergingSortedTransform. (MEMORY_LIMIT_EXCEEDED)
(comment: {"kind":"rows","feature":"tracing","type":"observation",
"operation_name":"getObservationsTableInternal"})
原因分析
问题根因在于三个表格组件(ObservationsTable、TracesTable、EventsTable)都使用了相同的 filter state 覆盖模式:const filterState = externalFilterState || combinedFilterState;。当 eval 预览通过 externalFilterState 传入外部过滤状态时,该逻辑会用 externalFilterState 完全替换掉 combinedFilterState,而 combinedFilterState 中包含了默认的 24 小时时间范围过滤器(dateRangeFilter)。尽管 eval 预览同时传入了 externalDateRange={last1Day},但这个独立的 prop 并未被合并到生效的 filter state 中,因此 ClickHouse 查询没有时间范围限制,导致扫描整个项目历史。
环境排查
- Langfuse 版本:v3.174.1(以及当前 main 分支)
- ClickHouse 版本:未明确指定,但需确保内存配置足够支撑大项目查询
- 项目规模:如果 observation/trace 数据量超过数万条,更容易触发此问题
- 受影响组件路径:
web/src/components/table/use-cases/traces.tsx(约 L346)、web/src/components/table/use-cases/observations.tsx(约 L467)、web/src/features/events/components/EventsTable.tsx(约 L420)
解决步骤
- 定位并修改 filter state 合并逻辑:在 ObservableTable、TracesTable 和 EventsTable 组件中,将
const filterState = externalFilterState || combinedFilterState;改为合并方式,例如:const filterState = externalFilterState ? [...externalFilterState, ...dateRangeFilter] : combinedFilterState;。注意dateRangeFilter需要从组件现有的时间范围逻辑中提取。 - 或者修改 eval 预览调用方:在
web/src/features/evals/components/inner-evaluator-form.tsx(约 L187-L193)中,手动将时间范围过滤条件包含到externalFilterState中再传入表格组件。 - 协调并行工作:注意 PR #14939 正在重构相同的 filter state 逻辑,如果该 PR 尚未合并,建议在其基础上进行修改以避免冲突。
- 部署修复后重启后端服务:确保修改后的代码已部署并重启相关服务。
验证方法
在拥有大量 observation/trace 历史数据的自部署项目中,进入 Evaluators → Set up evaluator,预览表格的 generations.all / generations.countAll tRPC 请求中应包含有效的 startTime >= 过滤器。同时检查 ClickHouse 日志,确认不再出现因全量扫描导致的 MEMORY_LIMIT_EXCEEDED 错误。对于约 70,854 条 observation 的项目,24 小时限制的查询应只读取约 5.1K 行 / 386 KiB 内存,而非全表的 78.98K 行 / 22.14 MiB。


