用同步中断修复脆弱的dbt测试

2022-02-17 02:42:58

我在事故现场工作。io是伦敦的一家初创公司,它刚刚使用Fivetran进行ETL,使用dbt进行转换,构建了我们的第一个数据堆栈。

虽然我们构建的管道仅供内部使用,但我们很快意识到,Metabase可以为我们的内部产品提供比我们零碎的基本Javascript图形更好的仪表盘。

这很好,但我们在为自己建设时接受了一些妥协,如果我们向客户提供这些数据,我们就无法做到这一点。也就是说,我们的dbt测试是脆弱的,我们不可能发布一个测试经常失败的数据产品。

---版本:2个模型:-名称:行动描述:事件行动列:-名称:组织id描述:";组织ID";测试:-非空-关系:to:ref(';组织';)字段:组织id

该测试让dbt确认行动表中的所有组织id值都出现在组织中。简单地说,我们是不是在加入时搞砸了什么,还是数据看起来不错?

根据我们的测试套件,数据看起来不太好。但只有在某些情况下,测试套件每三次运行一次,我们就会看到如下错误:

测试关系失败_事件_行动_组织_id u参考u组织uu(models/staging/product/stg_product.yml)得到1个结果,如果!=0目标/已编译/分析/模型/登台/产品/stg_产品的完整SQL。yml/relationships_inc_e2d88f3fd5bd723431990564532e121c。sql

这不是最清晰的输出,但可以理解为“actions表中的Organization ID与Organization Stable中的ID不匹配”。

我们使用Fivetran将Postgres数据库中的数据(组织和事件行动的来源)拉入BigQuery数据仓库。

在Postgres land中,您可以期望在所有表中以(基本上)一致的数据视图运行。即使对于单个查询,数据也是以原子方式插入和更新的,因此,如果您在另一个表中找到一个引用了不存在该引用的内容的源,那将是非常奇怪的。

这就引出了一个问题:如果我们从Postgres获取数据,这是一致的,那么这些断裂的关系会带来什么?

虽然Postgres可能是一致的,但由此产生的BigQuery数据仓库却不一致。Fivetran的同步过程可以简化为以下psuedo代码:

每15分钟:用于数据库中的表。所有表格:自上次同步以来的更改。获取自(table.last_Synched)表以来的更改。last_synced=now()仓库。插入(表格,自上次同步以来已更改)

BigQuery不提供跨多个表的一致性,因此我们最终生成了一个“锯齿”数据集,其中每个表在同一时间同步到不同的点。

如果在t0之后,但在t2之前,我们添加了一个组织和一些与之相关的事件操作,那么我们的同步将跳过该组织,但包含这些操作。

这就是我们测试失败的原因,也是它们随机失败的原因(flake):这完全取决于Fivetran何时执行了同步,以及测试是否失败可能遗漏了哪些数据。

在理想情况下,我们的BigQuery仓库中的表包含一致截止的更新,在所有表中应用相同。这将避免我们的关系不协调,并允许我们依靠我们的测试。

首先,我们创建一个dbt模型sync_水印,该模型估计在最后一次Fivetran运行开始之前安全的时间戳。

--模型/同步_水印。sql{config(物化=";表";,)}——这个表标记了我们';我运行dbt。--cutoff用于过滤每个--database表中最近的任何更改,使我们能够确保--dataset中的每个表是一致的,即使同步发生在不同的--period。---当Fivetran每15米尝试同步一次时,选择20米,这应该在<;100万。返回20米可以确保我们在最后一次完整同步开始后安全地切断,这意味着每个表都是一致的。选择时间戳_sub(当前时间戳(),间隔20分钟)作为截止时间

由于我们的Fivetran每15米同步一次,并且每次同步在大约1米的时间内完成,我们知道所有表都将完成一次同步<;2000万年前,届时它将包含截止日期之前和之后的所有数据。

这意味着我们可以将截止应用于所有表,忽略超出该点的任何不一致的同步进度。

请注意,我们已经将这个表具体化,所以在dbt运行开始时只计算一次。这与视图相反,在视图中,每当我们查询表时,current_timestamp()的值都会改变。

然后,对于每个表模型,我们对createdat行应用截止值:

源代码为(从{source(';core#u production#u public';,';organizations';)}中选择*,{{ref(';sync#u水印';)}同步_水印,其中_fivetran _deleted为空,并在<;同步水印。截止时间),重命名为(选择/*…*/从源代码)选择*从重命名

使用ref(';sync#u水印';)这意味着dbt将知道在我们的模型之前构建水印,因为它将跟踪dbt图形中的依赖关系。

我们对其余的数据库表应用相同的模式,确保每个表都有一个一致的截止点。

我们不只是从我们的Postgres数据库同步数据:我们从各种资源中提取数据,比如细分市场或社交媒体,所有这些都可能引用corePostgres资源。

如果我们看到类似的片状测试问题,我们可以在这些ModelsTo上重复使用截止。我们对BigQuery事件表就是这样做的,这些表是从产品实时写入的。

有很多方法可以解决这个问题,但这是简单而快速的,并且如果需要引用或检查数据仓库,可以将中断保存到数据仓库中。

不管你是用这个还是别的什么,避免不可靠的测试都很重要。当我第一次应用中断时,我发现与中断无关的故障是合法的错误,这并不奇怪。

虽然在原型设计时忽略这些失败是正确的决定,但我建议在向客户公开这些数据之前先对其进行分类。

当你有一套可以依赖的测试套件时,生活的压力就更小了!

在Hackernews上讨论这篇帖子。如果你喜欢这篇文章,想看到更多,请点击@lawrjones跟我来。