github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/storage/m3/storage_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package m3
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"math"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/m3db/m3/src/dbnode/client"
    32  	"github.com/m3db/m3/src/dbnode/encoding"
    33  	"github.com/m3db/m3/src/dbnode/storage/index"
    34  	"github.com/m3db/m3/src/query/block"
    35  	"github.com/m3db/m3/src/query/generated/proto/prompb"
    36  	"github.com/m3db/m3/src/query/models"
    37  	"github.com/m3db/m3/src/query/storage"
    38  	"github.com/m3db/m3/src/query/storage/m3/consolidators"
    39  	"github.com/m3db/m3/src/query/storage/m3/storagemetadata"
    40  	"github.com/m3db/m3/src/query/test/seriesiter"
    41  	"github.com/m3db/m3/src/query/ts"
    42  	"github.com/m3db/m3/src/x/ident"
    43  	"github.com/m3db/m3/src/x/instrument"
    44  	"github.com/m3db/m3/src/x/sync"
    45  	bytetest "github.com/m3db/m3/src/x/test"
    46  	xtest "github.com/m3db/m3/src/x/test"
    47  	xtime "github.com/m3db/m3/src/x/time"
    48  
    49  	"github.com/golang/mock/gomock"
    50  	"github.com/stretchr/testify/assert"
    51  	"github.com/stretchr/testify/require"
    52  )
    53  
    54  const (
    55  	test1MonthRetention  = 30 * 24 * time.Hour
    56  	test3MonthRetention  = 90 * 24 * time.Hour
    57  	test6MonthRetention  = 180 * 24 * time.Hour
    58  	test1YearRetention   = 365 * 24 * time.Hour
    59  	testLongestRetention = test1YearRetention
    60  )
    61  
    62  var testFetchResponseMetadata = client.FetchResponseMetadata{Exhaustive: true}
    63  
    64  type testSessions struct {
    65  	unaggregated1MonthRetention                       *client.MockSession
    66  	aggregated1MonthRetention1MinuteResolution        *client.MockSession
    67  	aggregated3MonthRetention5MinuteResolution        *client.MockSession
    68  	aggregatedPartial6MonthRetention1MinuteResolution *client.MockSession
    69  	aggregated1YearRetention10MinuteResolution        *client.MockSession
    70  }
    71  
    72  func (s testSessions) forEach(fn func(session *client.MockSession)) {
    73  	for _, session := range []*client.MockSession{
    74  		s.unaggregated1MonthRetention,
    75  		s.aggregated1MonthRetention1MinuteResolution,
    76  		s.aggregated3MonthRetention5MinuteResolution,
    77  		s.aggregatedPartial6MonthRetention1MinuteResolution,
    78  		s.aggregated1YearRetention10MinuteResolution,
    79  	} {
    80  		fn(session)
    81  	}
    82  }
    83  
    84  func setup(
    85  	t *testing.T,
    86  	ctrl *gomock.Controller,
    87  ) (storage.Storage, testSessions) {
    88  	unaggregated1MonthRetention := client.NewMockSession(ctrl)
    89  	aggregated1MonthRetention1MinuteResolution := client.NewMockSession(ctrl)
    90  	aggregated3MonthRetention5MinuteResolution := client.NewMockSession(ctrl)
    91  	aggregatedPartial6MonthRetention1MinuteResolution := client.NewMockSession(ctrl)
    92  	aggregated1YearRetention10MinuteResolution := client.NewMockSession(ctrl)
    93  	clusters, err := NewClusters(UnaggregatedClusterNamespaceDefinition{
    94  		NamespaceID: ident.StringID("metrics_unaggregated"),
    95  		Session:     unaggregated1MonthRetention,
    96  		Retention:   test1MonthRetention,
    97  	}, AggregatedClusterNamespaceDefinition{
    98  		NamespaceID: ident.StringID("metrics_aggregated_1m:30d"),
    99  		Session:     aggregated1MonthRetention1MinuteResolution,
   100  		Retention:   test1MonthRetention,
   101  		Resolution:  time.Minute,
   102  	}, AggregatedClusterNamespaceDefinition{
   103  		NamespaceID: ident.StringID("metrics_aggregated_5m:90d"),
   104  		Session:     aggregated3MonthRetention5MinuteResolution,
   105  		Retention:   test3MonthRetention,
   106  		Resolution:  5 * time.Minute,
   107  	}, AggregatedClusterNamespaceDefinition{
   108  		NamespaceID: ident.StringID("metrics_aggregated_partial_1m:180d"),
   109  		Session:     aggregatedPartial6MonthRetention1MinuteResolution,
   110  		Retention:   test6MonthRetention,
   111  		Resolution:  1 * time.Minute,
   112  		Downsample:  &ClusterNamespaceDownsampleOptions{All: false},
   113  	}, AggregatedClusterNamespaceDefinition{
   114  		NamespaceID: ident.StringID("metrics_aggregated_10m:365d"),
   115  		Session:     aggregated1YearRetention10MinuteResolution,
   116  		Retention:   test1YearRetention,
   117  		Resolution:  10 * time.Minute,
   118  	})
   119  	require.NoError(t, err)
   120  	return newTestStorage(t, clusters), testSessions{
   121  		unaggregated1MonthRetention:                       unaggregated1MonthRetention,
   122  		aggregated1MonthRetention1MinuteResolution:        aggregated1MonthRetention1MinuteResolution,
   123  		aggregated3MonthRetention5MinuteResolution:        aggregated3MonthRetention5MinuteResolution,
   124  		aggregatedPartial6MonthRetention1MinuteResolution: aggregatedPartial6MonthRetention1MinuteResolution,
   125  		aggregated1YearRetention10MinuteResolution:        aggregated1YearRetention10MinuteResolution,
   126  	}
   127  }
   128  
   129  func newTestStorage(t *testing.T, clusters Clusters) storage.Storage {
   130  	writePool, err := sync.NewPooledWorkerPool(10,
   131  		sync.NewPooledWorkerPoolOptions())
   132  	require.NoError(t, err)
   133  	writePool.Init()
   134  	tagOpts := models.NewTagOptions().SetMetricName([]byte("name"))
   135  	opts := NewOptions(encoding.NewOptions()).
   136  		SetWriteWorkerPool(writePool).
   137  		SetLookbackDuration(time.Minute).
   138  		SetTagOptions(tagOpts)
   139  	storage, err := NewStorage(clusters, opts, instrument.NewTestOptions(t))
   140  	require.NoError(t, err)
   141  	return storage
   142  }
   143  
   144  func newFetchReq() *storage.FetchQuery {
   145  	matchers := models.Matchers{
   146  		{
   147  			Type:  models.MatchEqual,
   148  			Name:  []byte("foo"),
   149  			Value: []byte("bar"),
   150  		},
   151  		{
   152  			Type:  models.MatchEqual,
   153  			Name:  []byte("biz"),
   154  			Value: []byte("baz"),
   155  		},
   156  	}
   157  	return &storage.FetchQuery{
   158  		TagMatchers: matchers,
   159  		Start:       time.Now().Add(-10 * time.Minute),
   160  		End:         time.Now(),
   161  	}
   162  }
   163  
   164  func newWriteQuery(t *testing.T) *storage.WriteQuery {
   165  	tags := models.EmptyTags().AddTags([]models.Tag{
   166  		{Name: []byte("foo"), Value: []byte("bar")},
   167  		{Name: []byte("biz"), Value: []byte("baz")},
   168  	})
   169  
   170  	q, err := storage.NewWriteQuery(storage.WriteQueryOptions{
   171  		Tags: tags,
   172  		Unit: xtime.Millisecond,
   173  		Datapoints: ts.Datapoints{
   174  			{
   175  				Timestamp: xtime.Now(),
   176  				Value:     1.0,
   177  			},
   178  			{
   179  				Timestamp: xtime.Now().Add(-10 * time.Second),
   180  				Value:     2.0,
   181  			},
   182  		},
   183  		Attributes: storagemetadata.Attributes{
   184  			MetricsType: storagemetadata.UnaggregatedMetricsType,
   185  		},
   186  	})
   187  	require.NoError(t, err)
   188  
   189  	return q
   190  }
   191  
   192  func setupLocalWrite(t *testing.T, ctrl *gomock.Controller) storage.Storage {
   193  	store, sessions := setup(t, ctrl)
   194  	session := sessions.unaggregated1MonthRetention
   195  	session.EXPECT().WriteTagged(gomock.Any(), gomock.Any(), gomock.Any(),
   196  		gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   197  	return store
   198  }
   199  
   200  func TestQueryStorageMetadataAttributes(t *testing.T) {
   201  	ctrl := xtest.NewController(t)
   202  	defer ctrl.Finish()
   203  	store, _ := setup(t, ctrl)
   204  
   205  	unaggAttrs, err := store.QueryStorageMetadataAttributes(
   206  		context.Background(),
   207  		time.Now().Add(-10*time.Minute),
   208  		time.Now(),
   209  		buildFetchOpts(),
   210  	)
   211  	require.NoError(t, err)
   212  	require.Equal(t, []storagemetadata.Attributes{
   213  		{
   214  			MetricsType: storagemetadata.UnaggregatedMetricsType,
   215  			Retention:   test1MonthRetention,
   216  		},
   217  	}, unaggAttrs)
   218  
   219  	aggAttrs, err := store.QueryStorageMetadataAttributes(
   220  		context.Background(),
   221  		time.Now().Add(-120*24*time.Hour),
   222  		time.Now(),
   223  		buildFetchOpts(),
   224  	)
   225  	require.NoError(t, err)
   226  	require.Equal(t, []storagemetadata.Attributes{
   227  		{
   228  			MetricsType: storagemetadata.AggregatedMetricsType,
   229  			Retention:   test1YearRetention,
   230  			Resolution:  10 * time.Minute,
   231  		},
   232  		{
   233  			MetricsType: storagemetadata.AggregatedMetricsType,
   234  			Retention:   test6MonthRetention,
   235  			Resolution:  1 * time.Minute,
   236  		},
   237  	}, aggAttrs)
   238  }
   239  
   240  func TestLocalWriteEmpty(t *testing.T) {
   241  	ctrl := xtest.NewController(t)
   242  	defer ctrl.Finish()
   243  	store := setupLocalWrite(t, ctrl)
   244  	err := store.Write(context.TODO(), nil)
   245  	assert.Error(t, err)
   246  }
   247  
   248  func TestLocalWriteSuccess(t *testing.T) {
   249  	ctrl := xtest.NewController(t)
   250  	defer ctrl.Finish()
   251  	store := setupLocalWrite(t, ctrl)
   252  	writeQuery := newWriteQuery(t)
   253  	err := store.Write(context.TODO(), writeQuery)
   254  	assert.NoError(t, err)
   255  	assert.NoError(t, store.Close())
   256  }
   257  
   258  func TestLocalWriteAggregatedNoClusterNamespaceError(t *testing.T) {
   259  	ctrl := xtest.NewController(t)
   260  	defer ctrl.Finish()
   261  	store, _ := setup(t, ctrl)
   262  
   263  	opts := newWriteQuery(t).Options()
   264  
   265  	// Use unsupported retention/resolution
   266  	opts.Attributes = storagemetadata.Attributes{
   267  		MetricsType: storagemetadata.AggregatedMetricsType,
   268  		Retention:   1234,
   269  		Resolution:  5678,
   270  	}
   271  
   272  	writeQuery, err := storage.NewWriteQuery(opts)
   273  	require.NoError(t, err)
   274  
   275  	err = store.Write(context.TODO(), writeQuery)
   276  	assert.Error(t, err)
   277  	assert.True(t, strings.Contains(err.Error(), "no configured cluster namespace"),
   278  		fmt.Sprintf("unexpected error string: %v", err.Error()))
   279  }
   280  
   281  func TestLocalWriteUnaggregatedNamespaceUninitializedError(t *testing.T) {
   282  	t.Parallel()
   283  
   284  	ctrl := xtest.NewController(t)
   285  	defer ctrl.Finish()
   286  	// We setup an empty dynamic cluster, which will by default
   287  	// have an uninitialized unaggregated namespace.
   288  	store := newTestStorage(t, &dynamicCluster{})
   289  
   290  	opts := newWriteQuery(t).Options()
   291  
   292  	writeQuery, err := storage.NewWriteQuery(opts)
   293  	require.NoError(t, err)
   294  
   295  	err = store.Write(context.TODO(), writeQuery)
   296  	assert.Error(t, err)
   297  	assert.True(t, strings.Contains(err.Error(), "unaggregated namespace is not yet initialized"),
   298  		fmt.Sprintf("unexpected error string: %v", err.Error()))
   299  }
   300  
   301  func TestWriteToReadOnlyNamespaceFail(t *testing.T) {
   302  	ctrl := xtest.NewController(t)
   303  	defer ctrl.Finish()
   304  
   305  	clusters, err := NewClusters(
   306  		UnaggregatedClusterNamespaceDefinition{
   307  			NamespaceID: ident.StringID("unaggregated"),
   308  			Session:     client.NewMockSession(ctrl),
   309  			Retention:   time.Hour,
   310  		},
   311  		AggregatedClusterNamespaceDefinition{
   312  			NamespaceID: ident.StringID("aggregated_readonly"),
   313  			Session:     client.NewMockSession(ctrl),
   314  			Retention:   24 * time.Hour,
   315  			Resolution:  time.Minute,
   316  			ReadOnly:    true,
   317  		},
   318  	)
   319  	require.NoError(t, err)
   320  
   321  	store := newTestStorage(t, clusters)
   322  
   323  	opts := newWriteQuery(t).Options()
   324  
   325  	opts.Attributes = storagemetadata.Attributes{
   326  		MetricsType: storagemetadata.AggregatedMetricsType,
   327  		Retention:   24 * time.Hour,
   328  		Resolution:  time.Minute,
   329  	}
   330  
   331  	writeQuery, err := storage.NewWriteQuery(opts)
   332  	require.NoError(t, err)
   333  
   334  	err = store.Write(context.TODO(), writeQuery)
   335  	assert.Error(t, err)
   336  	assert.True(t,
   337  		strings.Contains(err.Error(), "cannot write to read only namespace aggregated_readonly"),
   338  		fmt.Sprintf("unexpected error string: %v", err.Error()))
   339  }
   340  
   341  func TestLocalWriteAggregatedInvalidMetricsTypeError(t *testing.T) {
   342  	ctrl := xtest.NewController(t)
   343  	defer ctrl.Finish()
   344  	store, _ := setup(t, ctrl)
   345  
   346  	opts := newWriteQuery(t).Options()
   347  
   348  	// Use unsupported retention/resolution
   349  	opts.Attributes = storagemetadata.Attributes{
   350  		MetricsType: storagemetadata.MetricsType(math.MaxUint64),
   351  		Retention:   30 * 24 * time.Hour,
   352  	}
   353  
   354  	writeQuery, err := storage.NewWriteQuery(opts)
   355  	require.NoError(t, err)
   356  
   357  	err = store.Write(context.TODO(), writeQuery)
   358  	assert.Error(t, err)
   359  	assert.True(t, strings.Contains(err.Error(), "invalid write request"),
   360  		fmt.Sprintf("unexpected error string: %v", err.Error()))
   361  }
   362  
   363  func TestLocalWriteAggregatedSuccess(t *testing.T) {
   364  	ctrl := xtest.NewController(t)
   365  	defer ctrl.Finish()
   366  	store, sessions := setup(t, ctrl)
   367  
   368  	opts := newWriteQuery(t).Options()
   369  
   370  	// Use unsupported retention/resolution
   371  	opts.Attributes = storagemetadata.Attributes{
   372  		MetricsType: storagemetadata.AggregatedMetricsType,
   373  		Retention:   30 * 24 * time.Hour,
   374  		Resolution:  time.Minute,
   375  	}
   376  
   377  	writeQuery, err := storage.NewWriteQuery(opts)
   378  	require.NoError(t, err)
   379  
   380  	session := sessions.aggregated1MonthRetention1MinuteResolution
   381  	session.EXPECT().WriteTagged(gomock.Any(), gomock.Any(), gomock.Any(),
   382  		gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(len(writeQuery.Datapoints()))
   383  
   384  	err = store.Write(context.TODO(), writeQuery)
   385  	assert.NoError(t, err)
   386  	assert.NoError(t, store.Close())
   387  }
   388  
   389  func TestLocalRead(t *testing.T) {
   390  	ctrl := xtest.NewController(t)
   391  	defer ctrl.Finish()
   392  
   393  	store, sessions := setup(t, ctrl)
   394  	testTags := seriesiter.GenerateTag()
   395  
   396  	session := sessions.unaggregated1MonthRetention
   397  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   398  		Return(seriesiter.NewMockSeriesIters(ctrl, testTags, 1, 2),
   399  			testFetchResponseMetadata, nil)
   400  
   401  	searchReq := newFetchReq()
   402  	results, err := store.FetchProm(context.TODO(), searchReq, buildFetchOpts())
   403  	require.NoError(t, err)
   404  	assertFetchResult(t, results, testTags)
   405  }
   406  
   407  func TestLocalReadExceedsRetention(t *testing.T) {
   408  	ctrl := xtest.NewController(t)
   409  	defer ctrl.Finish()
   410  	store, sessions := setup(t, ctrl)
   411  	testTag := seriesiter.GenerateTag()
   412  
   413  	session := sessions.aggregated1YearRetention10MinuteResolution
   414  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   415  		Return(seriesiter.NewMockSeriesIters(ctrl, testTag, 1, 2),
   416  			testFetchResponseMetadata, nil)
   417  
   418  	searchReq := newFetchReq()
   419  	searchReq.Start = time.Now().Add(-2 * testLongestRetention)
   420  	searchReq.End = time.Now()
   421  	results, err := store.FetchProm(context.TODO(), searchReq, buildFetchOpts())
   422  	require.NoError(t, err)
   423  	assertFetchResult(t, results, testTag)
   424  }
   425  
   426  func TestFetchPromWithNamespaceStitching(t *testing.T) {
   427  	ctrl := xtest.NewController(t)
   428  	defer ctrl.Finish()
   429  
   430  	var (
   431  		end   = xtime.Now().Truncate(time.Hour)
   432  		start = end.Add(-48 * time.Hour)
   433  
   434  		testTag = seriesiter.GenerateTag()
   435  
   436  		unaggSession = client.NewMockSession(ctrl)
   437  		aggSession   = client.NewMockSession(ctrl)
   438  
   439  		unaggNamespaceID = ident.StringID("unaggregated")
   440  		aggNamespaceID   = ident.StringID("aggregated")
   441  
   442  		unaggQueryOpts, aggQueryOpts index.QueryOptions
   443  	)
   444  
   445  	clusters, err := NewClusters(
   446  		UnaggregatedClusterNamespaceDefinition{
   447  			NamespaceID: unaggNamespaceID,
   448  			Session:     unaggSession,
   449  			Retention:   24 * time.Hour,
   450  		},
   451  		AggregatedClusterNamespaceDefinition{
   452  			NamespaceID: aggNamespaceID,
   453  			Session:     aggSession,
   454  			Retention:   96 * time.Hour,
   455  			Resolution:  time.Minute,
   456  			DataLatency: 10 * time.Hour,
   457  		},
   458  	)
   459  	require.NoError(t, err)
   460  
   461  	store := newTestStorage(t, clusters)
   462  
   463  	unaggSession.EXPECT().FetchTagged(gomock.Any(), unaggNamespaceID, gomock.Any(), gomock.Any()).
   464  		DoAndReturn(func(
   465  			_ context.Context,
   466  			_ ident.ID,
   467  			_ index.Query,
   468  			opts index.QueryOptions,
   469  		) (encoding.SeriesIterators, client.FetchResponseMetadata, error) {
   470  			unaggQueryOpts = opts
   471  			return seriesiter.NewMockSeriesIters(ctrl, testTag, 1, 2), testFetchResponseMetadata, nil
   472  		})
   473  
   474  	aggSession.EXPECT().FetchTagged(gomock.Any(), aggNamespaceID, gomock.Any(), gomock.Any()).
   475  		DoAndReturn(func(
   476  			_ context.Context,
   477  			_ ident.ID,
   478  			_ index.Query,
   479  			opts index.QueryOptions,
   480  		) (encoding.SeriesIterators, client.FetchResponseMetadata, error) {
   481  			aggQueryOpts = opts
   482  			return seriesiter.NewMockSeriesIters(ctrl, testTag, 1, 2), testFetchResponseMetadata, nil
   483  		})
   484  
   485  	var (
   486  		fetchOpts = buildFetchOpts()
   487  		req       = newFetchReq()
   488  	)
   489  
   490  	req.Start = start.ToTime()
   491  	req.End = end.ToTime()
   492  
   493  	results, err := store.FetchProm(context.TODO(), req, fetchOpts)
   494  	require.NoError(t, err)
   495  
   496  	assert.Equal(t, start, aggQueryOpts.StartInclusive)
   497  	assert.Equal(t, aggQueryOpts.EndExclusive, unaggQueryOpts.StartInclusive) // stitching point
   498  	assert.Equal(t, end, unaggQueryOpts.EndExclusive)
   499  
   500  	assertFetchResult(t, results, testTag)
   501  }
   502  
   503  // TestLocalWriteWithExpiredContext ensures that writes are at least attempted
   504  // even with an expired context, this is so that data is not lost even if
   505  // the original writer has already disconnected.
   506  func TestLocalWriteWithExpiredContext(t *testing.T) {
   507  	ctrl := xtest.NewController(t)
   508  	defer ctrl.Finish()
   509  	store := setupLocalWrite(t, ctrl)
   510  	writeQuery := newWriteQuery(t)
   511  
   512  	past := time.Now().Add(-time.Minute)
   513  
   514  	ctx, cancel := context.WithDeadline(context.Background(), past)
   515  	defer cancel()
   516  
   517  	// Ensure expired.
   518  	var expired bool
   519  	select {
   520  	case <-ctx.Done():
   521  		expired = true
   522  	default:
   523  	}
   524  	require.True(t, expired, "context expected to be expired")
   525  
   526  	err := store.Write(ctx, writeQuery)
   527  	assert.NoError(t, err)
   528  	assert.NoError(t, store.Close())
   529  }
   530  
   531  // TestLocalWritesWithExpiredContext ensures that writes are at least attempted
   532  // even with an expired context, this is so that data is not lost even if
   533  // the original writer has already disconnected.
   534  func TestLocalWritesWithExpiredContext(t *testing.T) {
   535  	ctrl := xtest.NewController(t)
   536  	defer ctrl.Finish()
   537  	store := setupLocalWrite(t, ctrl)
   538  	writeQueryOpts := newWriteQuery(t).Options()
   539  	writeQueryOpts.Datapoints = ts.Datapoints{
   540  		ts.Datapoint{
   541  			Timestamp: xtime.Now(),
   542  			Value:     42,
   543  		},
   544  		ts.Datapoint{
   545  			Timestamp: xtime.Now(),
   546  			Value:     84,
   547  		},
   548  	}
   549  	writeQuery, err := storage.NewWriteQuery(writeQueryOpts)
   550  	require.NoError(t, err)
   551  
   552  	past := time.Now().Add(-time.Minute)
   553  
   554  	ctx, cancel := context.WithDeadline(context.Background(), past)
   555  	defer cancel()
   556  
   557  	// Ensure expired.
   558  	var expired bool
   559  	select {
   560  	case <-ctx.Done():
   561  		expired = true
   562  	default:
   563  	}
   564  	require.True(t, expired, "context expected to be expired")
   565  
   566  	err = store.Write(ctx, writeQuery)
   567  	assert.NoError(t, err)
   568  	assert.NoError(t, store.Close())
   569  }
   570  
   571  func buildFetchOpts() *storage.FetchOptions {
   572  	opts := storage.NewFetchOptions()
   573  	opts.SeriesLimit = 100
   574  	opts.MaxMetricMetadataStats = 1
   575  	return opts
   576  }
   577  
   578  func TestLocalReadExceedsUnaggregatedRetentionWithinAggregatedRetention(t *testing.T) {
   579  	ctrl := xtest.NewController(t)
   580  	defer ctrl.Finish()
   581  	store, sessions := setup(t, ctrl)
   582  	testTag := seriesiter.GenerateTag()
   583  
   584  	session := sessions.aggregated3MonthRetention5MinuteResolution
   585  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   586  		Return(seriesiter.NewMockSeriesIters(ctrl, testTag, 1, 2),
   587  			testFetchResponseMetadata, nil)
   588  
   589  	session = sessions.aggregatedPartial6MonthRetention1MinuteResolution
   590  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   591  		Return(encoding.EmptySeriesIterators,
   592  			testFetchResponseMetadata, nil)
   593  
   594  	// Test searching between 1month and 3 months (so 2 months) to hit multiple aggregated
   595  	// namespaces that we need to choose from
   596  	searchReq := newFetchReq()
   597  	searchReq.Start = time.Now().Add(-2 * test1MonthRetention)
   598  	searchReq.End = time.Now()
   599  	results, err := store.FetchProm(context.TODO(), searchReq, buildFetchOpts())
   600  	require.NoError(t, err)
   601  	assertFetchResult(t, results, testTag)
   602  }
   603  
   604  func TestLocalReadExceedsAggregatedButNotUnaggregatedAndPartialAggregated(t *testing.T) {
   605  	ctrl := xtest.NewController(t)
   606  	defer ctrl.Finish()
   607  
   608  	unaggregated1MonthRetention := client.NewMockSession(ctrl)
   609  	aggregatedPartial6MonthRetention1MinuteResolution := client.NewMockSession(ctrl)
   610  
   611  	clusters, err := NewClusters(UnaggregatedClusterNamespaceDefinition{
   612  		NamespaceID: ident.StringID("metrics_unaggregated"),
   613  		Session:     unaggregated1MonthRetention,
   614  		Retention:   test1MonthRetention,
   615  	}, AggregatedClusterNamespaceDefinition{
   616  		NamespaceID: ident.StringID("metrics_aggregated_1m:180d"),
   617  		Session:     aggregatedPartial6MonthRetention1MinuteResolution,
   618  		Retention:   test6MonthRetention,
   619  		Resolution:  time.Minute,
   620  		Downsample:  &ClusterNamespaceDownsampleOptions{All: false},
   621  	})
   622  	require.NoError(t, err)
   623  
   624  	store := newTestStorage(t, clusters)
   625  
   626  	testTag := seriesiter.GenerateTag()
   627  
   628  	session := unaggregated1MonthRetention
   629  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   630  		Return(seriesiter.NewMockSeriesIters(ctrl, testTag, 1, 2),
   631  			testFetchResponseMetadata, nil)
   632  
   633  	session = aggregatedPartial6MonthRetention1MinuteResolution
   634  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   635  		Return(encoding.EmptySeriesIterators,
   636  			testFetchResponseMetadata, nil)
   637  
   638  	// Test searching past unaggregated namespace and verify that we fan out to both
   639  	// the unaggregated namespaces and the partial aggregated namespace
   640  	searchReq := newFetchReq()
   641  	searchReq.Start = time.Now().Add(-2 * test1MonthRetention)
   642  	searchReq.End = time.Now()
   643  	results, err := store.FetchProm(context.TODO(), searchReq, buildFetchOpts())
   644  	require.NoError(t, err)
   645  	assertFetchResult(t, results, testTag)
   646  }
   647  
   648  func TestLocalReadExceedsAggregatedAndPartialAggregated(t *testing.T) {
   649  	ctrl := xtest.NewController(t)
   650  	defer ctrl.Finish()
   651  
   652  	unaggregated1MonthRetention := client.NewMockSession(ctrl)
   653  	aggregated3MonthRetention5MinuteResolution := client.NewMockSession(ctrl)
   654  	aggregatedPartial6MonthRetention1MinuteResolution := client.NewMockSession(ctrl)
   655  
   656  	clusters, err := NewClusters(UnaggregatedClusterNamespaceDefinition{
   657  		NamespaceID: ident.StringID("metrics_unaggregated"),
   658  		Session:     unaggregated1MonthRetention,
   659  		Retention:   test1MonthRetention,
   660  	}, AggregatedClusterNamespaceDefinition{
   661  		NamespaceID: ident.StringID("metrics_aggregated_5m:90d"),
   662  		Session:     aggregated3MonthRetention5MinuteResolution,
   663  		Retention:   test3MonthRetention,
   664  		Resolution:  5 * time.Minute,
   665  	}, AggregatedClusterNamespaceDefinition{
   666  		NamespaceID: ident.StringID("metrics_aggregated_1m:180d"),
   667  		Session:     aggregatedPartial6MonthRetention1MinuteResolution,
   668  		Retention:   test6MonthRetention,
   669  		Resolution:  time.Minute,
   670  		Downsample:  &ClusterNamespaceDownsampleOptions{All: false},
   671  	})
   672  	require.NoError(t, err)
   673  
   674  	store := newTestStorage(t, clusters)
   675  
   676  	testTag := seriesiter.GenerateTag()
   677  
   678  	session := aggregated3MonthRetention5MinuteResolution
   679  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   680  		Return(seriesiter.NewMockSeriesIters(ctrl, testTag, 1, 2),
   681  			testFetchResponseMetadata, nil)
   682  
   683  	session = aggregatedPartial6MonthRetention1MinuteResolution
   684  	session.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   685  		Return(encoding.EmptySeriesIterators,
   686  			testFetchResponseMetadata, nil)
   687  
   688  	// Test searching past aggregated and partially aggregated namespace, fan out to both
   689  	searchReq := newFetchReq()
   690  	searchReq.Start = time.Now().Add(-2 * test6MonthRetention)
   691  	searchReq.End = time.Now()
   692  	results, err := store.FetchProm(context.TODO(), searchReq, buildFetchOpts())
   693  	require.NoError(t, err)
   694  	assertFetchResult(t, results, testTag)
   695  }
   696  
   697  func assertFetchResult(t *testing.T, results storage.PromResult, testTag ident.Tag) {
   698  	require.NotNil(t, results.PromResult)
   699  	series := results.PromResult.GetTimeseries()
   700  	meta := results.Metadata
   701  	require.Equal(t, 1, len(series))
   702  	labels := series[0].GetLabels()
   703  	require.Equal(t, 1, len(labels))
   704  	l := labels[0]
   705  	assert.Equal(t, testTag.Name.String(), string(l.GetName()))
   706  	assert.Equal(t, testTag.Value.String(), string(l.GetValue()))
   707  	merged := meta.MetadataByNameMerged()
   708  	assert.Equal(t, 1, meta.FetchedSeriesCount)
   709  	assert.Equal(t, block.ResultMetricMetadata{Unaggregated: 1, WithSamples: 1}, merged)
   710  }
   711  
   712  func TestLocalSearchError(t *testing.T) {
   713  	ctrl := xtest.NewController(t)
   714  	defer ctrl.Finish()
   715  	store, sessions := setup(t, ctrl)
   716  
   717  	// Query is just for last 10mins to only expect unaggregated namespace.
   718  	for _, session := range []*client.MockSession{
   719  		sessions.unaggregated1MonthRetention,
   720  	} {
   721  		session.EXPECT().FetchTaggedIDs(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   722  			Return(nil, client.FetchResponseMetadata{Exhaustive: false}, fmt.Errorf("an error"))
   723  		session.EXPECT().IteratorPools().
   724  			Return(nil, nil).AnyTimes()
   725  	}
   726  
   727  	// Issue query for last 10mins.
   728  	searchReq := newFetchReq()
   729  	searchReq.Start = time.Now().Add(-10 * time.Minute)
   730  	searchReq.End = time.Now()
   731  	_, err := store.SearchSeries(context.TODO(), searchReq, buildFetchOpts())
   732  	assert.Error(t, err)
   733  }
   734  
   735  func TestLocalSearchSuccess(t *testing.T) {
   736  	ctrl := xtest.NewController(t)
   737  	defer ctrl.Finish()
   738  	store, sessions := setup(t, ctrl)
   739  
   740  	type testFetchTaggedID struct {
   741  		id        string
   742  		namespace string
   743  		tagName   string
   744  		tagValue  string
   745  	}
   746  
   747  	fetches := []testFetchTaggedID{
   748  		{
   749  			id:        "foo",
   750  			namespace: "metrics_unaggregated",
   751  			tagName:   "qux",
   752  			tagValue:  "qaz",
   753  		},
   754  	}
   755  
   756  	sessions.forEach(func(session *client.MockSession) {
   757  		var f testFetchTaggedID
   758  		switch {
   759  		case session == sessions.unaggregated1MonthRetention:
   760  			f = fetches[0]
   761  		default:
   762  			// Not expecting from other (partial) namespaces
   763  			return
   764  		}
   765  		iter := client.NewMockTaggedIDsIterator(ctrl)
   766  		gomock.InOrder(
   767  			iter.EXPECT().Next().Return(true),
   768  			iter.EXPECT().Current().Return(
   769  				ident.StringID(f.namespace),
   770  				ident.StringID(f.id),
   771  				ident.NewTagsIterator(ident.NewTags(
   772  					ident.Tag{
   773  						Name:  ident.StringID(f.tagName),
   774  						Value: ident.StringID(f.tagValue),
   775  					})),
   776  			),
   777  			iter.EXPECT().Next().Return(false),
   778  			iter.EXPECT().Err().Return(nil),
   779  			iter.EXPECT().Finalize(),
   780  		)
   781  
   782  		session.EXPECT().FetchTaggedIDs(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   783  			Return(iter, testFetchResponseMetadata, nil)
   784  
   785  		session.EXPECT().IteratorPools().
   786  			Return(nil, nil).AnyTimes()
   787  	})
   788  	searchReq := newFetchReq()
   789  	searchReq.Start = time.Now().Add(-10 * time.Minute)
   790  	searchReq.End = time.Now()
   791  	result, err := store.SearchSeries(context.TODO(), searchReq, buildFetchOpts())
   792  	require.NoError(t, err)
   793  
   794  	require.Equal(t, len(fetches), len(result.Metrics))
   795  
   796  	expected := make(map[string]testFetchTaggedID)
   797  	for _, f := range fetches {
   798  		expected[f.id] = f
   799  	}
   800  
   801  	actual := make(map[string]models.Metric)
   802  	for _, m := range result.Metrics {
   803  		actual[string(m.ID)] = m
   804  	}
   805  
   806  	for id, actual := range actual {
   807  		expected, ok := expected[id]
   808  		require.True(t, ok)
   809  
   810  		assert.Equal(t, []byte(expected.id), actual.ID)
   811  		assert.Equal(t, []models.Tag{{
   812  			Name: []byte(expected.tagName), Value: []byte(expected.tagValue),
   813  		}}, actual.Tags.Tags)
   814  	}
   815  }
   816  
   817  func newCompleteTagsReq() *storage.CompleteTagsQuery {
   818  	matchers := models.Matchers{
   819  		{
   820  			Type:  models.MatchEqual,
   821  			Name:  []byte("qux"),
   822  			Value: []byte(".*"),
   823  		},
   824  	}
   825  
   826  	return &storage.CompleteTagsQuery{
   827  		CompleteNameOnly: false,
   828  		FilterNameTags:   [][]byte{[]byte("qux")},
   829  		TagMatchers:      matchers,
   830  	}
   831  }
   832  
   833  func TestLocalCompleteTagsSuccess(t *testing.T) {
   834  	ctrl := xtest.NewController(t)
   835  	defer ctrl.Finish()
   836  	store, sessions := setup(t, ctrl)
   837  
   838  	type testFetchTaggedID struct {
   839  		tagName  string
   840  		tagValue string
   841  	}
   842  
   843  	fetches := []testFetchTaggedID{
   844  		{
   845  			tagName:  "qux",
   846  			tagValue: "qaz",
   847  		},
   848  		{
   849  			tagName:  "aba",
   850  			tagValue: "quz",
   851  		},
   852  		{
   853  			tagName:  "qam",
   854  			tagValue: "qak",
   855  		},
   856  		{
   857  			tagName:  "qux",
   858  			tagValue: "qaz2",
   859  		},
   860  	}
   861  
   862  	sessions.forEach(func(session *client.MockSession) {
   863  		var f []testFetchTaggedID
   864  		switch {
   865  		case session == sessions.unaggregated1MonthRetention:
   866  			f = fetches
   867  		default:
   868  			// Not expecting from other (partial) namespaces
   869  			return
   870  		}
   871  
   872  		iter := client.NewMockAggregatedTagsIterator(ctrl)
   873  
   874  		var calls []*gomock.Call
   875  		calls = append(calls, []*gomock.Call{
   876  			iter.EXPECT().Remaining().Return(len(f)),
   877  		}...)
   878  		for _, elem := range f {
   879  			calls = append(calls, []*gomock.Call{
   880  				iter.EXPECT().Next().Return(true),
   881  				iter.EXPECT().Current().Return(
   882  					ident.StringID(elem.tagName),
   883  					ident.NewIDsIterator(ident.StringID(elem.tagValue)),
   884  				),
   885  			}...)
   886  		}
   887  		calls = append(calls, []*gomock.Call{
   888  			iter.EXPECT().Next().Return(false),
   889  			iter.EXPECT().Err().Return(nil),
   890  			iter.EXPECT().Finalize(),
   891  		}...)
   892  
   893  		gomock.InOrder(calls...)
   894  
   895  		session.EXPECT().Aggregate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   896  			Return(iter, testFetchResponseMetadata, nil)
   897  	})
   898  
   899  	req := newCompleteTagsReq()
   900  	req.Start = xtime.Now().Add(-10 * time.Minute)
   901  	req.End = xtime.Now()
   902  	result, err := store.CompleteTags(context.TODO(), req, buildFetchOpts())
   903  	require.NoError(t, err)
   904  
   905  	require.False(t, result.CompleteNameOnly)
   906  	require.Equal(t, 3, len(result.CompletedTags))
   907  	// NB: expected will be sorted alphabetically
   908  	expected := []consolidators.CompletedTag{
   909  		{
   910  			Name:   []byte("aba"),
   911  			Values: [][]byte{[]byte("quz")},
   912  		},
   913  		{
   914  			Name:   []byte("qam"),
   915  			Values: [][]byte{[]byte("qak")},
   916  		},
   917  		{
   918  			Name:   []byte("qux"),
   919  			Values: [][]byte{[]byte("qaz"), []byte("qaz2")},
   920  		},
   921  	}
   922  
   923  	assert.Equal(t, expected, result.CompletedTags)
   924  }
   925  
   926  func TestLocalCompleteTagsSuccessFinalize(t *testing.T) {
   927  	ctrl := xtest.NewController(t)
   928  	defer ctrl.Finish()
   929  
   930  	unagg := client.NewMockSession(ctrl)
   931  	clusters, err := NewClusters(UnaggregatedClusterNamespaceDefinition{
   932  		NamespaceID: ident.StringID("metrics_unaggregated"),
   933  		Session:     unagg,
   934  		Retention:   test1MonthRetention,
   935  	})
   936  
   937  	require.NoError(t, err)
   938  	store := newTestStorage(t, clusters)
   939  
   940  	name, value := ident.StringID("name"), ident.StringID("value")
   941  	iter := newAggregatedTagsIter(ctrl, name, value)
   942  
   943  	unagg.EXPECT().Aggregate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   944  		Return(iter, testFetchResponseMetadata, nil)
   945  
   946  	req := newCompleteTagsReq()
   947  	result, err := store.CompleteTags(context.TODO(), req, buildFetchOpts())
   948  	require.NoError(t, err)
   949  
   950  	require.False(t, result.CompleteNameOnly)
   951  	require.Equal(t, 1, len(result.CompletedTags))
   952  	// NB: expected will be sorted alphabetically
   953  	expected := []consolidators.CompletedTag{
   954  		{
   955  			Name:   []byte("name"),
   956  			Values: [][]byte{[]byte("value")},
   957  		},
   958  	}
   959  
   960  	require.Equal(t, expected, result.CompletedTags)
   961  
   962  	// ensure that the tag names and values are not backed by the same data.
   963  	n, v := result.CompletedTags[0].Name, result.CompletedTags[0].Values[0]
   964  	assert.False(t, bytetest.ByteSlicesBackedBySameData(name.Bytes(), n))
   965  	assert.False(t, bytetest.ByteSlicesBackedBySameData(value.Bytes(), v))
   966  }
   967  
   968  func TestCompleteTagsWithNamespaceStitching(t *testing.T) {
   969  	ctrl := xtest.NewController(t)
   970  	defer ctrl.Finish()
   971  
   972  	var (
   973  		end   = xtime.Now().Truncate(time.Hour)
   974  		start = end.Add(-48 * time.Hour)
   975  
   976  		name  = ident.StringID("name")
   977  		value = ident.StringID("value")
   978  
   979  		unaggSession = client.NewMockSession(ctrl)
   980  		aggSession   = client.NewMockSession(ctrl)
   981  
   982  		unaggNamespaceID = ident.StringID("unaggregated")
   983  		aggNamespaceID   = ident.StringID("aggregated")
   984  
   985  		unaggQueryOpts, aggQueryOpts index.AggregationOptions
   986  	)
   987  
   988  	clusters, err := NewClusters(
   989  		UnaggregatedClusterNamespaceDefinition{
   990  			NamespaceID: unaggNamespaceID,
   991  			Session:     unaggSession,
   992  			Retention:   24 * time.Hour,
   993  		},
   994  		AggregatedClusterNamespaceDefinition{
   995  			NamespaceID: aggNamespaceID,
   996  			Session:     aggSession,
   997  			Retention:   96 * time.Hour,
   998  			Resolution:  time.Minute,
   999  			DataLatency: 10 * time.Hour,
  1000  		},
  1001  	)
  1002  	require.NoError(t, err)
  1003  
  1004  	store := newTestStorage(t, clusters)
  1005  
  1006  	unaggIter := newAggregatedTagsIter(ctrl, name, value)
  1007  	unaggSession.EXPECT().Aggregate(gomock.Any(), unaggNamespaceID, gomock.Any(), gomock.Any()).
  1008  		DoAndReturn(func(
  1009  			_ context.Context,
  1010  			_ ident.ID,
  1011  			_ index.Query,
  1012  			opts index.AggregationOptions,
  1013  		) (client.AggregatedTagsIterator, client.FetchResponseMetadata, error) {
  1014  			unaggQueryOpts = opts
  1015  			return unaggIter, testFetchResponseMetadata, nil
  1016  		})
  1017  
  1018  	aggIter := newAggregatedTagsIter(ctrl, name, value)
  1019  	aggSession.EXPECT().Aggregate(gomock.Any(), aggNamespaceID, gomock.Any(), gomock.Any()).
  1020  		DoAndReturn(func(
  1021  			_ context.Context,
  1022  			_ ident.ID,
  1023  			_ index.Query,
  1024  			opts index.AggregationOptions,
  1025  		) (client.AggregatedTagsIterator, client.FetchResponseMetadata, error) {
  1026  			aggQueryOpts = opts
  1027  			return aggIter, testFetchResponseMetadata, nil
  1028  		})
  1029  
  1030  	var (
  1031  		fetchOpts = buildFetchOpts()
  1032  		req       = newCompleteTagsReq()
  1033  	)
  1034  
  1035  	req.Start = start
  1036  	req.End = end
  1037  
  1038  	result, err := store.CompleteTags(context.TODO(), req, fetchOpts)
  1039  	require.NoError(t, err)
  1040  
  1041  	assert.Equal(t, start, aggQueryOpts.StartInclusive)
  1042  	assert.Equal(t, aggQueryOpts.EndExclusive, unaggQueryOpts.StartInclusive) // stitching point
  1043  	assert.Equal(t, end, unaggQueryOpts.EndExclusive)
  1044  
  1045  	expected := []consolidators.CompletedTag{
  1046  		{
  1047  			Name:   []byte("name"),
  1048  			Values: [][]byte{[]byte("value")},
  1049  		},
  1050  	}
  1051  	assert.Equal(t, expected, result.CompletedTags)
  1052  }
  1053  
  1054  func TestInvalidBlockTypes(t *testing.T) {
  1055  	opts := NewOptions(encoding.NewOptions())
  1056  	s, err := NewStorage(nil, opts, instrument.NewOptions())
  1057  	require.NoError(t, err)
  1058  
  1059  	query := &storage.FetchQuery{}
  1060  	fetchOpts := &storage.FetchOptions{BlockType: models.TypeMultiBlock}
  1061  	defer instrument.SetShouldPanicEnvironmentVariable(true)()
  1062  	require.Panics(t, func() { _, _ = s.FetchBlocks(context.TODO(), query, fetchOpts) })
  1063  }
  1064  
  1065  func newAggregatedTagsIter(
  1066  	ctrl *gomock.Controller,
  1067  	name, value ident.ID,
  1068  ) client.AggregatedTagsIterator {
  1069  	iter := client.NewMockAggregatedTagsIterator(ctrl)
  1070  
  1071  	gomock.InOrder(
  1072  		iter.EXPECT().Remaining().Return(1),
  1073  		iter.EXPECT().Next().Return(true),
  1074  		iter.EXPECT().Current().Return(
  1075  			name,
  1076  			ident.NewIDsIterator(value),
  1077  		),
  1078  		iter.EXPECT().Next().Return(false),
  1079  		iter.EXPECT().Err().Return(nil),
  1080  		iter.EXPECT().Finalize().Do(func() {
  1081  			name.Finalize()
  1082  			value.Finalize()
  1083  		}),
  1084  	)
  1085  
  1086  	return iter
  1087  }
  1088  
  1089  func TestFindReservedLabel(t *testing.T) {
  1090  	nameLabel := []byte("__name__")
  1091  	rollupLabel := []byte("__rollup__")
  1092  
  1093  	// Empty
  1094  	labels := []prompb.Label{}
  1095  	assert.Nil(t, findReservedLabel(labels, nameLabel))
  1096  	assert.Nil(t, findReservedLabel(labels, rollupLabel))
  1097  
  1098  	// Single label, shorter than the reserved prefix
  1099  	labels = []prompb.Label{
  1100  		{Name: []byte("_"), Value: []byte("1")},
  1101  	}
  1102  	assert.Nil(t, findReservedLabel(labels, nameLabel))
  1103  	assert.Nil(t, findReservedLabel(labels, rollupLabel))
  1104  
  1105  	// Multiple labels, only one contains than the reserved prefix
  1106  	labels = []prompb.Label{
  1107  		{Name: []byte("_"), Value: []byte("1")},
  1108  		{Name: []byte("__wrong__"), Value: []byte("2")},
  1109  	}
  1110  	assert.Nil(t, findReservedLabel(labels, nameLabel))
  1111  	assert.Nil(t, findReservedLabel(labels, rollupLabel))
  1112  
  1113  	// Multiple labels, only one contains an expected value
  1114  	labels = []prompb.Label{
  1115  		{Name: []byte("_"), Value: []byte("1")},
  1116  		{Name: []byte("__name__"), Value: []byte("2")},
  1117  		{Name: []byte("mymetric"), Value: []byte("3")},
  1118  	}
  1119  	assert.Equal(t, []byte("2"), findReservedLabel(labels, nameLabel))
  1120  	assert.Nil(t, findReservedLabel(labels, rollupLabel))
  1121  
  1122  	// Multiple labels, only one contains an expected value
  1123  	labels = []prompb.Label{
  1124  		{Name: []byte("__abc__"), Value: []byte("1")},
  1125  		{Name: []byte("__rollup__"), Value: []byte("2")},
  1126  		{Name: []byte("metric"), Value: []byte("2")},
  1127  	}
  1128  	assert.Nil(t, findReservedLabel(labels, nameLabel))
  1129  	assert.Equal(t, []byte("2"), findReservedLabel(labels, rollupLabel))
  1130  
  1131  	// Multiple labels, all expected values exist contain an expected value
  1132  	labels = []prompb.Label{
  1133  		{Name: []byte("__name__"), Value: []byte("1")},
  1134  		{Name: []byte("__rollup__"), Value: []byte("2")},
  1135  		{Name: []byte("one"), Value: []byte("2")},
  1136  		{Name: []byte("two"), Value: []byte("2")},
  1137  	}
  1138  	assert.Equal(t, []byte("1"), findReservedLabel(labels, nameLabel))
  1139  	assert.Equal(t, []byte("2"), findReservedLabel(labels, rollupLabel))
  1140  
  1141  	// Multiple labels, with reserved section, nothing exists.
  1142  	labels = []prompb.Label{
  1143  		{Name: []byte("__a__"), Value: []byte("1")},
  1144  		{Name: []byte("__b__"), Value: []byte("2")},
  1145  		{Name: []byte("one"), Value: []byte("2")},
  1146  		{Name: []byte("two"), Value: []byte("2")},
  1147  	}
  1148  	assert.Nil(t, findReservedLabel(labels, nameLabel))
  1149  	assert.Nil(t, findReservedLabel(labels, rollupLabel))
  1150  }