Merge pull request #3708 from weaveworks/3693-split-ranges

Handle range wrap-around when checking for allocations
This commit is contained in:
Bryan Boreham authored and GitHub committed 2019-10-15 09:19:37 +01:00
commit cb4a8769a8
3 files changed
+36 -26

No files matched your search

+9 -3
View File
@@ -814,8 +814,14 @@ func (alloc *Allocator) sendRingUpdate(dest mesh.PeerName) {
alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) checkRangeHasAllocations(r address.Range) bool {
return alloc.space.NumFreeAddressesInRange(r) != r.Size()
func (alloc *Allocator) hasAllocations(rs []address.Range) bool {
for _, r := range rs {
alloc.debugf("hasAllocations: %v %v vs %v", r, alloc.space.NumFreeAddressesInRange(r), r.Size())
if alloc.space.NumFreeAddressesInRange(r) != r.Size() {
return true
}
}
return false
}
func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
@@ -836,7 +842,7 @@ func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
// If someone sent us a ring, merge it into ours. Note this will move us
// out of the awaiting-consensus state if we didn't have a ring already.
case data.Ring != nil:
updated, err := alloc.ring.Merge(*data.Ring, alloc.checkRangeHasAllocations)
updated, err := alloc.ring.Merge(*data.Ring, alloc.hasAllocations)
switch err {
case nil:
if updated {
+16 -14
View File
@@ -213,7 +213,7 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
// Merge the given ring into this ring and indicate whether this ring
// got updated as a result.
func (r *Ring) Merge(gossip Ring, checkRangeHasAllocations func(r address.Range) bool) (bool, error) {
func (r *Ring) Merge(gossip Ring, hasAllocations func(r []address.Range) bool) (bool, error) {
r.assertInvariants()
defer r.trackUpdates()()
@@ -238,7 +238,7 @@ func (r *Ring) Merge(gossip Ring, checkRangeHasAllocations func(r address.Range)
return false, ErrDifferentRange
}
result, updated, err := r.Entries.merge(gossip.Entries, r.Peer, r, checkRangeHasAllocations)
result, updated, err := r.Entries.merge(gossip.Entries, r.Peer, r, hasAllocations)
if err != nil {
return false, err
@@ -270,7 +270,7 @@ func (r *Ring) Merge(gossip Ring, checkRangeHasAllocations func(r address.Range)
// entries belonging to ourPeer. Returns the merged entries and an
// indication whether the merge resulted in any changes, i.e. the
// result differs from the original.
func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, checkRangeHasAllocations func(r address.Range) bool) (result entries, updated bool, err error) {
func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, hasAllocations func(r []address.Range) bool) (result entries, updated bool, err error) {
var mine, theirs *entry
var previousOwner *mesh.PeerName
addToResult := func(e entry) { result = append(result, &e) }
@@ -284,7 +284,7 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, checkRang
var i, j int
for i < len(es) && j < len(other) {
mine, theirs = es[i], other[j]
common.Log.Debugln(fmt.Sprintf("[ring %s]: Merge mine.Token=%s theirs.Token=%s mine.Peer=%s theirs.Peer=%s mine.Version=%s theirs.Version=%s", ourPeer, mine.Token, theirs.Token, mine.Peer, theirs.Peer, fmt.Sprint(mine.Version), fmt.Sprint(theirs.Version)))
common.Log.Debugf("[ring %s]: Merge mine.Token=%s theirs.Token=%s mine.Peer=%s theirs.Peer=%s mine.Version=%v theirs.Version=%v", ourPeer, mine.Token, theirs.Token, mine.Peer, theirs.Peer, mine.Version, theirs.Version)
switch {
case mine.Token < theirs.Token:
addToResult(*mine)
@@ -294,7 +294,7 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, checkRang
// insert, checking that a range owned by us hasn't been split
if previousOwner != nil && *previousOwner == ourPeer && theirs.Peer != ourPeer {
// check we have no allocations in the range that got split
if checkRangeHasAllocations(address.Range{Start: theirs.Token, End: mine.Token}) {
if hasAllocations(r.makeRanges(theirs.Token, mine.Token)) {
err = errEntryInMyRange(theirs)
return
}
@@ -302,14 +302,13 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, checkRang
addTheirs(*theirs)
j++
case mine.Token == theirs.Token:
common.Log.Debugln(fmt.Sprintf("[ring %s]: Merge token=%s mine.Peer=%s theirs.Peer=%s mine.Version=%s theirs.Version=%s", ourPeer, mine.Token, mine.Peer, theirs.Peer, fmt.Sprint(mine.Version), fmt.Sprint(theirs.Version)))
// merge
switch {
case mine.Version >= theirs.Version:
if mine.Version == theirs.Version && !mine.Equal(theirs) {
if mine.Peer == ourPeer {
// if we own the entry and has allocations
if checkRangeHasAllocations(address.Range{Start: mine.Token, End: es.entry(i + 1).Token}) {
if hasAllocations(r.makeRanges(theirs.Token, es.entry(i+1).Token)) {
err = errInconsistentEntry(mine, theirs)
return
}
@@ -332,7 +331,8 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, checkRang
// in the range effectively given away, or it belongs to our own peer, in
// which case we should set our version to the one received plus one,
// effectively imposing our existing entry.
if theirs.Peer != ourPeer && !checkRangeHasAllocations(address.Range{Start: mine.Token, End: es.entry(i + 1).Token}) {
if theirs.Peer != ourPeer && !hasAllocations(r.makeRanges(mine.Token, es.entry(i+1).Token)) {
common.Log.Infof("ring: repair inconsistent data by accepting token %v from %v - no allocations up to %v", theirs.Token, theirs.Peer, es.entry(i+1).Token)
addTheirs(*theirs)
} else if theirs.Peer == ourPeer {
mine.Version = theirs.Version + 1
@@ -375,6 +375,11 @@ func (r *Ring) Empty() bool {
return len(r.Entries) == 0
}
// convenience function to normalise a single range that may span zero
func (r *Ring) makeRanges(start, end address.Address) []address.Range {
return r.splitRangesOverZero([]address.Range{{Start: start, End: end}})
}
// Given a slice of ranges which are all in the right order except
// possibly the last one spans zero, fix that up and return the slice
func (r *Ring) splitRangesOverZero(ranges []address.Range) []address.Range {
@@ -425,10 +430,7 @@ type RangeInfo struct {
func (r *Ring) AllRangeInfo() (result []RangeInfo) {
for i, entry := range r.Entries {
nextEntry := r.Entries.entry(i + 1)
ranges := []address.Range{{Start: entry.Token, End: nextEntry.Token}}
ranges = r.splitRangesOverZero(ranges)
for _, r := range ranges {
for _, r := range r.makeRanges(entry.Token, r.Entries.entry(i+1).Token) {
result = append(result, RangeInfo{entry.Peer, r, entry.Version})
}
}
@@ -526,7 +528,7 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Count) (updated
entries[i].Free = free
entries[i].Version++
common.Log.Debugln(fmt.Sprintf("[ring %s]: ReportFree token=%s peer=%s version=%s", r.Peer, entries[i].Token, entries[i].Peer, fmt.Sprint(entries[i].Version)))
common.Log.Debugf("[ring %s]: ReportFree token=%s peer=%s version=%v", r.Peer, entries[i].Token, entries[i].Peer, entries[i].Version)
updated = true
}
return
@@ -610,7 +612,7 @@ func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range {
// bump version by large value so that transfer of range takes precedence (if there is any conflict in merging entry)
// over other opertaions on the entry that increments the version
entry.Version = entry.Version + 100
common.Log.Debugln(fmt.Sprintf("[ring %s]: Transfer token=%s from=%s to=%s version=%s", r.Peer, entry.Token, from, to, fmt.Sprint(entry.Version)))
common.Log.Debugf("[ring %s]: Transfer token=%s from=%s to=%s version=%v", r.Peer, entry.Token, from, to, entry.Version)
newRanges = append(newRanges, address.Range{Start: entry.Token, End: r.Entries.entry(i + 1).Token})
}
}
+11 -9
View File
@@ -38,19 +38,21 @@ func merge(r1, r2 *Ring) error {
}
return address.Count((r1.End - start) + (end - r1.Start))
}
checkEntryHasAllocations := func(r address.Range) bool {
size := distance(r.Start, r.End)
entry, found := r1.Entries.get(r.Start)
if !found {
return false
}
if entry.Free < size {
return true
check := func(rs []address.Range) bool {
for _, r := range rs {
size := distance(r.Start, r.End)
entry, found := r1.Entries.get(r.Start)
if !found {
continue
}
if entry.Free < size {
return true
}
}
return false
}
_, err := r1.Merge(*r2, checkEntryHasAllocations)
_, err := r1.Merge(*r2, check)
return err
}