diff --git a/CHANGELOG.md b/CHANGELOG.md index 8776a86d6..c964f7ae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ * [ENHANCEMENT] Replace go-kit/kit/log with go-kit/log. #52 * [ENHANCEMENT] Add spanlogger package. #42 * [ENHANCEMENT] Add runutil.CloseWithLogOnErr function. #58 -* [ENHANCEMENT] Optimise memberlist receive path when used as a backing store for rings with a large number of members. #76 #77 #84 +* [ENHANCEMENT] Optimise memberlist receive path when used as a backing store for rings with a large number of members. #76 #77 #84 #91 * [ENHANCEMENT] Memberlist: prepare the data to send on the write before starting counting the elapsed time for `-memberlist.packet-write-timeout`, in order to reduce chances we hit the timeout when sending a packet to other node. #89 * [ENHANCEMENT] flagext: for cases such as `DeprecatedFlag()` that need a logger, add RegisterFlagsWithLogger. #80 * [BUGFIX] spanlogger: Support multiple tenant IDs. #59 diff --git a/ring/model.go b/ring/model.go index b8ad22281..13753aeb2 100644 --- a/ring/model.go +++ b/ring/model.go @@ -202,11 +202,15 @@ func (d *Desc) mergeWithTime(mergeable memberlist.Mergeable, localCAS bool, now otherIngesterMap := other.Ingesters var updated []string + tokensChanged := false for name, oing := range otherIngesterMap { ting := thisIngesterMap[name] // ting.Timestamp will be 0, if there was no such ingester in our version if oing.Timestamp > ting.Timestamp { + if !tokensEqual(ting.Tokens, oing.Tokens) { + tokensChanged = true + } oing.Tokens = append([]uint32(nil), oing.Tokens...) // make a copy of tokens thisIngesterMap[name] = oing updated = append(updated, name) @@ -241,7 +245,7 @@ func (d *Desc) mergeWithTime(mergeable memberlist.Mergeable, localCAS bool, now } // resolveConflicts allocates lot of memory, so if we can avoid it, do that. - if conflictingTokensExist(thisIngesterMap) { + if tokensChanged && conflictingTokensExist(thisIngesterMap) { resolveConflicts(thisIngesterMap) } @@ -303,6 +307,19 @@ func normalizeIngestersMap(inputRing *Desc) { } } +// tokensEqual checks for equality of two slices. Assumes the slices are sorted. +func tokensEqual(lhs, rhs []uint32) bool { + if len(lhs) != len(rhs) { + return false + } + for i := 0; i < len(lhs); i++ { + if lhs[i] != rhs[i] { + return false + } + } + return true +} + var tokenMapPool = sync.Pool{New: func() interface{} { return make(map[uint32]struct{}) }} func conflictingTokensExist(normalizedIngesters map[string]InstanceDesc) bool {