Skip to content

Commit e33e141

Browse files
alexweavpracucci
andauthored
ingest-storage: Do not deduplicate metadata by family name when unmarshalling (#12804)
#### What this PR does This PR builds on the refactor in #12792. By default, RW2 deserialization deduplicates metric metadata by family name. All metadata beyond the first instance for a given family are discarded. This PR adds a new deserialization option, called `SkipDeduplicateMetadata`, plus a new `metadataSet` implementation that avoids the deduplication and preserves all given metadata and ordering. This is done so that we can preserve metadata to the letter, even if it conflicts, ensuring we [adhere to prometheus behavior](https://prometheus.io/docs/prometheus/latest/querying/api/#querying-metadata) particularly in RW1.0. RW1 requests don't have the duplication that natural RW2 requests tend to have. Natural RW2 requests will already be deduplicated in the distributor before they reach the ingest-storage layer, so this change does not add undue duplication for clients sending RW2.0 into Mimir. #### Which issue(s) this PR fixes or relates to contrib grafana/mimir-squad#2253 #### Checklist - [x] Tests updated. - [ ] Documentation added. - [ ] `CHANGELOG.md` updated - the order of entries should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If changelog entry is not needed, please add the `changelog-not-needed` label to the PR. - [ ] [`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md) updated with experimental features. --------- Co-authored-by: Marco Pracucci <[email protected]>
1 parent e64c326 commit e33e141

File tree

11 files changed

+178
-36
lines changed

11 files changed

+178
-36
lines changed

pkg/mimirpb/compat_rw2_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,50 @@ func TestRW2Unmarshal(t *testing.T) {
797797
require.Equal(t, received.Metadata[0].Help, "It's a cool series, but old description.")
798798
require.Equal(t, received.Metadata[0].Unit, "megawatts")
799799
})
800+
801+
t.Run("conflicting metadata, skipDeduplicateMetadata is true, both metadata and their order is preserved", func(t *testing.T) {
802+
writeRequest := &WriteRequest{
803+
SymbolsRW2: []string{"", "__name__", "my_cool_series", "It's a cool series, but old description.", "It's a cool series, but new description.", "megawatts"},
804+
TimeseriesRW2: []TimeSeriesRW2{
805+
{
806+
LabelsRefs: []uint32{1, 2},
807+
Metadata: MetadataRW2{
808+
Type: METRIC_TYPE_COUNTER,
809+
HelpRef: 3,
810+
UnitRef: 5,
811+
},
812+
},
813+
{
814+
LabelsRefs: []uint32{1, 2},
815+
Metadata: MetadataRW2{
816+
Type: METRIC_TYPE_COUNTER,
817+
HelpRef: 4,
818+
UnitRef: 5,
819+
},
820+
},
821+
},
822+
}
823+
data, err := writeRequest.Marshal()
824+
require.NoError(t, err)
825+
826+
// Unmarshal the data back into Mimir's WriteRequest.
827+
received := PreallocWriteRequest{
828+
SkipDeduplicateMetadata: true,
829+
}
830+
received.UnmarshalFromRW2 = true
831+
err = received.Unmarshal(data)
832+
require.NoError(t, err)
833+
834+
require.Len(t, received.Metadata, 2)
835+
require.Equal(t, received.Metadata[0].MetricFamilyName, "my_cool_series")
836+
require.Equal(t, received.Metadata[0].Type, COUNTER)
837+
require.Equal(t, received.Metadata[0].Help, "It's a cool series, but old description.")
838+
require.Equal(t, received.Metadata[0].Unit, "megawatts")
839+
require.Equal(t, received.Metadata[1].MetricFamilyName, "my_cool_series")
840+
require.Equal(t, received.Metadata[1].Type, COUNTER)
841+
require.Equal(t, received.Metadata[1].Help, "It's a cool series, but new description.")
842+
require.Equal(t, received.Metadata[1].Unit, "megawatts")
843+
})
800844
}
801845

802846
func makeTestRW2WriteRequest(syms *test.SymbolTableBuilder) *WriteRequest {

pkg/mimirpb/custom.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,13 @@ type MarshalerWithSize interface {
452452
MarshalWithSize(size int) ([]byte, error)
453453
}
454454

455+
func metadataSetFromSettings(skipDeduplicateMetadata bool) metadataSet {
456+
if skipDeduplicateMetadata {
457+
return newPassthroughMetadataSet()
458+
}
459+
return newDedupingMetadataSet()
460+
}
461+
455462
// metadataSet is the collection of metadata within a request.
456463
// It keeps the order at which metadata is added. Metadata may optionally be deduplicated by family name.
457464
type metadataSet interface {
@@ -461,6 +468,7 @@ type metadataSet interface {
461468
}
462469

463470
var _ metadataSet = dedupingMetadataSet{}
471+
var _ metadataSet = &passthroughMetadataSet{}
464472

465473
// dedupingMetadataSet is a metadataSet that only stores one metadata per metric family.
466474
// Only the first metadata seen for a given family is kept.
@@ -496,6 +504,28 @@ func (m dedupingMetadataSet) slice() []*MetricMetadata {
496504
return result
497505
}
498506

507+
type passthroughMetadataSet struct {
508+
metadata []*MetricMetadata
509+
}
510+
511+
func newPassthroughMetadataSet() *passthroughMetadataSet {
512+
return &passthroughMetadataSet{
513+
metadata: make([]*MetricMetadata, 0),
514+
}
515+
}
516+
517+
func (m *passthroughMetadataSet) add(family string, mm MetricMetadata) {
518+
m.metadata = append(m.metadata, &mm)
519+
}
520+
521+
func (m *passthroughMetadataSet) len() int {
522+
return len(m.metadata)
523+
}
524+
525+
func (m *passthroughMetadataSet) slice() []*MetricMetadata {
526+
return m.metadata
527+
}
528+
499529
// orderAwareMetricMetadata is a tuple (index, metadata) that knows its own position in a metadata slice.
500530
// It's tied to custom logic that unmarshals RW2 metadata into a map, and allows us to
501531
// remember the order that metadata arrived in when unmarshalling.

pkg/mimirpb/mimir.pb.go

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)