你的测量管道在流量高峰丢事件,归因数字又从来对不上出价方日志。这是一个症状背后的两处故障:在没人计数的接缝上从未到达的事件,以及到达了但按另一套规则计数的事件。本文讲接缝、键、迟到窗口和对账桥是怎么搭起来的。
一条在流量高峰丢事件、又始终对不上出价方日志的事件管道,可能是一个症状背后的两处故障。一处是传输:事件被创建却从未落地,在一道没人计数的接缝上。另一处是定义:事件落了地,但计数规则与竞价记录不同。
通常的说法——管道在丢事件,所以把管道重建一遍——最多只能修好其中一个。下文把两者分开,并说明一次对账产出的是什么,它不是相等。下面引用的默认值,传输侧取自 Kafka,存储侧取自 ClickHouse。
简短的答案是结构性的:每次竞价一个端到端携带的标识符,每一跳的两侧各一个计数器,以及对一个已经关闭的窗口做的对账。amBrain 可以公开证实的内容是 AdTech 这块:DSP 开发、实时竞价平台与广告交易平台工程。在任何 RTB 技术栈里,出价、胜出和展示记录都来自这一侧。下面这条管道是从问题本身的机理讲起的,不是来自我们的某个案例。
第一种故障是丢失:事件被创建却从未到达,在某一具体的跳上,出于某一具体的原因。从未离开页面的信标、发布中途被重启的边缘节点、被写满的生产者缓冲区、在处理之前就提交了偏移量的消费者。
第二种根本不是故障。测量侧数的是客户端发起的事件;出价方记录的是服务端的竞价结果。一个是授予,另一个是对这次授予后来怎么样的观察。MRC 的测量指南把预取、预渲染和自动刷新当作需要分别检测并披露的事项:这是计数规则,不是传输故障。把两者分开只需要一条查询和一点耐心。
在那条曲线出现之前,争论的双方都只是观点。之后,它的形状决定本文哪一半适用,而这两半并不互斥。
广告事件被创建之后又悄悄不复存在的地方有七处,另外还有一个看起来像保证、其实不是的配置项。
这条规则是报表规则,不是工程规则:一次丢失要么归到一道叫得出名字的接缝上,要么根本没有归属。两个告警能让安静的接缝保持可见——以时间度量、并与保留期对照的消费者 lag,以及一条宁可报错也不跳转的重置策略。
去重键不是在目的端顺手挑的主键。它在所有重试的上游就已经分配好——在竞价发生时,或在事件被创建时——接收端从不自己造一个。到达时间戳不进入键:重试会带来新的到达时间,也就带来新的键。
这个键在每一次尝试中都必须完全相同,这决定了它的组成:广告交易平台或席位、竞价标识符、展示标识符和事件类型。按这个键给 topic 分区,让重试落在一起,按键的顺序也能在重试后保留。这条指示只针对传输层:同一个词用到列式存储上,会变成每个事件一个分区,插入会因为每个块的分区数上限而失败。存储按时间分区,按这个键排序。
所以可行的形态是 at-least-once 传输加幂等键。写入时去重让存储账单保持理智;读取时去重才让数字正确。合并从不把不同分区的 part 合到一起,所以落到下一个分区里的重复,只有在查询发问时才会被解决。
过载之下系统有三个选择:让生产者慢下来、带计数地降载,或者悄悄地丢。只有第三个不可接受,而它正是从没被问过这个问题的代码的默认行为。高峰期的丢失,要么是一条一直涨到内存耗尽的队列,要么是一次在持久化之前就发出的确认。
带标签计数器的丢弃是一个已知量,之后还能对账。没有计数器的丢弃,丢掉的不是数据,而是一个数字。
OpenRTB 的实施指南说得很直接:从广告请求经过竞价、再到渲染和计费的这条序列,本质上不是事务性的。两个计数之间站着太多方。
延迟是预期之内的,而不是例外。竞价请求可以携带展示的过期时间,出价可以携带出价方能容忍的延迟,同一份指南给出的经验值,从网页端的分钟量级,一直到应用内缓存格式和拼接视频的远远更长。
这两个字段都不是契约。指南说得很直白:比出价方声明的过期时间更晚到达的计费通知,仍然可能是可计费的——这是出价方与广告交易平台之间的政策讨论,而不是协议强加的东西。
窗口关闭之后才到达的事件不是管道的缺陷,而是这个媒介的属性。唯一真正的选择是:窗口开着时让数字在公开处变动,还是之后在私下变动。
广告交易平台替换进通知和跟踪 URL 的标识符是:来自竞价请求的竞价标识符、展示标识符,以及在出价方生成过的情况下的出价标识符。这三个单独拿出来都不能当键。
规范说竞价标识符只在广告交易平台内唯一,而不是全局唯一:两个广告交易平台可以在同一天给你同一个字符串。展示标识符只在它自己的竞价请求内唯一,经常就是字面量 1。出价标识符是可选的。
站得住的键是组合键:你经由的广告交易平台或席位,加上竞价标识符,加上展示标识符。在竞价发生时于出价方一侧生成它,任何比它短的东西都只当作前缀,而不是键。
如果信标里没有这些宏,事件级别的对账就不可能,剩下的只有按时间、广告位和创意做匹配。你能改为搭起来的,是一座由六个计数组成的对账桥,每一个都说明自己与上一步不同的原因。
目标是各步骤之间的比值稳定,警报是无法解释的变动。在同一个系统内部,各跳的比值应当等于一,任何偏离都是信号。而跨越竞价到测量的边界时,等于一才是可疑读数。
同样这些计数器,按比值来读,问题就变成了算术:接收数比发送数、生产数比接收数、消费数比生产数、插入数比消费数。一张图上的四个比值,能在任何人打开日志之前说清事件去哪了。再按来源和分区加上生产者侧的序号,一个空洞就从怀疑变成了证据。
有一个问题对账桥回答不了、而合同能回答:你按其中哪个数字付钱。卖方按自己的可计费事件入账收入,买方按自己的那个控速,而指南把长期存在的差距当作双方之间的支持沟通来处理。
提前决定哪个计数是花费的权威记录,以及差距达到多少时,报表里的一条备注要变成向广告交易平台提的工单。上面这些工作换来的是:丢失可归因、重复被如实呈现、对账能逐行解释。它换不来下面这些。
能回答那些披露问题的管道,有一套完整性说法。回答不了的只有一个观点,而观点就是季度末拿来吵架的东西。
值得设防的故障,不是那种缺了一小时数据、引发调查的故障。而是安静的那一种:一道不计数就丢弃的接缝、一次在持久化之前就被确认的插入,以及一次针对仍然开着的窗口做的对账。
amBrain 可以公开证实的内容:amBrain 是一家位于亚美尼亚埃里温的软件工程公司,用 Rust 构建低延迟交易平台、matching engine 和实时竞价系统。amBrain 自 2019 年起做软件开发。重建测量管道不是这里描述的工作。如果数字是在出价方和广告交易平台一侧对不上——也就是出价、胜出和展示记录本身——那才是值得谈的那场对话,而它从收敛曲线开始,不是从重建开始。
带上您当前的架构和让您担心的故障场景,我们用半小时一起梳理。