在阅读查询过程的代码时,许多操作与Block的处理有关。如果对于Tempo如何将Block进行压缩和删除逻辑不理解,无法理解其中的查询逻辑。所以本节介绍Compactor组件的压缩Block,删除过期Block的过程。
compactionLoop
在Tempo源码的docs目录有一张blocks压缩过程的讲解图:

可以看到Compactor的Block压缩过程分为两步:
Level1将接收到的若干个Block压缩成一个较大的Block。
Level2将较大的Block压缩成一个更大的Block。
doCompaction
根据配置compaction_cycle
(默认30s)周期性执行doCompaction
。
compactorTenantOffset
每次doCompaction
只处理一个租户的Blocks,按照取余之后加一的逻辑挨个处理租户。

TimeWindowBlockSelector
根据配置compaction_window
(默认1小时)确定blocklist中哪些Blocks可以被压缩到一个窗口期,这些可以被压缩到一个窗口期的Block具有相同的hash,该hash由窗口参数w计算而来。

然后根据默认的配置defaultMinInputBlocks
、defaultMaxInputBlocks
,将这个窗口期最少2个,最多4个Block压缩成一个Block。
defaultMinInputBlocks = 2
defaultMaxInputBlocks = 4
除了上述参数,同时max_compaction_objects
(默认600万)、max_block_bytes
(默认100G)。限制被压缩的Block中包含的Trace最大包含的Object(span数量)、最大的空间。
同时满足以上4个条件的一个窗口期的Block将会放在一个chosen中,最终返回需要被压缩的compactBlocks。

compact
获取到toBeCompacted
(需要被压缩的Block数组)后,对toBeCompacted执行
compact`操作,进行压缩。

读取这些Blocks的meta.json
,确保这些Block还存在

对blocks进行压缩,这里压缩逻辑由encoding/v2
的Compact
函数完成,将压缩到一起的Blocks的内容添加至currentBlock
,currentBlock
的Level等于被压缩的Blocks的Level+1。

返回数组newCompactedBlocks
,newCompactedBlocks包括很多个压缩在一起的Blocks的meta信息。

这个时候已经确定了将哪些blocks压缩成更大的Block。
markCompacted
将Blocks打上标记,打上标记的过程其实就是将meta.json
转化为meta.compacted.json
,这个转换是直接对存储中的文件进行操作。

首先在对象存储中将这些老的Blocks的meta.json文件复制为meta.compacted.json
然后删除meta.json
。

更新blocklist
在blocklist里更新租户的最新blocklist,删除已经压缩的blocks,添加最新压缩的newCompactions
。

到这里是Blocks
压缩为CompactedBlocks
的过程,而过期的Blocks和CompactedBlocks在什么时候删除的呢,我们接着看代码。
retentionLoop
根据配置参数blocklist_poll
(默认5m)周期性的执行doRetention
。

根据配置参数retention_concurrency
(默认值10)并发执行对各租户的处理。

retainTenant
根据配置block_retention
(默认1h)保留的数据时长,确定结束时间在cutoff
时间前的blocklist可以被删除。

在执行删除前会对Block再一次进行标记压缩。MarkBlockCompacted
的处理在上面的过程已经进行了讲解,这里就不再重复讲解。

这一步将一些满足条件的blocks在一次压缩成了compactedBlocklist,后面对compactedBlocklist进行清理即可。
ClearBlock
对blocks进行清理,同时更新blockList。

清理Block的操作是对存储中的当前blockID目录下的文件进行删除

到这里就完成了Compactor组件对Blocks进行压缩和删除过程的代码讲解。
虽然Compactor的处理过程代码阅读完了,但是读到这里,还是有三个疑问:
将Blocks压缩为CompactedBlocks后,只是将属性文件在对象存储里进行了更新,那这些CompactedBlocks是什么时候写入到对象存储的呢?Compactor组件的运行过程好像是没有做这个操作的,是否是其他的组件完成这个步骤呢?
将Blocks压缩为CompactedBlocks后,原Block文件目录不会被清理,直到超过保留时间后才会被清理掉,那这个压缩的过程有什么意义呢?
ClearBlock操作只会删除Block里的文件,那当前的Block目录会删除吗?好像也没有看到这个过程,这样的话,租户目录下的Blocks是不是会越来越多,然后会对再加载Blocks的过程有影响呢?
带着上面三个疑问,我觉得还需要将Compactor组件的代码阅读一遍,包括其他的组件代码也要完整阅读,才能有结论。有结论后,可以针对上述三个问题,对Tempo源码进行优化,成为Tempo源码的贡献者。
往期回顾:
Grafana Tempo源码解读(六)Compactor的代码结构和同步Block过程
Grafana Tempo源码解读(五)总结Tempo接收数据的限制以及配置调整
Grafana Tempo源码解读(四)Ingester组件将数据写入持久化存储
Grafana Tempo源码解读(三)Ingester组件接收Trace数据的过程
Grafana Tempo源码解读(二)Distributor对Trace数据的处理和发送至Ingester
Grafana Tempo源码解读(一)Distributor建立监听接收Trace数据




