• pass 3A 了。但是有个问题,我在 kv 层维护了 responses map[string] string, 从 client 的 requestId 映射到 responses(防止 client 发重复的 requestid),但是随着 client 不断发 request, 这个 map 会不断增长。实际应用肯定是有问题的。

  • 另外一个问题是写 goroutine 中发现的,goroutine 好像允许一个 goroutine 锁 lock,然后另一个 goroutine unlock, 简单说明代码如下:

    var mu sync.Mutex
    mu.Lock()
        var wg sync.WaitGroup
        wg.Add(1)
        go func() {
            mu.Unlock()
            wg.Done()
        }()
        wg.Wait()
        fmt.Println("locking again")
        mu.Lock()
        mu.Unlock()
    

    也就是对于 go 的 mutex 的实现来说,只管 lock/unlock,而不管是在那个 goroutine unlock? (通常我们是同一个 goroutine lock/unlock)

    主要问题是因为调试 raft 代码中一大堆 goroutine 和一堆 lock, 当 hold lock 的时候,起 goroutine,这些 goroutine 相当于是看到这个锁 lock 的? 代码如下:

    rf.mu.Lock()
    go func() {
                rf.applyCh <- ApplyMsg{SnapshotValid: true, Snapshot: args.Data, SnapshotTerm: args.LastIncludedTerm, SnapshotIndex: args.LastIncludedIndex}
    }()
    rf.mu.Unlock()
    

    ), 在实现 snapshot 的时候,这个 goroutine 会有一段时间看见这个锁 lock?

    所以感觉这里锁的语义不是很明确

  • 这句话在哪里?我怎么没有找到?

    我读的是这个 raft extended https://pdos.csail.mit.edu/6.824/papers/raft-extended.pdf

    另外,这说的是接受 request 端吧?我问的是拿到 reply 端的情况,reply 端估计应该也检查 term

  • 对的 1.改了 3.还是要检查的,比如现在 term 8 我是 server,发出去的一个 AppendEntries rpc delay 了,然后经过一些奇怪的超时,我现在是 term 10 的 leader 了,然后 term 8 的 reply 回来, 我如果更新 nextIndex/matchIndex,就不对了。 同样 If last log index ≥ nextIndex for a follower: send AppendEntries RPC with log entries starting at nextIndex • If successful: update nextIndex and matchIndex for follower (§5.3)

    If successful 这个分支忘记问了,但是我感觉也是同样,必须检查是否仍然为同 term 时候的 leader,跟

    If AppendEntries fails because of log inconsistency 需要的检查是一样的。

    然后想了一下,那么同样 Candidates 的 Rule:

    If votes received from majority of servers: become leader

    同样也要对 reply 检查是否同一 term,

    否则我 term 8 选举一个 reply delay 了,然后我 term 9 选举收了 majority-1 个 reply,

    然后突然 term 8 的 reply 到了,这样我 succcesful vote + 1 还以为变成了 majority vote,

    这就不对了,所以同样也要对 reply 检查是否同一 term

  • 目前我 lab 2 的所有 test 都能 pass😀 1.确实如你所说,new server 意思是本 term 之后的所有 term,不包括那些老的 server 发过来的 AppendEntries rpc,我的实现虽然没有 bug,但是不 performant, 有可能需要新一轮的选举

    2.我的 code 是有 heartbeat() 函数和 AppendEntries() 函数,heartbeat() 中,如果 prevLogIndex/prevLogTerm,马上就会在 heartbeat() 中进行重试,送一些 entries 出来,其实按照你说的,定义就是普通的带 Entries 的 AppendEntries RPC,只不过我是写在 heartbeat() 中而已

    3.All leader 的 Rule 我肯定是执行的: All Servers:

    • If commitIndex > lastApplied: increment lastApplied, apply log[lastApplied] to state machine (§5.3)

    • If RPC request or response contains term T > currentTerm: set currentTerm = T, convert to follower (§5.1)

    这两条我都有执行,我问的是 Leaders 下面的 Rule:

    • If AppendEntries fails because of log inconsistency: decrement nextIndex and retry (§5.3)

    • If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm: set commitIndex = N (§5.3, §5.4).

    这两条,在拿到 reply 的时候,是否需要检查我自己仍然为 Leader,

    我猜是需要的,我是加了 if term == rf.currentTerm 的 check

    这样老的 reply 不会更新 nextIndex/matchIndex 和 commitIndex,

    我觉得这是正确的语义

  • 做到 raft 2d 部分,还是很开心的,distributed system 还是很容易出一些奇奇怪怪的 bug 的, 改了好久才好。paper 中算法有几个有歧义的地方, 1.Candidates: If AppendEntries RPC received from new leader: convert to follower. 这个 new leader 的定义是什么?Leader Term>=Candidate's Term 么? 我实际做的时候不管, 看见有 leader 来的 AppendEntries RPC 就从 Candidates Convert 成 follower,反正如果 Leader Term currentTerm: set currentTerm = T, convert to follower, 所以 Leader 也会在这次 reply 被 convert 成 Follower,无非是多一次选举。 2.heartbeat 头一次是送 empty entries,但是如果 prevLogIndex/prevLogTerm 导致 nextIndex[i] 减了,那么 heartbeat 是否送 entries? 还是说 heartbeat 只管 heartbeat,而送 entries 的只由 normal AppendEntries RPC 不断重试? 目前我的实现是 heartbeat 一旦发现 prevLogIndex/prevLogTerm 导致 nextIndex[i] 减了,是会送出 entries 的。 这样比较简单,反正都是幂等操作。 3.Leaders Rule:If AppendEntries fails because of log inconsistency: decrement nextIndex and retry 和 If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm: set commitIndex = N 这个检查在 leader 接收到 reply 的时候检查,但是接收到 reply 是否要重新检查自己还为 Leader, 因为如果网络延时,reply 时可能都新 term 了,leader 可能变成新 term 的 follower,当然也可能仍然是新 term 的 leader 目前他这个 Rule 在 Leaders section 下面,所以我猜他是要检查的,我的检查 比较简单,判断仍然 term==rf.currentTerm,也就是一旦新 term,所有老 term 的 request 都失效, 我不会再用它来更新 nextIndex/matchIndex,也不会更新 commitIndex

  • code review

  • 共享一下,抄作业~

  • 兄弟你作业做了没有?我看了第一集,上来就要求写一个 mapreduce.

  • 支持一下

  • Ruby2.7 irb paste long text error at 2022年07月25日

    感觉还是很奇怪的需求。有多种方式解决,你能写文件系统就写个文件 require, 要不然建个 model DynamicCode,然后后台系统弄个 textarea 填代码, 然后 console 里头 eval DynamicCode.first.code

  • Ruby2.7 irb paste long text error at 2022年07月25日

    为什么需要粘贴大段代码?写个 rake task 不行么? 或者写个文件,console 里头 require 就行

  • 厉害,一般 Developer 的美感都不足, 要 Design 还是有些困难的

  • 各位觉得这个怎么样? at 2022年07月02日

    ruby 以前到有个 opal,这么多年了不知道怎么样了

  • @Stephanie 之前不是说要求会广东话么?

  • 对了,那个 apollo 的搭建,也有运维人员的帮忙。

  • @lanzhiheng 前后端分离挺好用的啊,后端只需要写好逻辑就好了,很 focus,很符合经济学原理 里头的分工理论,即甲乙产出 A,B 产品,即使甲 A,B 产出效率都比乙高,分工也效率更好, 让甲专心做 A,已专心做 B,出来的效率也比甲乙都混合做 A,B 高 (经济学原理第一章)。目前实践下来挺方便的, 除了后期调试 bug 要复现他们所说的 bug 的时候不太方便以外,其他都还好。至于之前有个人说什么 前端莫名奇妙做缓存导致改 api 变动出问题,那是前端程序员的问题吧,依赖一个不存在的约定。

    另外,部署为什么要用 Etcd,我们用 apollo.另外 docker 部署确实快,现在一个改动 push 下去, 都是几 s 就好,最近几次都是 7-8s, 比 cap,mina 什么的快多了。得益于 docker 的 layer 机制。

    其次,你们运维就简单的搭了 k8s 么,其他都不管的么。那这怎么实践最佳实践啊。我们运维 负责把环境全部搭好,我们只需要把 builder 好的 docker image 给他就行了,做 CI/CD。

    所以,没有做最佳实践,估计还是做最佳实践的各个组件,或者一部分组件 (指人) 没有做好吧。 那我这边再好的插头,没有插座也没用啊。国外那帮推最佳实践的,总体的实力都比较强啊。 就像之前的敏捷,传着传着就变形了。很难做到 copy 不走样。

  • @jasl 那就看看要支持到什么程度了,或者自己实现一套。跟 MRI 做交互的话,序列话反序列化类似 [:bigdecimal,value,scale] ,虽然不知道 msgpack 啥样,反正一个值表示类型,然后再把值和精度 表示上,就可以跟 MRI 做交互了。查了一下 mruby bigint, 一个是https://github.com/chasonr/mruby-bignum, 一个是https://github.com/chasonr/mruby-gmp-bignum/ , 有 gmp support 的。

  • @jasl 是 Decimal 类么?稍微试了一下, x = Decimal.new("1515") * Decimal.new("1515") x=x*x*x*x x=x*x*x*x x=x*x*x*x puts x

    123827471039368400410635789691185425627549561183592154304459242100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 数字一大就错了。

    另外 integer,乘着乘着就变浮点数了,这是 mruby 的特性么?

    ps,感觉接下去就比较 specific 了,要不然我去 github 上开个 issue 讨论?

  • @jasl 我看你说 mruby 没有 bigdecimal? 据我所知, bigdecimal 就是 bigint+ 精度,比如 (a,4) 就是 a/10000.0, 那么 +,-实现只要对齐就行了,实现就是 bigint 相乘,精度相加,比如 (a,4)(b,2) 就是 (a*b, 4+2),/实现也是类似。mruby 有 bigint 么?

  • @jasl 如果 spawn 一个 ruby,然后 remove 一些 unsafe 的东西,比如 undef File, IO, remove `等等, 会不会更简单一些?

  • 挺不错的,顶一下。

  • 我也要入群 @wangshu

  • @DerekZhang 你不是在区块链么?怎么又变成供应链了?

  • 我在写 Perl 时的效率太高,去写别的语言短时间内根本达不到同等水平。毕竟这是十六年积累下来的经验。毕竟这是两万小时的练习成果。哈哈,我写 ruby 也是这样啊,估计也有两万小时了吧😂 。解决问题写很 elegant 的方法 (method),方法搞不定的用 meta-programming,再搞不定还可以用 ast 语法树。(可以叫做 super meta-programming 吗哈哈), 其他语言根本达不到同等效率。即使之前用了 python,但是感觉对于 python 也就是调调 api,解决解决算法问题这样的程度,根本达不到得心应手的程度。