mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #1662 from yichengq/211
etcdserver: fix data race in cluster
This commit is contained in:
commit
c9894687fc
@ -121,7 +121,7 @@ func (c *Cluster) Members() []*Member {
|
|||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
var sms SortableMemberSlice
|
var sms SortableMemberSlice
|
||||||
for _, m := range c.members {
|
for _, m := range c.members {
|
||||||
sms = append(sms, m)
|
sms = append(sms, m.Clone())
|
||||||
}
|
}
|
||||||
sort.Sort(sms)
|
sort.Sort(sms)
|
||||||
return []*Member(sms)
|
return []*Member(sms)
|
||||||
@ -136,7 +136,7 @@ func (s SortableMemberSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|||||||
func (c *Cluster) Member(id types.ID) *Member {
|
func (c *Cluster) Member(id types.ID) *Member {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
return c.members[id]
|
return c.members[id].Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemberByName returns a Member with the given name if exists.
|
// MemberByName returns a Member with the given name if exists.
|
||||||
@ -153,7 +153,7 @@ func (c *Cluster) MemberByName(name string) *Member {
|
|||||||
memb = m
|
memb = m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return memb
|
return memb.Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) MemberIDs() []types.ID {
|
func (c *Cluster) MemberIDs() []types.ID {
|
||||||
@ -335,6 +335,12 @@ func (c *Cluster) RemoveMember(id types.ID) {
|
|||||||
c.removed[id] = true
|
c.removed[id] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) UpdateMemberAttributes(id types.ID, attr Attributes) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
c.members[id].Attributes = attr
|
||||||
|
}
|
||||||
|
|
||||||
// nodeToMember builds member through a store node.
|
// nodeToMember builds member through a store node.
|
||||||
// the child nodes of the given node should be sorted by key.
|
// the child nodes of the given node should be sorted by key.
|
||||||
func nodeToMember(n *store.NodeExtern) (*Member, error) {
|
func nodeToMember(n *store.NodeExtern) (*Member, error) {
|
||||||
|
@ -80,6 +80,27 @@ func (m *Member) PickPeerURL() string {
|
|||||||
return m.PeerURLs[rand.Intn(len(m.PeerURLs))]
|
return m.PeerURLs[rand.Intn(len(m.PeerURLs))]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Member) Clone() *Member {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mm := &Member{
|
||||||
|
ID: m.ID,
|
||||||
|
Attributes: Attributes{
|
||||||
|
Name: m.Name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if m.PeerURLs != nil {
|
||||||
|
mm.PeerURLs = make([]string, len(m.PeerURLs))
|
||||||
|
copy(mm.PeerURLs, m.PeerURLs)
|
||||||
|
}
|
||||||
|
if m.ClientURLs != nil {
|
||||||
|
mm.ClientURLs = make([]string, len(m.ClientURLs))
|
||||||
|
copy(mm.ClientURLs, m.ClientURLs)
|
||||||
|
}
|
||||||
|
return mm
|
||||||
|
}
|
||||||
|
|
||||||
func memberStoreKey(id types.ID) string {
|
func memberStoreKey(id types.ID) string {
|
||||||
return path.Join(storeMembersPrefix, id.String())
|
return path.Join(storeMembersPrefix, id.String())
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package etcdserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -88,3 +89,21 @@ func TestMemberPick(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMemberClone(t *testing.T) {
|
||||||
|
tests := []*Member{
|
||||||
|
newTestMemberp(1, nil, "abc", nil),
|
||||||
|
newTestMemberp(1, []string{"http://a"}, "abc", nil),
|
||||||
|
newTestMemberp(1, nil, "abc", []string{"http://b"}),
|
||||||
|
newTestMemberp(1, []string{"http://a"}, "abc", []string{"http://b"}),
|
||||||
|
}
|
||||||
|
for i, tt := range tests {
|
||||||
|
nm := tt.Clone()
|
||||||
|
if nm == tt {
|
||||||
|
t.Errorf("#%d: the pointers are the same, and clone doesn't happen", i)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(nm, tt) {
|
||||||
|
t.Errorf("#%d: member = %+v, want %+v", i, nm, tt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -618,13 +618,11 @@ func (s *EtcdServer) applyRequest(r pb.Request) Response {
|
|||||||
default:
|
default:
|
||||||
if storeMemberAttributeRegexp.MatchString(r.Path) {
|
if storeMemberAttributeRegexp.MatchString(r.Path) {
|
||||||
id := mustParseMemberIDFromKey(path.Dir(r.Path))
|
id := mustParseMemberIDFromKey(path.Dir(r.Path))
|
||||||
m := s.Cluster.Member(id)
|
var attr Attributes
|
||||||
if m == nil {
|
if err := json.Unmarshal([]byte(r.Val), &attr); err != nil {
|
||||||
log.Panicf("fetch member %s should never fail", id)
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal([]byte(r.Val), &m.Attributes); err != nil {
|
|
||||||
log.Panicf("unmarshal %s should never fail: %v", r.Val, err)
|
log.Panicf("unmarshal %s should never fail: %v", r.Val, err)
|
||||||
}
|
}
|
||||||
|
s.Cluster.UpdateMemberAttributes(id, attr)
|
||||||
}
|
}
|
||||||
return f(s.store.Set(r.Path, r.Dir, r.Val, expr))
|
return f(s.store.Set(r.Path, r.Dir, r.Val, expr))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user