[etcd]raftpd confchange.go

______________________看前须知_________________

在状态改变的程序中需要使用如下接口定义。首先有2个ConfChange,一个是ConfChange,另外一个是ConfChangeV2,这两个接口需要统一化,但是在proto中定义有差异,所以后面的go文件对这两个接口统一化写了一些函数来处理。所以这里要看清楚这两个接口到底有哪些不一样的地方。

message ConfChange {
	optional ConfChangeType  type    = 2 [(gogoproto.nullable) = false];
	optional uint64          node_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "NodeID" ];
	optional bytes           context = 4;

	// NB: this is used only by etcd to thread through a unique identifier.
	// Ideally it should really use the Context instead. No counterpart to
	// this field exists in ConfChangeV2.
	optional uint64          id      = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ID" ];
}

enum ConfChangeType {
	ConfChangeAddNode        = 0;
	ConfChangeRemoveNode     = 1;
	ConfChangeUpdateNode     = 2;
	ConfChangeAddLearnerNode = 3;
}


_________________________________________________________________________________________
message ConfChangeV2 {
	optional ConfChangeTransition transition = 1 [(gogoproto.nullable) = false];
	repeated ConfChangeSingle     changes =    2 [(gogoproto.nullable) = false];
	optional bytes                context =    3;
}

// ConfChangeSingle is an individual configuration change operation. Multiple
// such operations can be carried out atomically via a ConfChangeV2.
message ConfChangeSingle {
	optional ConfChangeType  type    = 1 [(gogoproto.nullable) = false];
	optional uint64          node_id = 2 [(gogoproto.nullable) = false, (gogoproto.customname) = "NodeID"];
}

// ConfChangeTransition specifies the behavior of a configuration change with
// respect to joint consensus.
enum ConfChangeTransition {
	// Automatically use the simple protocol if possible, otherwise fall back
	// to ConfChangeJointImplicit. Most applications will want to use this.
	ConfChangeTransitionAuto          = 0;
	// Use joint consensus unconditionally, and transition out of them
	// automatically (by proposing a zero configuration change).
	//
	// This option is suitable for applications that want to minimize the time
	// spent in the joint configuration and do not store the joint configuration
	// in the state machine (outside of InitialState).
	ConfChangeTransitionJointImplicit = 1;
	// Use joint consensus and remain in the joint configuration until the
	// application proposes a no-op configuration change. This is suitable for
	// applications that want to explicitly control the transitions, for example
	// to use a custom payload (via the Context field).
	ConfChangeTransitionJointExplicit = 2;
}

对于ConfChangeTransition的理解:ConfChangeTransition指定关于联合共识的配置更改的行为。自动、显式、隐式三种。

__________________________________________________

confchange.go文件中首先要看怎么把confchange和confchangeV2统一起来

首先confchange.AsV1(),confchange.AsV2(),confchangeV2.AsV1(),confchangeV2.AsV2()这四个函数就将V1和V2两个版本的接口可以随意转化。

然后对于ConfChangeV2有V2.EnterJoint()和V2.LeaveJoint()方法, 这个对应Raft论文里面的cluster joint consensus。

V2.ConfChangesFromString()是将空格分隔的操作序列解析为ConfChangeSingle的切片,这样可以将一串字符串按照格式解析成单个confchangge。V2.ConfChangesToString()是反过来操作,将单个ConfChange连成字符串。

最后,主要的MarshalConfChange()函数,marshal 会将 []byte 代表的 json 数据转换为我们想要的结构体


// ConfChangeI abstracts over ConfChangeV2 and (legacy) ConfChange to allow
// treating them in a unified manner.
type ConfChangeI interface {
	AsV2() ConfChangeV2
	AsV1() (ConfChange, bool)
}

// MarshalConfChange calls Marshal on the underlying ConfChange or ConfChangeV2
// and returns the result along with the corresponding EntryType.
func MarshalConfChange(c ConfChangeI) (EntryType, []byte, error) {
	var typ EntryType
	var ccdata []byte
	var err error
	if ccv1, ok := c.AsV1(); ok {
		typ = EntryConfChange
		ccdata, err = ccv1.Marshal()
	} else {
		ccv2 := c.AsV2()
		typ = EntryConfChangeV2
		ccdata, err = ccv2.Marshal()
	}
	return typ, ccdata, err
}

