Cassandra LWT 写入产生不正确的数据

Cassandra LWT writes producing incorrect data

我们有一个 3 节点 Cassandra 集群(单个 DC),以及一个 table 应用程序 运行 在单独的节点上访问(对于 read/writes)。

在重负载下,当应用程序的两个实例都试图更新相同的用户条目(例如添加属性)时,我们观察到来自 app1 的更新(运行 on node1成功 - 即 ResultSet#wasApplied returns true)。但是,当 app2(在 node2 上读取数据时,它在 app1 更新之前获取过时数据)。

我想知道为什么会这样,因为具有串行一致性的 LTW 应该可以防止这种类型的不一致。非常感谢任何帮助!

提前致谢!

示例:(基于应用程序日志)

  1. 最初用户的属性 A 的值为 1
  2. app1app2 都在添加新属性; app1 添加 B:2app2 添加 C:3
  3. app1 将正确的数据读入内存,添加新属性 B 并成功写入 Cassandra。日志显示具有 A:1B:2 元组的最终属性列表。
  4. app2 读取数据,但只看到 A:1app1app2 的日志之间的时间差只有 2ms;因此可能发生以任何顺序)。
  5. 一旦app2的写入完成,结束状态只有A:1C:2,这是不正确的。

更多信息

对 table 的(准备好的语句)读写具有以下特征:

table 看起来像:

CREATE TABLE my_table(userid ascii, username ascii, attributes map);

更新属性映射时应用内的逻辑是:

User user = dao.getUser(userid);
user.addNewAttribute("key", "value");
dao.update(user);

因为 read/writes 发生在 2 个不同的节点上,我们没有在应用程序级别同步操作。

app2app1 开始写入之前正在执行 read。因此,app1 得到了旧数据(不是旧数据)。 update 操作设置整个地图而不是 add/removing 元素 to/from 地图。

我们更改了逻辑,因此 add/remove 操作现在执行添加(并分别删除)而不是执行 "read and then set"。

新准备好的语句类似于:

QueryBuilder.update("my_table")
            .onlyIf(QueryBuilder.eq("username", QueryBuilder.bindMarker()))
            .with(QueryBuilder.putAll("attributes", QueryBuilder.bindMarker()))
            .and(QueryBuilder.set("lastUpdatedOn", QueryBuilder.bindMarker()))
            .where(QueryBuilder.eq("userid", QueryBuilder.bindMarker()));

在使用 set

设置属性之前