阅读 etcd
分布式锁 mutex
的源码时,遇到 waitDeletes
函数
func waitDeletes(ctx context.Context, client *v3.Client, pfx string, maxCreateRev int64) (*pb.ResponseHeader, error) {
getOpts := append(v3.WithLastCreate(), v3.WithMaxCreateRev(maxCreateRev))
for {
resp, err := client.Get(ctx, pfx, getOpts...)
if err != nil {
return nil, err
}
if len(resp.Kvs) == 0 {
return resp.Header, nil
}
lastKey := string(resp.Kvs[0].Key)
if err = waitDelete(ctx, client, lastKey, resp.Header.Revision); err != nil {
return nil, err
}
}
}
对 getOpts := append(v3.WithLastCreate(), v3.WithMaxCreateRev(maxCreateRev))
这个语句不是特别理解
向 etcd
中插入原始数据
$ etcdctl --endpoints=http://127.0.0.1:10001 put k1 v1 -w json
{
"header":{
"cluster_id":18293669711776909085,"member_id":8241799522139745222,"revision":2,"raft_term":2}}
$ etcdctl --endpoints=http://127.0.0.1:10001 put k2 v2 -w json
{
"header":{
"cluster_id":18293669711776909085,"member_id":8241799522139745222,"revision":3,"raft_term":2}}
$ etcdctl --endpoints=http://127.0.0.1:10001 put k3 v3 -w json
{
"header":{
"cluster_id":18293669711776909085,"member_id":8241799522139745222,"revision":4,"raft_term":2}}
$ etcdctl --endpoints=http://127.0.0.1:10001 put k4 v4 -w json
{
"header":{
"cluster_id":18293669711776909085,"member_id":8241799522139745222,"revision":5,"raft_term":2}}
$ etcdctl --endpoints=http://127.0.0.1:10001 put k5 v5 -w json
{
"header":{
"cluster_id":18293669711776909085,"member_id":8241799522139745222,"revision":6,"raft_term":2}}
$ tcdctl --endpoints=http://127.0.0.1:10001 get k --prefix --keys-only
k1
k2
k3
k4
k5
包含 WithMaxCreateRev
的测试代码如下
package main
import (
"context"
"fmt"
"go.etcd.io/etcd/clientv3"
"log"
)
func main() {
endpoints := []string{
"127.0.0.1:10001", "127.0.0.1:10002", "127.0.0.1:10003"}
cli, err := clientv3.New(clientv3.Config{
Endpoints: endpoints})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
getOps := append(clientv3.WithLastCreate(), clientv3.WithMaxCreateRev(5))
resp, err := cli.Get(context.TODO(), "k", getOps...)
if err != nil {
log.Fatal(err)
}
if len(resp.Kvs) != 0 {
fmt.Printf("%s -> %s, create rev=%d, head rev=%d\n",
string(resp.Kvs[0].Key),
string(resp.Kvs[0].Value),
resp.Kvs[0].CreateRevision,
resp.Header.Revision)
}
}
结合前述的 put
语句, k4
对应的 revision
值为 5
,测试代码的 WithMaxCreateRev
也为 5,测试程序输出结果如下:
k4 -> v4, create rev=5, head rev=6
在 etcd
中删除 k4
$ etcdctl --endpoints=http://127.0.0.1:10001 del k4 -w json
{
"header":{
"cluster_id":18293669711776909085,"member_id":8241799522139745222,"revision":7,"raft_term":2},"deleted":1}
重新运行测试程序运行结果如下"
k3 -> v3, create rev=4, head rev=7
据此可以得到以下结论
WithMaxCreateRev
是在当前 etcd
最新的 revision
上进行查询CreateRev
不能超过WithMaxCreateRev
设定值revision
为7
,测试对应的 k4
已经被删除了,所以 WithLastCreate
返回 k3
的结果为了验证我们的结论,可以在测试程序的 getOps := append(clientv3.WithLastCreate(), clientv3.WithMaxCreateRev(5))
添加 clientv3.WithRev(6)
,完整测试代码如下:
package main
import (
"context"
"fmt"
"go.etcd.io/etcd/clientv3"
"log"
)
func main() {
endpoints := []string{
"127.0.0.1:10001", "127.0.0.1:10002", "127.0.0.1:10003"}
cli, err := clientv3.New(clientv3.Config{
Endpoints: endpoints})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
getOps := append(clientv3.WithLastCreate(), clientv3.WithMaxCreateRev(5), clientv3.WithRev(6))
resp, err := cli.Get(context.TODO(), "k", getOps...)
if err != nil {
log.Fatal(err)
}
if len(resp.Kvs) != 0 {
fmt.Printf("%s -> %s, create rev=%d, head rev=%d\n",
string(resp.Kvs[0].Key),
string(resp.Kvs[0].Value),
resp.Kvs[0].CreateRevision,
resp.Header.Revision)
}
}
程序输出结果如下:
k4 -> v4, create rev=5, head rev=7
在新的测试程序中,指定在 revision
为6
上进行查询,这个条件下 k4
并没有被删除,所以 WithLastCreate
返回 k4
WithMaxCreateRev
和 WithRev
没有任何关系,这是两个不同维度的参数WithRev
指定了在哪个 snapshot
上进行查询WithMaxCreateRev
过滤当前 snapshot
上 CreateRev
不大于 WithMaxCreateRev
的记录