TT Lab
开始
学习 学习路径 课程

Loki — 不索引日志的日志库

被悄悄截断的结果最危险

在 TT Lab 中继续学习

一句话总结

limit 会悄悄地截断答案,而服务器的限额会让查询被 400 拒绝。毁掉事故调查的,总是悄悄的那一种。

为什么需要它

一个在找事故原因的人,对两小时区间发出了 {app="gateway"} |= "timeout"。结果是 1000 行,他把最早一行的时刻报告为事故开始。后来发现,实际的开始比它还早 40 分钟。

limit 的默认值是 1000,方向是 backward。也就是说,只返回了最近的 1000 行,比这更早的行没有出现在画面上。响应的任何地方都没有“被截断了”的标记。返回的行数恰好等于 limit,这一点是唯一的线索。

工作原理

决定日志查询结果数量的有三样。

旋钮 性质 超过时
请求的 limit 由客户端选择 悄悄被截断
limits_config.max_entries_limit_per_query 服务器定的上限 以 400 拒绝
limits_config.max_query_length 区间长度上限 以 400 拒绝

direction 决定哪些会被截掉。backward(默认)从最近的开始填,所以旧的行被截掉,forward 则相反。找事故的开始时,如果原样使用默认值,被截掉的恰好就是想要找的东西。

limit 悄悄截断的位置。direction=backward 从最近的行开始填,所以区间前面的部分被截掉,报告出的时刻比实际事故开始晚了 40 分钟;改成 direction=forward 后从另一端开始填,事故的开始就进入了结果里

查询还会按 split_queries_by_interval 被切成时间分片并行运行。响应统计中的 splits 就是这个分片数。分片多,结束得更快,但调度器和 querier 会承受更多负载。反过来,分片为 0,说明是作为一个分片运行的。

所以需要扫描很宽的区间时,正确的方法不是把 limit 调大。而是把区间切开,问很多次再合并。如果每个分片的结果数都小于 limit,就能保证那个分片是完整的,合计也就可信。自动化脚本几乎总是应该是这个样子。

提高限额成为答案的情况很少见。把 max_entries_limit_per_query 调大,querier 的内存就会变大,一个人的查询就可能撼动整个集群。限额是防止事故的安全装置,不是阻碍。

在现场相遇的样子

第一,“返回的行数 == limit”时,一律怀疑。如果是自动化,就要明确检查这个条件并发出警告。如果是给人用的仪表板,光是把 limit 写在面板标题里,误解就会大大减少。

第二,找调查开始时刻时,要用 direction=forward。这一个词,就会改变报告里的时间线。

第三,400 响应的正文通常很友好——连超过了哪个限额、超过了多少,都会用数字写出来。如果自动化丢掉这个正文,只在日志里留下状态码,以后找原因时就得手工把同样的查询再发一遍。

下一项实验要做什么

放入跨度两小时的数据,用数字确认 limit 给得小时会悄悄截断。故意越过服务器限额,亲手收到 400 及其正文,区间长度限额也用同样的方法确认。测量 splits 随区间如何变化,最后写一个在限额之内把区间切开、不遗漏地数出两小时数据的脚本。