博客/工程

如何固定一个重复计算普罗米修斯错误而做Grafana云的项目

2021年8月19日 6分钟

在我作为一个软件工程师在Grafana实验室,我最近做的一个项目,涉及生成bob电竞频道PromQL查询。我们验证了正确性的一个方法生成的查询与一套集成测试。这些测试将执行生成的普罗米修斯的PromQL查询本地实例查询引擎和一些测试数据,并验证结果。

集成测试是非常有用的,帮助我们找到问题PromQL查询生成器,并且,在一个案例中,一个错误普罗米修斯。在这篇文章中,我将讨论如何发现错误,是什么导致了它,和修复。

看到双

添加PromQL查询生成器的功能之后,我决定成为一个负责任的软件工程师和添加集成测试来验证新的行为。

或者,至少,我试着出现。

出于某种原因,我写的一个新测试不通过。查询生成器输出预期PromQL查询。然而,它似乎有一个问题与普罗米修斯本身:在评估生成的查询,返回不正确的结果。

测试在做类似如下:

  • 创建一个名为foo时间序列的样本值(5、10、15、20、25)每隔30秒。
  • 运行PromQL查询sum_over_time (foo [30])60秒(即一个步骤。,每隔60秒,总结样本值在最后30秒)。

因此,我们预期的结果是:

  • 在0 5 0(单样本值1)
  • 25在60年代(样本在30年代价值10 +样品在60年代价值15)
  • 45在120年代与价值20 +样品(样品在90年代在120年代价值25)

然而,实际结果在60年代是35,而不是25。

尝试不同的样本值测试失败,我开始注意到一个模式。

例如,使用样本值(0,1,100,1000]在30秒的间隔和加载与前面的示例相同的查询和步骤,结果在60秒12岁而不是11。

本系列的第二个样品被重复计算。对于这个示例,样品在30年代(1)价值是重复计算,导致结果在60年代是1 + 1 + 10 = 12,而不是1 + 10 = 11。

回到最初的例子中,样品在30年代(10)与价值是重复计算,导致结果在60年代是10 + 10 + 15 = 35个,而不是10 + 15 = 25。

寻求答案

现在,我有一个粗略的问题,是时候仔细看看发生了什么事在查询评估。配备可再生失败的测试用例,一个调试器,相关的背景音乐,我跳进了普罗米修斯的代码库。

计算sum_over_time ()系列,普罗米修斯查询引擎首先需要提取原始样本在相关时间范围。它通过使用一个迭代器,可以为一系列timestamp-increasing顺序遍历样本。查询引擎所使用的两个迭代器的方法是:寻求(),提出了迭代器第一个示例等于或大于一个指定的时间戳,和Next (),移动到下一个样品。

有多个迭代器实现。的memSafeIterator用于查询样品在内存中。它包括:1)内部迭代器读取已经存储在一个压缩格式(样本xorIterator),2)缓冲存储过去四个样品在一个未压缩的格式。如果需要过去四个样品,memSafeIterator从缓冲区读取。否则,它刚从其内部迭代器返回的结果。

在我们的测试失败,memSafeIterator是被其内部迭代器不同步。当调用Next ()由一个样品,两个迭代器将推动。然而,当调用寻求(),只有内部迭代器会更新当前的样本。这意味着memSafeIterator没有正确读取缓冲区。

解释清楚,让我们经历(简化)介绍迭代器的错误导致集成测试失败。

开始时,内部的xorIteratormemSafeIterator在0指着最初的样本。评估sum_over_time ()查询在0年代,样本值在0 s是阅读。在缓冲区,因为样本不是内部迭代器的结果(即使用。5)。

然后在60年代查询评估。这意味着所有的样本值从30年代到60年代是总结。的寻求()方法被调用时搬到最早的样品,时间范围(在这种情况下,样本在30年代)。错误,只有内部迭代器更新目前的30年代的一个示例。memSafeIterator仍为0的样本。

memSafeIterator并不认为它已经达到了缓冲区,所以当阅读当前示例,它使用从内部迭代器,而不是结果。内部迭代器返回10作为第一个值在时间范围。

结束的时间范围内没有到达,Next ()就是下面的示例。这将导致memSafeIterator推进在30年代的示例。它检测已达到缓冲区,因此停止阅读从内部迭代器。相反,30年代的样本值再次阅读,但这一次的缓冲区。

Next ()又被称为,推进memSafeIterator最后一个示例在当前时间范围,60年代的样本,它的值为15。

总结收集到的样本值(10 + 10 + 15),我们得到了35的错误的答案。

修复

memSafeIterator是一个结构体。其内部迭代器是一种嵌入式领域。这意味着如果一个方法没有定义memSafeIterator但内部迭代器时,将调用内部迭代器的方法。

在这种情况下,memSafeIterator没有寻求()方法,尽管其内部迭代器,因此调用memSafeIterator.Seek ()才导致内部迭代器执行它寻求()方法,推进当前的样本。

完成调查,我添加了一个寻求()方法memSafeIterator更新当前样本正确,提交修复普罗米修斯,最后,检查我们的集成测试都成功。

结论

自从我加入Grafana实验室bob电竞频道9个月前,我曾在度量摄入和查询翻译,了解了一些监测系统,并开始开源贡献。的重复计算普罗米修斯错误只是一个例子,我遇到一个有趣的问题。如果这个工作听起来很有趣,我们正在招聘!


在这一页上
滚动的更多