// AsV2 returns a V2 configuration change carrying out the same operation.
func (c ConfChange) AsV2() ConfChangeV2 {
	return ConfChangeV2{
		Changes: []ConfChangeSingle{{
			Type:   c.Type,
			NodeID: c.NodeID,
		}},
		Context: c.Context,
	}
}

// AsV1 returns the ConfChange and true.
func (c ConfChange) AsV1() (ConfChange, bool) {
	return c, true
}

// AsV2 is the identity.
func (c ConfChangeV2) AsV2() ConfChangeV2 { return c }

// AsV1 returns ConfChange{} and false.
func (c ConfChangeV2) AsV1() (ConfChange, bool) { return ConfChange{}, false }

// EnterJoint returns two bools. The second bool is true if and only if this
// config change will use Joint Consensus, which is the case if it contains more
// than one change or if the use of Joint Consensus was requested explicitly.
// The first bool can only be true if second one is, and indicates whether the
// Joint State will be left automatically.
func (c ConfChangeV2) EnterJoint() (autoLeave bool, ok bool) {
	// NB: in theory, more config changes could qualify for the "simple"
	// protocol but it depends on the config on top of which the changes apply.
	// For example, adding two learners is not OK if both nodes are part of the
	// base config (i.e. two voters are turned into learners in the process of
	// applying the conf change). In practice, these distinctions should not
	// matter, so we keep it simple and use Joint Consensus liberally.
	if c.Transition != ConfChangeTransitionAuto || len(c.Changes) > 1 {
		// Use Joint Consensus.
		var autoLeave bool
		switch c.Transition {
		case ConfChangeTransitionAuto:
			autoLeave = true
		case ConfChangeTransitionJointImplicit:
			autoLeave = true
		case ConfChangeTransitionJointExplicit:
		default:
			panic(fmt.Sprintf("unknown transition: %+v", c))
		}
		return autoLeave, true
	}
	return false, false
}

// LeaveJoint is true if the configuration change leaves a joint configuration.
// This is the case if the ConfChangeV2 is zero, with the possible exception of
// the Context field.
func (c ConfChangeV2) LeaveJoint() bool {
	// NB: c is already a copy.
	c.Context = nil
	return proto.Equal(&c, &ConfChangeV2{})
}

// ConfChangesFromString parses a Space-delimited sequence of operations into a
// slice of ConfChangeSingle. The supported operations are:
// - vn: make n a voter,
// - ln: make n a learner,
// - rn: remove n, and
// - un: update n.
func ConfChangesFromString(s string) ([]ConfChangeSingle, error) {
	var ccs []ConfChangeSingle
	toks := strings.Split(strings.TrimSpace(s), " ")
	if toks[0] == "" {
		toks = nil
	}
	for _, tok := range toks {
		if len(tok) < 2 {
			return nil, fmt.Errorf("unknown token %s", tok)
		}
		var cc ConfChangeSingle
		switch tok[0] {
		case 'v':
			cc.Type = ConfChangeAddNode
		case 'l':
			cc.Type = ConfChangeAddLearnerNode
		case 'r':
			cc.Type = ConfChangeRemoveNode
		case 'u':
			cc.Type = ConfChangeUpdateNode
		default:
			return nil, fmt.Errorf("unknown input: %s", tok)
		}
		id, err := strconv.ParseUint(tok[1:], 10, 64)
		if err != nil {
			return nil, err
		}
		cc.NodeID = id
		ccs = append(ccs, cc)
	}
	return ccs, nil
}

// ConfChangesToString is the inverse to ConfChangesFromString.
func ConfChangesToString(ccs []ConfChangeSingle) string {
	var buf strings.Builder
	for i, cc := range ccs {
		if i > 0 {
			buf.WriteByte(' ')
		}
		switch cc.Type {
		case ConfChangeAddNode:
			buf.WriteByte('v')
		case ConfChangeAddLearnerNode:
			buf.WriteByte('l')
		case ConfChangeRemoveNode:
			buf.WriteByte('r')
		case ConfChangeUpdateNode:
			buf.WriteByte('u')
		default:
			buf.WriteString("unknown")
		}
		fmt.Fprintf(&buf, "%d", cc.NodeID)
	}
	return buf.String()
}

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值