Skip to content

Commit b9f9410

Browse files
authored
Memberlist: fix "forget" (#3603)
* When deleting entry in the ring and using gossip, update timestamp to deletion time. Signed-off-by: Peter Štibraný <[email protected]>
1 parent ddfbbf2 commit b9f9410

File tree

3 files changed

+18
-9
lines changed

3 files changed

+18
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* [ENHANCEMENT] Memberlist: client can now keep a size-bounded buffer with sent and received messages and display them in the admin UI (/memberlist) for troubleshooting. #3581
1616
* [BUGFIX] Allow `-querier.max-query-lookback` use `y|w|d` suffix like deprecated `-store.max-look-back-period`. #3598
1717
* [BUGFIX] Query-Frontend: `cortex_query_seconds_total` now return seconds not nanoseconds. #3589
18+
* [BUGFIX] Memberlist: Entry in the ring should now not appear again after using "Forget" feature (unless it's still heartbeating). #3603
1819

1920
## 1.6.0-rc.0 in progress
2021

pkg/ring/merge_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -356,30 +356,30 @@ func TestMergeRemoveMissing(t *testing.T) {
356356
Ingesters: map[string]IngesterDesc{
357357
"Ing 1": {Addr: "addr1", Timestamp: now, State: ACTIVE, Tokens: []uint32{30, 40, 50}},
358358
"Ing 2": {Addr: "addr2", Timestamp: now + 5, State: ACTIVE, Tokens: []uint32{5, 10, 20, 100, 200}},
359-
"Ing 3": {Addr: "addr3", Timestamp: now, State: LEFT},
359+
"Ing 3": {Addr: "addr3", Timestamp: now + 3, State: LEFT}, // When deleting, time depends on value passed to merge function.
360360
},
361361
}
362362
}
363363

364364
{
365-
our, ch := mergeLocalCAS(firstRing(), secondRing())
365+
our, ch := mergeLocalCAS(firstRing(), secondRing(), now+3)
366366
assert.Equal(t, expectedFirstSecondMerge(), our)
367367
assert.Equal(t, &Desc{
368368
Ingesters: map[string]IngesterDesc{
369369
"Ing 2": {Addr: "addr2", Timestamp: now + 5, State: ACTIVE, Tokens: []uint32{5, 10, 20, 100, 200}},
370-
"Ing 3": {Addr: "addr3", Timestamp: now, State: LEFT},
370+
"Ing 3": {Addr: "addr3", Timestamp: now + 3, State: LEFT}, // When deleting, time depends on value passed to merge function.
371371
},
372372
}, ch) // entire second ring is new
373373
}
374374

375-
{ // idempotency: (no change after applying same ring again)
376-
our, ch := mergeLocalCAS(expectedFirstSecondMerge(), secondRing())
375+
{ // idempotency: (no change after applying same ring again, even if time has advanced)
376+
our, ch := mergeLocalCAS(expectedFirstSecondMerge(), secondRing(), now+10)
377377
assert.Equal(t, expectedFirstSecondMerge(), our)
378378
assert.Equal(t, (*Desc)(nil), ch)
379379
}
380380

381381
{ // commutativity is broken when deleting missing entries. But let's make sure we get reasonable results at least.
382-
our, ch := mergeLocalCAS(secondRing(), firstRing())
382+
our, ch := mergeLocalCAS(secondRing(), firstRing(), now+3)
383383
assert.Equal(t, &Desc{
384384
Ingesters: map[string]IngesterDesc{
385385
"Ing 1": {Addr: "addr1", Timestamp: now, State: ACTIVE, Tokens: []uint32{30, 40, 50}},
@@ -419,7 +419,7 @@ func TestMergeMissingIntoLeft(t *testing.T) {
419419
}
420420

421421
{
422-
our, ch := mergeLocalCAS(ring1(), ring2())
422+
our, ch := mergeLocalCAS(ring1(), ring2(), now+10)
423423
assert.Equal(t, &Desc{
424424
Ingesters: map[string]IngesterDesc{
425425
"Ing 1": {Addr: "addr1", Timestamp: now + 10, State: ACTIVE, Tokens: []uint32{30, 40, 50}},
@@ -438,8 +438,8 @@ func TestMergeMissingIntoLeft(t *testing.T) {
438438
}
439439
}
440440

441-
func mergeLocalCAS(ring1, ring2 *Desc) (*Desc, *Desc) {
442-
change, err := ring1.Merge(ring2, true)
441+
func mergeLocalCAS(ring1, ring2 *Desc, nowUnixTime int64) (*Desc, *Desc) {
442+
change, err := ring1.mergeWithTime(ring2, true, time.Unix(nowUnixTime, 0))
443443
if err != nil {
444444
panic(err)
445445
}

pkg/ring/model.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ func (i *IngesterDesc) IsHealthy(op Operation, heartbeatTimeout time.Duration) b
189189
//
190190
// This method is part of memberlist.Mergeable interface, and is only used by gossiping ring.
191191
func (d *Desc) Merge(mergeable memberlist.Mergeable, localCAS bool) (memberlist.Mergeable, error) {
192+
return d.mergeWithTime(mergeable, localCAS, time.Now())
193+
}
194+
195+
func (d *Desc) mergeWithTime(mergeable memberlist.Mergeable, localCAS bool, now time.Time) (memberlist.Mergeable, error) {
192196
if mergeable == nil {
193197
return nil, nil
194198
}
@@ -229,6 +233,10 @@ func (d *Desc) Merge(mergeable memberlist.Mergeable, localCAS bool) (memberlist.
229233
// missing, let's mark our ingester as LEFT
230234
ting.State = LEFT
231235
ting.Tokens = nil
236+
// We are deleting entry "now", and should not keep old timestamp, because there may already be pending
237+
// message in the gossip network with newer timestamp (but still older than "now").
238+
// Such message would "resurrect" this deleted entry.
239+
ting.Timestamp = now.Unix()
232240
thisIngesterMap[name] = ting
233241

234242
updated = append(updated, name)

0 commit comments

Comments
 (0)