Cassandra 数据重现

Cassandra data reappearing

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

下面是 lock/unlock(伪)代码的样子:

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。但是,锁定仍然存在(或重新出现)

这是为什么?

现在我知道我可以轻松地在此 table 上设置 TTL(对于此用例),但我如何可靠地删除该行?

我看到这段代码的猜测是分布式系统编程中发生的常见错误。假设万一失败,您纠正失败的尝试将会成功。

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


让我们想象几个副本 A、B 和 C 的场景。

客户端创建锁但抛出错误。锁存在于所有副本上,但客户端超时,因为连接丢失或中断。

系统状态

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

我们在客户端发生异常,并尝试通过发出删除来撤消锁定,但失败并在客户端返回异常。这意味着系统可以处于多种状态。

0 次成功写入删除

A[Lock], B[Lock], C[Lock] 所有仲裁请求都会看到锁。不存在可以向我们显示锁已被移除的副本组合。

1 次成功写入删除

A[Lock], B[Lock], C[] 在这种情况下,我们仍然很脆弱。任何将 C 排除在仲裁调用之外的请求都将错过删除。如果只有 A 和 B 被轮询,那么我们仍然会看到锁存在。

2/3 成功写入删除(满足仲裁 CL)

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


结论

在这种情况下,棘手的事情之一是,如果您由于网络不稳定而无法正确锁定,那么您的更正也不太可能成功,因为它必须在完全相同的环境中工作。

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