我有一个聚合在KTable上的拓扑。 这是我创建的一种通用方法,用于根据我拥有的不同主题构建此拓扑。
public static <A, B, C> KTable<C, Set<B>> groupTable(KTable<A, B> table, Function<B, C> getKeyFunction,
Serde<C> keySerde, Serde<B> valueSerde, Serde<Set<B>> aggregatedSerde) {
return table
.groupBy((key, value) -> KeyValue.pair(getKeyFunction.apply(value), value),
Serialized.with(keySerde, valueSerde))
.aggregate(() -> new HashSet<>(), (key, newValue, agg) -> {
agg.remove(newValue);
agg.add(newValue);
return agg;
}, (key, oldValue, agg) -> {
agg.remove(oldValue);
return agg;
}, Materialized.with(keySerde, aggregatedSerde));
}
这在使用Kafka时非常有效,但在通过`TopologyTestDriver`进行测试时则不能。
在两种情况下,当我得到更新时,首先调用subtractor
,然后调用adder
。问题在于,使用TopologyTestDriver
时,会发出两条消息进行更新:一条消息在subtractor
调用之后,另一条消息在adder
调用之后。更不用说在subrtractor
之后和adder
之前发送的消息处于错误的阶段。
还有其他人可以确认这是一个错误吗?我已经针对Kafka 2.0.1版和2.1.0版进行了测试。
编辑:
我在github中创建了一个测试用例来说明问题:https://github.com/mulho/topology-testcase
答案 0 :(得分:2)
预期的行为是有两个输出记录(一个“减”记录和一个“加”记录)。了解它的工作原理有些棘手,所以让我尝试解释一下。
假设您具有以下输入表:
key | value
-----+---------
A | <10,2>
B | <10,3>
C | <11,4>
在KTable#groupBy()
上,提取值的第一部分作为新键(即10
或11
),然后将第二部分相加(即2
, 3
,4
)中的聚合。由于A
和B
记录都具有10
作为新密钥,因此您将求和2+3
,也将求和4
作为新密钥11
。结果表将是:
key | value
-----+---------
10 | 5
11 | 4
现在假定更新记录<B,<11,5>>
将原始输入KTable更改为:
key | value
-----+---------
A | <10,2>
B | <11,5>
C | <11,4>
因此,新结果表应汇总5+4
的{{1}}和11
的{{1}}:
2
如果将第一个结果表与第二个结果表进行比较,您可能会注意到两个行都已更新。从10
中减去旧的 key | value
-----+---------
10 | 2
11 | 9
记录,得到B|<10,3>
,新的10|5
记录被添加到10|2
,从而得到B|<11,5>
。>
这正是您看到的两个输出记录。第一个输出记录(执行减法后)更新第一行(它将减去不再属于聚合结果的旧值),而第二个记录将新值添加到聚合结果中。在我们的示例中,减记录为11|4
,加记录为11|9
(这些记录的格式为<10,<null,<10,3>>>
(请注意,减记录仅设置了{{1} }部分,而添加记录仅设置<11,<<11,5>,null>>
部分。
最后说明:不可能将正负记录放在一起,因为正负记录的键可以不同(在我们的示例<key, <plus,minus>>
和minus
中),因此可能进入不同的分区。这意味着加号和减号操作可能由不同的机器执行,因此不可能只发出包含加号和减号部分的记录。