无法使用Java删除以前在Cassandra中使用upsert创建的行

时间:2018-01-30 17:06:10

标签: java cassandra datastax-java-driver

TL; DR是我无法使用Java删除先前使用upsert创建的行。

基本上我有一张这样的表:

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);

什么都没发生。我的意思是:没有抛出异常而且行仍在那里!

其他一些奇怪的东西:

  • 如果我用普通插页替换upsert,那么删除工作
  • 如果我使用cqlsh直接运行sql代码(更新和删除),它可以正常运行
  • 如果我针对EmbeddedCassandraService运行此代码,它可以工作(这非常糟糕,因为我的集成测试只是绿色!)

我的环境:

  • cassandra:3.11.1
  • datastax java driver:3.4.0
  • docker image:cassandra:3.11.1

非常感谢任何关于如何解决这个问题的想法/建议; - )

1 个答案:

答案 0 :(得分:6)

我认为您遇到的问题可能是通过轻量级交易(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时不同:

  

轻量级事务将阻止发生其他轻量级事务,但不会阻止正常的读写操作发生。 轻量级事务使用与正常操作不同的时间戳机制,混合LWT和正常操作可能导致错误。如果使用轻量级事务来写入分区中的行,则只应使用读取和写入操作的轻量级事务。

来源:How do I accomplish lightweight transactions with linearizable consistency?

更复杂的是,默认情况下,java驱动程序使用客户端时间戳,这意味着写时间戳由客户端而不是协调cassandra节点确定。但是,使用LWT时,会绕过客户端时间戳。在您的情况下,除非您禁用客户端时间戳,否则您的非LWT查询正在使用客户端时间戳,其中您的LWT查询使用cassandra中的paxos逻辑分配的时间戳。在任何情况下,即使驱动程序没有分配客户端时间戳,这仍然可能是一个问题,因为LWT和非LWT的C *端的时间戳分配逻辑也不同。

要解决此问题,您可以更改delete声明以包含IF EXISTS,即:

delete from transactions where key = 'test' if exists

Similar issue from the java driver mailing list