stateIs0 / lu-raft-kv

this is raft java project. raft-kv-storage
https://thinkinjava.cn/2019/01/12/2019/2019-01-12-lu-raft-kv/
Apache License 2.0
771 stars 211 forks source link

volatile多线程并不安全 #15

Open fsan42 opened 1 year ago

fsan42 commented 1 year ago
if (N > commitIndex) {
    LogEntry entry = logModule.read(N);
    if (entry != null && entry.getTerm() == currentTerm) {
        commitIndex = N;
    }
}

//  响应客户端(成功一半)
if (success.get() >= (count / 2)) {
    // 更新
    commitIndex = logEntry.getIndex();
    //  应用到状态机
    getStateMachine().apply(logEntry);
    lastApplied = commitIndex;

    log.info("success apply local state machine,  logEntry info : {}", logEntry);
    // 返回成功.
    return ClientKVAck.ok();
} else {
    // 回滚已经提交的日志.
    logModule.removeOnStartIndex(logEntry.getIndex());
    log.warn("fail apply local state  machine,  logEntry info : {}", logEntry);
    // TODO 不应用到状态机,但已经记录到日志中.由定时任务从重试队列取出,然后重复尝试,当达到条件时,应用到状态机.
    // 这里应该返回错误, 因为没有成功复制过半机器.
    return ClientKVAck.fail();
}

这种涉及到多线程操作的变量比如commitIndex 虽然是 volatile修饰了 但是是不是仍然存在线程安全问题可能被一个旧的更小的值覆盖掉?而这里的赋值有很多先查询后写入的过程,那如果这里有问题,我们继续推断一下,是不是所有多线程操作的变量都有可能出现更新丢失?

Kakk22 commented 1 year ago

方法级别加了synchronized

kebukeYi commented 1 year ago
if (N > commitIndex) {
    LogEntry entry = logModule.read(N);
    if (entry != null && entry.getTerm() == currentTerm) {
        commitIndex = N;
    }
}

//  响应客户端(成功一半)
if (success.get() >= (count / 2)) {
    // 更新
    commitIndex = logEntry.getIndex();
    //  应用到状态机
    getStateMachine().apply(logEntry);
    lastApplied = commitIndex;

    log.info("success apply local state machine,  logEntry info : {}", logEntry);
    // 返回成功.
    return ClientKVAck.ok();
} else {
    // 回滚已经提交的日志.
    logModule.removeOnStartIndex(logEntry.getIndex());
    log.warn("fail apply local state  machine,  logEntry info : {}", logEntry);
    // TODO 不应用到状态机,但已经记录到日志中.由定时任务从重试队列取出,然后重复尝试,当达到条件时,应用到状态机.
    // 这里应该返回错误, 因为没有成功复制过半机器.
    return ClientKVAck.fail();
}

这种涉及到多线程操作的变量比如commitIndex 虽然是 volatile修饰了 但是是不是仍然存在线程安全问题可能被一个旧的更小的值覆盖掉?而这里的赋值有很多先查询后写入的过程,那如果这里有问题,我们继续推断一下,是不是所有多线程操作的变量都有可能出现更新丢失?

你最好将这块代码所在的 类#方法 说明白啊; DefaultNode#handlerClientRequest() 方法