无法删除以前使用 Java 在 Cassandra 中使用更新插入创建的行
Can't delete a row previously created with an upsert in Cassandra using Java
TL;DR 是我无法删除以前使用 Java.
的更新插入创建的行
基本上我有这样一个 table:
CREATE TABLE transactions (
key text PRIMARY KEY,
created_at timestamp
);
然后我执行:
String sql = "update transactions set created_at = toTimestamp(now()) where key = 'test' if created_at = null";
session.execute(sql)
如预期的那样创建了行:
cqlsh:thingleme> SELECT * FROM transactions ;
key | created_at
------+---------------------------------
test | 2018-01-30 16:35:16.663000+0000
但是(这让我发疯)如果我执行:
sql = "delete from transactions where key = 'test'";
ResultSet resultSet = session.execute(sql);
没有任何反应。我的意思是:没有抛出异常并且该行仍然存在!
其他一些奇怪的东西:
- 如果我用普通插入替换更新插入,则删除有效
- 如果我使用 cqlsh 直接 运行 sql 代码(更新和删除),它可以工作
- 如果我 运行 此代码针对 EmbeddedCassandraService,它可以工作(这非常糟糕,因为我的集成测试只是绿色!)
我的环境:
- 卡桑德拉:3.11.1
- datastax java 驱动程序:3.4.0
- docker 图片:cassandra:3.11.1
任何关于如何解决这个问题的 idea/suggestion 非常感谢;-)
我认为您遇到的问题可能是轻量级事务 (LWT) (update transactions set created_at = toTimestamp(now()) where key = 'test' if created_at = null
) 和非 LWT (delete from transactions where key = 'test'
) 混合使用造成的。
Cassandra 使用时间戳来确定哪些变更(删除、更新)是最近应用的。使用 LWT 时,时间戳分配与不使用 LWT 时不同:
Lightweight transactions will block other lightweight transactions from occurring, but will not stop normal read and write operations from occurring. Lightweight transactions use a timestamping mechanism different than for normal operations and mixing LWTs and normal operations can result in errors. If lightweight transactions are used to write to a row within a partition, only lightweight transactions for both read and write operations should be used.
来源:How do I accomplish lightweight transactions with linearizable consistency?
更复杂的是,默认情况下,java 驱动程序使用客户端时间戳,这意味着写入时间戳由客户端而不是协调的 cassandra 节点确定。但是,当您使用 LWT 时,客户端时间戳将被绕过。在您的情况下,除非您禁用客户端时间戳,否则您的非 LWT 查询将使用客户端时间戳,而您的 LWT 查询将使用由 cassandra 中的 paxos 逻辑分配的时间戳。在任何情况下,即使驱动程序没有分配客户端时间戳,这仍然可能是一个问题,因为时间戳分配逻辑在 C* 端对于 LWT 和非 LWT 也是不同的。
要解决此问题,您可以更改 delete
语句以包含 IF EXISTS
,即:
delete from transactions where key = 'test' if exists
TL;DR 是我无法删除以前使用 Java.
的更新插入创建的行基本上我有这样一个 table:
CREATE TABLE transactions (
key text PRIMARY KEY,
created_at timestamp
);
然后我执行:
String sql = "update transactions set created_at = toTimestamp(now()) where key = 'test' if created_at = null";
session.execute(sql)
如预期的那样创建了行:
cqlsh:thingleme> SELECT * FROM transactions ;
key | created_at
------+---------------------------------
test | 2018-01-30 16:35:16.663000+0000
但是(这让我发疯)如果我执行:
sql = "delete from transactions where key = 'test'";
ResultSet resultSet = session.execute(sql);
没有任何反应。我的意思是:没有抛出异常并且该行仍然存在!
其他一些奇怪的东西:
- 如果我用普通插入替换更新插入,则删除有效
- 如果我使用 cqlsh 直接 运行 sql 代码(更新和删除),它可以工作
- 如果我 运行 此代码针对 EmbeddedCassandraService,它可以工作(这非常糟糕,因为我的集成测试只是绿色!)
我的环境:
- 卡桑德拉:3.11.1
- datastax java 驱动程序:3.4.0
- docker 图片:cassandra:3.11.1
任何关于如何解决这个问题的 idea/suggestion 非常感谢;-)
我认为您遇到的问题可能是轻量级事务 (LWT) (update transactions set created_at = toTimestamp(now()) where key = 'test' if created_at = null
) 和非 LWT (delete from transactions where key = 'test'
) 混合使用造成的。
Cassandra 使用时间戳来确定哪些变更(删除、更新)是最近应用的。使用 LWT 时,时间戳分配与不使用 LWT 时不同:
Lightweight transactions will block other lightweight transactions from occurring, but will not stop normal read and write operations from occurring. Lightweight transactions use a timestamping mechanism different than for normal operations and mixing LWTs and normal operations can result in errors. If lightweight transactions are used to write to a row within a partition, only lightweight transactions for both read and write operations should be used.
来源:How do I accomplish lightweight transactions with linearizable consistency?
更复杂的是,默认情况下,java 驱动程序使用客户端时间戳,这意味着写入时间戳由客户端而不是协调的 cassandra 节点确定。但是,当您使用 LWT 时,客户端时间戳将被绕过。在您的情况下,除非您禁用客户端时间戳,否则您的非 LWT 查询将使用客户端时间戳,而您的 LWT 查询将使用由 cassandra 中的 paxos 逻辑分配的时间戳。在任何情况下,即使驱动程序没有分配客户端时间戳,这仍然可能是一个问题,因为时间戳分配逻辑在 C* 端对于 LWT 和非 LWT 也是不同的。
要解决此问题,您可以更改 delete
语句以包含 IF EXISTS
,即:
delete from transactions where key = 'test' if exists