diff --git a/tsdb/head.go b/tsdb/head.go index bf181a415..25209cd01 100644 --- a/tsdb/head.go +++ b/tsdb/head.go @@ -1718,7 +1718,7 @@ func (m *seriesHashmap) get(hash uint64, lset labels.Labels) *memSeries { return nil } -func (m seriesHashmap) set(hash uint64, s *memSeries) { +func (m *seriesHashmap) set(hash uint64, s *memSeries) { if existing, found := m.unique[hash]; !found || labels.Equal(existing.lset, s.lset) { m.unique[hash] = s return @@ -1736,7 +1736,7 @@ func (m seriesHashmap) set(hash uint64, s *memSeries) { m.conflicts[hash] = append(l, s) } -func (m seriesHashmap) del(hash uint64, lset labels.Labels) { +func (m *seriesHashmap) del(hash uint64, lset labels.Labels) { var rem []*memSeries unique, found := m.unique[hash] switch { diff --git a/tsdb/head_test.go b/tsdb/head_test.go index f2325039a..64237e76a 100644 --- a/tsdb/head_test.go +++ b/tsdb/head_test.go @@ -5542,3 +5542,54 @@ func TestHeadCompactionWhileAppendAndCommitExemplar(t *testing.T) { app.Commit() h.Close() } + +func labelsWithHashCollision() (labels.Labels, labels.Labels) { + // These two series have the same XXHash; thanks to https://github.com/pstibrany/labels_hash_collisions + ls1 := labels.FromStrings("__name__", "metric", "lbl1", "value", "lbl2", "l6CQ5y") + ls2 := labels.FromStrings("__name__", "metric", "lbl1", "value", "lbl2", "v7uDlF") + + if ls1.Hash() != ls2.Hash() { + // These ones are the same when using -tags stringlabels + ls1 = labels.FromStrings("__name__", "metric", "lbl", "HFnEaGl") + ls2 = labels.FromStrings("__name__", "metric", "lbl", "RqcXatm") + } + + if ls1.Hash() != ls2.Hash() { + panic("This code needs to be updated: find new labels with colliding hash values.") + } + + return ls1, ls2 +} + +func TestStripeSeries_getOrSet(t *testing.T) { + lbls1, lbls2 := labelsWithHashCollision() + ms1 := memSeries{ + lset: lbls1, + } + ms2 := memSeries{ + lset: lbls2, + } + hash := lbls1.Hash() + s := newStripeSeries(1, noopSeriesLifecycleCallback{}) + + got, created, err := s.getOrSet(hash, lbls1, func() *memSeries { + return &ms1 + }) + require.NoError(t, err) + require.True(t, created) + require.Same(t, &ms1, got) + + // Add a conflicting series + got, created, err = s.getOrSet(hash, lbls2, func() *memSeries { + return &ms2 + }) + require.NoError(t, err) + require.True(t, created) + require.Same(t, &ms2, got) + + // Verify that we can get both of the series despite the hash collision + got = s.getByHash(hash, lbls1) + require.Same(t, &ms1, got) + got = s.getByHash(hash, lbls2) + require.Same(t, &ms2, got) +}