卡桑德拉数据再次出现

时间:2016-12-19 17:27:45

标签: database cassandra cql cassandra-2.1 nosql

this的启发,我在Cassandra 2.1.4上写了一个简单的互斥锁。

以下是锁定/解锁(伪)代码的外观:

public boolean lock(String uuid){
    try {
        Statement stmt = new SimpleStatement("INSERT INTO LOCK (id) VALUES (?) IF NOT EXISTS", uuid);
        stmt.setConsistencyLevel(ConsistencyLevel.QUORUM);
        ResultSet rs = session.execute(stmt);
        if (rs.wasApplied()) {
            return true;
        }
    } catch (Throwable t) {
        Statement stmt = new SimpleStatement("DELETE FROM LOCK WHERE id = ?", uuid);
        stmt.setConsistencyLevel(ConsistencyLevel.QUORUM);
        session.execute(stmt); // DATA DELETED HERE REAPPEARS!
    }
    return false;
}

public void unlock(String uuid) {
    try {
        Statement stmt = new SimpleStatement("DELETE FROM LOCK WHERE id = ?", uuid);
        stmt.setConsistencyLevel(ConsistencyLevel.QUORUM);
        session.execute(stmt);
    } catch (Throwable t) {
    }
}

现在,我可以随意重新创建一个在高负载测试中在lock()中抛出WriteTimeoutException的情况。这意味着数据may or may not be written。在此之后,我的代码删除了锁 - 并再次抛出WriteTimeoutException。但是,锁仍然存在(或重新出现)

这是为什么?

现在我知道我可以很容易地在这个表上放置一个TTL(对于这个用例),但是如何可靠地删除该行呢?

1 个答案:

答案 0 :(得分:2)

我对看到此代码的猜测是分布式系统编程中常见的错误。假设在失败的情况下,您尝试纠正失败将成功。

在上面的代码中,您检查以确保初始写入成功,但不要确保"回滚"也很成功。这可能会导致各种不需要的状态。

让我们想象一下副本A,B和C的一些场景。

客户端创建Lock但会引发错误。锁存在所有副本上,但客户端会因为该连接丢失或损坏而超时。

系统状态

A[Lock], B[Lock], C[Lock]

我们在客户端上有一个例外,并尝试通过发出删除来撤消锁定,但这会在客户端返回异常时失败。这意味着系统可以处于各种状态。

0成功写入删除

A[Lock], B[Lock], C[Lock] 所有仲裁请求都将看到锁定。没有复制品的组合可以向我们显示Lock已被删除。

1删除成功写入

A[Lock], B[Lock], C[] 在这种情况下,我们仍然很脆弱。任何将C作为仲裁呼叫的一部分而排除的请求都将错过删除。如果仅轮询A和B,我们仍然会看到锁存在。

2/3成功写入删除(Quorum CL Met Met)

A[Lock/], B[], C[] 在这种情况下,我们再次失去了与驱动程序的连接,但在某种程度上成功地在内部复制了删除请求。这些场景是我们实际上安全的唯一场景,未来的读取将不会看到锁定。

结论

在这种情况下,其中一个棘手的问题是,如果你失败了,由于网络不稳定而导致你的锁定正确,你的修正也不太可能成功,因为它必须在完全相同的环境中工作。

这可能是CAS操作有益的一个例子。但在大多数情况下,最好不要尝试使用分配锁定。