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);
什么都没发生。我的意思是:没有抛出异常而且行仍在那里!
其他一些奇怪的东西:
我的环境:
非常感谢任何关于如何解决这个问题的想法/建议; - )
答案 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