github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ingester/ingester_test.go (about)

     1  package ingester
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net"
     7  	"net/http"
     8  	"sort"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/grafana/dskit/flagext"
    14  	"github.com/grafana/dskit/services"
    15  	"github.com/prometheus/common/model"
    16  	"github.com/prometheus/prometheus/model/labels"
    17  	"github.com/stretchr/testify/require"
    18  	"github.com/weaveworks/common/httpgrpc"
    19  	"github.com/weaveworks/common/middleware"
    20  	"github.com/weaveworks/common/user"
    21  	"golang.org/x/net/context"
    22  	"google.golang.org/grpc"
    23  	"google.golang.org/grpc/credentials/insecure"
    24  	"google.golang.org/grpc/metadata"
    25  	"google.golang.org/grpc/test/bufconn"
    26  
    27  	"github.com/grafana/dskit/tenant"
    28  
    29  	"github.com/grafana/loki/pkg/chunkenc"
    30  	"github.com/grafana/loki/pkg/ingester/client"
    31  	"github.com/grafana/loki/pkg/ingester/index"
    32  	"github.com/grafana/loki/pkg/iter"
    33  	"github.com/grafana/loki/pkg/logproto"
    34  	"github.com/grafana/loki/pkg/logql"
    35  	"github.com/grafana/loki/pkg/runtime"
    36  	"github.com/grafana/loki/pkg/storage/chunk"
    37  	"github.com/grafana/loki/pkg/storage/chunk/fetcher"
    38  	"github.com/grafana/loki/pkg/storage/config"
    39  	"github.com/grafana/loki/pkg/storage/stores/index/stats"
    40  	"github.com/grafana/loki/pkg/validation"
    41  )
    42  
    43  func TestIngester(t *testing.T) {
    44  	ingesterConfig := defaultIngesterTestConfig(t)
    45  	limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil)
    46  	require.NoError(t, err)
    47  
    48  	store := &mockStore{
    49  		chunks: map[string][]chunk.Chunk{},
    50  	}
    51  
    52  	i, err := New(ingesterConfig, client.Config{}, store, limits, runtime.DefaultTenantConfigs(), nil)
    53  	require.NoError(t, err)
    54  	defer services.StopAndAwaitTerminated(context.Background(), i) //nolint:errcheck
    55  
    56  	req := logproto.PushRequest{
    57  		Streams: []logproto.Stream{
    58  			{
    59  				Labels: `{foo="bar",bar="baz1"}`,
    60  			},
    61  			{
    62  				Labels: `{foo="bar",bar="baz2"}`,
    63  			},
    64  		},
    65  	}
    66  	for i := 0; i < 10; i++ {
    67  		req.Streams[0].Entries = append(req.Streams[0].Entries, logproto.Entry{
    68  			Timestamp: time.Unix(0, 0),
    69  			Line:      fmt.Sprintf("line %d", i),
    70  		})
    71  		req.Streams[1].Entries = append(req.Streams[1].Entries, logproto.Entry{
    72  			Timestamp: time.Unix(0, 0),
    73  			Line:      fmt.Sprintf("line %d", i),
    74  		})
    75  	}
    76  
    77  	ctx := user.InjectOrgID(context.Background(), "test")
    78  	_, err = i.Push(ctx, &req)
    79  	require.NoError(t, err)
    80  
    81  	result := mockQuerierServer{
    82  		ctx: ctx,
    83  	}
    84  	err = i.Query(&logproto.QueryRequest{
    85  		Selector: `{foo="bar"}`,
    86  		Limit:    100,
    87  		Start:    time.Unix(0, 0),
    88  		End:      time.Unix(1, 0),
    89  	}, &result)
    90  	require.NoError(t, err)
    91  	require.Len(t, result.resps, 1)
    92  	require.Len(t, result.resps[0].Streams, 2)
    93  
    94  	result = mockQuerierServer{
    95  		ctx: ctx,
    96  	}
    97  	err = i.Query(&logproto.QueryRequest{
    98  		Selector: `{foo="bar",bar="baz1"}`,
    99  		Limit:    100,
   100  		Start:    time.Unix(0, 0),
   101  		End:      time.Unix(1, 0),
   102  	}, &result)
   103  	require.NoError(t, err)
   104  	require.Len(t, result.resps, 1)
   105  	require.Len(t, result.resps[0].Streams, 1)
   106  	require.Equal(t, `{bar="baz1", foo="bar"}`, result.resps[0].Streams[0].Labels)
   107  
   108  	result = mockQuerierServer{
   109  		ctx: ctx,
   110  	}
   111  	err = i.Query(&logproto.QueryRequest{
   112  		Selector: `{foo="bar",bar="baz2"}`,
   113  		Limit:    100,
   114  		Start:    time.Unix(0, 0),
   115  		End:      time.Unix(1, 0),
   116  	}, &result)
   117  	require.NoError(t, err)
   118  	require.Len(t, result.resps, 1)
   119  	require.Len(t, result.resps[0].Streams, 1)
   120  	require.Equal(t, `{bar="baz2", foo="bar"}`, result.resps[0].Streams[0].Labels)
   121  
   122  	// Series
   123  
   124  	// empty matcher return all series
   125  	resp, err := i.Series(ctx, &logproto.SeriesRequest{
   126  		Start: time.Unix(0, 0),
   127  		End:   time.Unix(1, 0),
   128  	})
   129  	require.Nil(t, err)
   130  	require.ElementsMatch(t, []logproto.SeriesIdentifier{
   131  		{
   132  			Labels: map[string]string{
   133  				"foo": "bar",
   134  				"bar": "baz1",
   135  			},
   136  		},
   137  		{
   138  			Labels: map[string]string{
   139  				"foo": "bar",
   140  				"bar": "baz2",
   141  			},
   142  		},
   143  	}, resp.GetSeries())
   144  
   145  	// wrong matchers fmt
   146  	_, err = i.Series(ctx, &logproto.SeriesRequest{
   147  		Start:  time.Unix(0, 0),
   148  		End:    time.Unix(1, 0),
   149  		Groups: []string{`{a="b`},
   150  	})
   151  	require.Error(t, err)
   152  
   153  	// no selectors
   154  	_, err = i.Series(ctx, &logproto.SeriesRequest{
   155  		Start:  time.Unix(0, 0),
   156  		End:    time.Unix(1, 0),
   157  		Groups: []string{`{foo="bar"}`, `{}`},
   158  	})
   159  	require.Error(t, err)
   160  
   161  	// foo=bar
   162  	resp, err = i.Series(ctx, &logproto.SeriesRequest{
   163  		Start:  time.Unix(0, 0),
   164  		End:    time.Unix(1, 0),
   165  		Groups: []string{`{foo="bar"}`},
   166  	})
   167  	require.Nil(t, err)
   168  	require.ElementsMatch(t, []logproto.SeriesIdentifier{
   169  		{
   170  			Labels: map[string]string{
   171  				"foo": "bar",
   172  				"bar": "baz1",
   173  			},
   174  		},
   175  		{
   176  			Labels: map[string]string{
   177  				"foo": "bar",
   178  				"bar": "baz2",
   179  			},
   180  		},
   181  	}, resp.GetSeries())
   182  
   183  	// foo=bar, bar=~"baz[2-9]"
   184  	resp, err = i.Series(ctx, &logproto.SeriesRequest{
   185  		Start:  time.Unix(0, 0),
   186  		End:    time.Unix(1, 0),
   187  		Groups: []string{`{foo="bar", bar=~"baz[2-9]"}`},
   188  	})
   189  	require.Nil(t, err)
   190  	require.ElementsMatch(t, []logproto.SeriesIdentifier{
   191  		{
   192  			Labels: map[string]string{
   193  				"foo": "bar",
   194  				"bar": "baz2",
   195  			},
   196  		},
   197  	}, resp.GetSeries())
   198  
   199  	// foo=bar, bar=~"baz[2-9]" in different groups should OR the results
   200  	resp, err = i.Series(ctx, &logproto.SeriesRequest{
   201  		Start:  time.Unix(0, 0),
   202  		End:    time.Unix(1, 0),
   203  		Groups: []string{`{foo="bar"}`, `{bar=~"baz[2-9]"}`},
   204  	})
   205  	require.Nil(t, err)
   206  	require.ElementsMatch(t, []logproto.SeriesIdentifier{
   207  		{
   208  			Labels: map[string]string{
   209  				"foo": "bar",
   210  				"bar": "baz1",
   211  			},
   212  		},
   213  		{
   214  			Labels: map[string]string{
   215  				"foo": "bar",
   216  				"bar": "baz2",
   217  			},
   218  		},
   219  	}, resp.GetSeries())
   220  }
   221  
   222  func TestIngesterStreamLimitExceeded(t *testing.T) {
   223  	ingesterConfig := defaultIngesterTestConfig(t)
   224  	defaultLimits := defaultLimitsTestConfig()
   225  	defaultLimits.MaxLocalStreamsPerUser = 1
   226  	overrides, err := validation.NewOverrides(defaultLimits, nil)
   227  
   228  	require.NoError(t, err)
   229  
   230  	store := &mockStore{
   231  		chunks: map[string][]chunk.Chunk{},
   232  	}
   233  
   234  	i, err := New(ingesterConfig, client.Config{}, store, overrides, runtime.DefaultTenantConfigs(), nil)
   235  	require.NoError(t, err)
   236  	defer services.StopAndAwaitTerminated(context.Background(), i) //nolint:errcheck
   237  
   238  	req := logproto.PushRequest{
   239  		Streams: []logproto.Stream{
   240  			{
   241  				Labels: `{foo="bar",bar="baz1"}`,
   242  			},
   243  		},
   244  	}
   245  	for i := 0; i < 10; i++ {
   246  		req.Streams[0].Entries = append(req.Streams[0].Entries, logproto.Entry{
   247  			Timestamp: time.Unix(0, 0),
   248  			Line:      fmt.Sprintf("line %d", i),
   249  		})
   250  	}
   251  
   252  	ctx := user.InjectOrgID(context.Background(), "test")
   253  	_, err = i.Push(ctx, &req)
   254  	require.NoError(t, err)
   255  
   256  	req.Streams[0].Labels = `{foo="bar",bar="baz2"}`
   257  
   258  	_, err = i.Push(ctx, &req)
   259  	if resp, ok := httpgrpc.HTTPResponseFromError(err); !ok || resp.Code != http.StatusTooManyRequests {
   260  		t.Fatalf("expected error about exceeding metrics per user, got %v", err)
   261  	}
   262  }
   263  
   264  type mockStore struct {
   265  	mtx    sync.Mutex
   266  	chunks map[string][]chunk.Chunk
   267  }
   268  
   269  func (s *mockStore) Put(ctx context.Context, chunks []chunk.Chunk) error {
   270  	s.mtx.Lock()
   271  	defer s.mtx.Unlock()
   272  
   273  	userid, err := tenant.TenantID(ctx)
   274  	if err != nil {
   275  		return err
   276  	}
   277  
   278  	s.chunks[userid] = append(s.chunks[userid], chunks...)
   279  	return nil
   280  }
   281  
   282  func (s *mockStore) SelectLogs(ctx context.Context, req logql.SelectLogParams) (iter.EntryIterator, error) {
   283  	return nil, nil
   284  }
   285  
   286  func (s *mockStore) SelectSamples(ctx context.Context, req logql.SelectSampleParams) (iter.SampleIterator, error) {
   287  	return nil, nil
   288  }
   289  
   290  func (s *mockStore) GetSeries(ctx context.Context, req logql.SelectLogParams) ([]logproto.SeriesIdentifier, error) {
   291  	return nil, nil
   292  }
   293  
   294  func (s *mockStore) GetSchemaConfigs() []config.PeriodConfig {
   295  	return defaultPeriodConfigs
   296  }
   297  
   298  func (s *mockStore) SetChunkFilterer(_ chunk.RequestChunkFilterer) {
   299  }
   300  
   301  // chunk.Store methods
   302  func (s *mockStore) PutOne(ctx context.Context, from, through model.Time, chunk chunk.Chunk) error {
   303  	return nil
   304  }
   305  
   306  func (s *mockStore) GetChunkRefs(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([][]chunk.Chunk, []*fetcher.Fetcher, error) {
   307  	return nil, nil, nil
   308  }
   309  
   310  func (s *mockStore) LabelValuesForMetricName(ctx context.Context, userID string, from, through model.Time, metricName string, labelName string, matchers ...*labels.Matcher) ([]string, error) {
   311  	return []string{"val1", "val2"}, nil
   312  }
   313  
   314  func (s *mockStore) LabelNamesForMetricName(ctx context.Context, userID string, from, through model.Time, metricName string) ([]string, error) {
   315  	return nil, nil
   316  }
   317  
   318  func (s *mockStore) GetChunkFetcher(tm model.Time) *fetcher.Fetcher {
   319  	return nil
   320  }
   321  
   322  func (s *mockStore) Stats(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) (*stats.Stats, error) {
   323  	return &stats.Stats{}, nil
   324  }
   325  
   326  func (s *mockStore) Stop() {}
   327  
   328  type mockQuerierServer struct {
   329  	ctx   context.Context
   330  	resps []*logproto.QueryResponse
   331  	grpc.ServerStream
   332  }
   333  
   334  func (*mockQuerierServer) SetTrailer(metadata.MD) {}
   335  
   336  func (m *mockQuerierServer) Send(resp *logproto.QueryResponse) error {
   337  	m.resps = append(m.resps, resp)
   338  	return nil
   339  }
   340  
   341  func (m *mockQuerierServer) Context() context.Context {
   342  	return m.ctx
   343  }
   344  
   345  func defaultLimitsTestConfig() validation.Limits {
   346  	limits := validation.Limits{}
   347  	flagext.DefaultValues(&limits)
   348  	return limits
   349  }
   350  
   351  func TestIngester_buildStoreRequest(t *testing.T) {
   352  	now := time.Now()
   353  	for _, tc := range []struct {
   354  		name                       string
   355  		queryStore                 bool
   356  		maxLookBackPeriod          time.Duration
   357  		start, end                 time.Time
   358  		expectedStart, expectedEnd time.Time
   359  		shouldQuery                bool
   360  	}{
   361  		{
   362  			name:        "do not query store",
   363  			queryStore:  false,
   364  			start:       now.Add(-time.Minute),
   365  			end:         now,
   366  			shouldQuery: false,
   367  		},
   368  		{
   369  			name:              "query store with max look back covering whole request duration",
   370  			queryStore:        true,
   371  			maxLookBackPeriod: time.Hour,
   372  			start:             now.Add(-10 * time.Minute),
   373  			end:               now,
   374  			expectedStart:     now.Add(-10 * time.Minute),
   375  			expectedEnd:       now,
   376  			shouldQuery:       true,
   377  		},
   378  		{
   379  			name:              "query store with max look back covering partial request duration",
   380  			queryStore:        true,
   381  			maxLookBackPeriod: time.Hour,
   382  			start:             now.Add(-2 * time.Hour),
   383  			end:               now,
   384  			expectedStart:     now.Add(-time.Hour),
   385  			expectedEnd:       now,
   386  			shouldQuery:       true,
   387  		},
   388  		{
   389  			name:              "query store with max look back not covering request duration at all",
   390  			queryStore:        true,
   391  			maxLookBackPeriod: time.Hour,
   392  			start:             now.Add(-4 * time.Hour),
   393  			end:               now.Add(-2 * time.Hour),
   394  			shouldQuery:       false,
   395  		},
   396  	} {
   397  		t.Run(tc.name, func(t *testing.T) {
   398  			ingesterConfig := defaultIngesterTestConfig(t)
   399  			ingesterConfig.QueryStore = tc.queryStore
   400  			ingesterConfig.QueryStoreMaxLookBackPeriod = tc.maxLookBackPeriod
   401  
   402  			start, end, ok := buildStoreRequest(ingesterConfig, tc.start, tc.end, now)
   403  
   404  			if !tc.shouldQuery {
   405  				require.False(t, ok)
   406  				return
   407  			}
   408  			require.Equal(t, tc.expectedEnd, end, "end")
   409  			require.Equal(t, tc.expectedStart, start, "start")
   410  		})
   411  	}
   412  }
   413  
   414  func TestIngester_asyncStoreMaxLookBack(t *testing.T) {
   415  	now := model.Now()
   416  
   417  	for _, tc := range []struct {
   418  		name                string
   419  		periodicConfigs     []config.PeriodConfig
   420  		expectedMaxLookBack time.Duration
   421  	}{
   422  		{
   423  			name: "not using async index store",
   424  			periodicConfigs: []config.PeriodConfig{
   425  				{
   426  					From:      config.DayTime{Time: now.Add(-24 * time.Hour)},
   427  					IndexType: "bigtable",
   428  				},
   429  			},
   430  		},
   431  		{
   432  			name: "just one periodic config with boltdb-shipper",
   433  			periodicConfigs: []config.PeriodConfig{
   434  				{
   435  					From:      config.DayTime{Time: now.Add(-24 * time.Hour)},
   436  					IndexType: "boltdb-shipper",
   437  				},
   438  			},
   439  			expectedMaxLookBack: time.Since(now.Add(-24 * time.Hour).Time()),
   440  		},
   441  		{
   442  			name: "just one periodic config with tsdb",
   443  			periodicConfigs: []config.PeriodConfig{
   444  				{
   445  					From:      config.DayTime{Time: now.Add(-24 * time.Hour)},
   446  					IndexType: "tsdb",
   447  				},
   448  			},
   449  			expectedMaxLookBack: time.Since(now.Add(-24 * time.Hour).Time()),
   450  		},
   451  		{
   452  			name: "active config boltdb-shipper, previous config non async index store",
   453  			periodicConfigs: []config.PeriodConfig{
   454  				{
   455  					From:      config.DayTime{Time: now.Add(-48 * time.Hour)},
   456  					IndexType: "bigtable",
   457  				},
   458  				{
   459  					From:      config.DayTime{Time: now.Add(-24 * time.Hour)},
   460  					IndexType: "boltdb-shipper",
   461  				},
   462  			},
   463  			expectedMaxLookBack: time.Since(now.Add(-24 * time.Hour).Time()),
   464  		},
   465  		{
   466  			name: "current and previous config both using async index store",
   467  			periodicConfigs: []config.PeriodConfig{
   468  				{
   469  					From:      config.DayTime{Time: now.Add(-48 * time.Hour)},
   470  					IndexType: "boltdb-shipper",
   471  				},
   472  				{
   473  					From:      config.DayTime{Time: now.Add(-24 * time.Hour)},
   474  					IndexType: "tsdb",
   475  				},
   476  			},
   477  			expectedMaxLookBack: time.Since(now.Add(-48 * time.Hour).Time()),
   478  		},
   479  		{
   480  			name: "active config non async index store, previous config tsdb",
   481  			periodicConfigs: []config.PeriodConfig{
   482  				{
   483  					From:      config.DayTime{Time: now.Add(-48 * time.Hour)},
   484  					IndexType: "tsdb",
   485  				},
   486  				{
   487  					From:      config.DayTime{Time: now.Add(-24 * time.Hour)},
   488  					IndexType: "bigtable",
   489  				},
   490  			},
   491  		},
   492  	} {
   493  		t.Run(tc.name, func(t *testing.T) {
   494  			ingester := Ingester{periodicConfigs: tc.periodicConfigs}
   495  			mlb := ingester.asyncStoreMaxLookBack()
   496  			require.InDelta(t, tc.expectedMaxLookBack, mlb, float64(time.Second))
   497  		})
   498  	}
   499  }
   500  
   501  func TestValidate(t *testing.T) {
   502  	for i, tc := range []struct {
   503  		in       Config
   504  		err      bool
   505  		expected Config
   506  	}{
   507  		{
   508  			in: Config{
   509  				MaxChunkAge:   time.Minute,
   510  				ChunkEncoding: chunkenc.EncGZIP.String(),
   511  				IndexShards:   index.DefaultIndexShards,
   512  			},
   513  			expected: Config{
   514  				MaxChunkAge:    time.Minute,
   515  				ChunkEncoding:  chunkenc.EncGZIP.String(),
   516  				parsedEncoding: chunkenc.EncGZIP,
   517  				IndexShards:    index.DefaultIndexShards,
   518  			},
   519  		},
   520  		{
   521  			in: Config{
   522  				ChunkEncoding: chunkenc.EncSnappy.String(),
   523  				IndexShards:   index.DefaultIndexShards,
   524  			},
   525  			expected: Config{
   526  				ChunkEncoding:  chunkenc.EncSnappy.String(),
   527  				parsedEncoding: chunkenc.EncSnappy,
   528  				IndexShards:    index.DefaultIndexShards,
   529  			},
   530  		},
   531  		{
   532  			in: Config{
   533  				IndexShards:   index.DefaultIndexShards,
   534  				ChunkEncoding: "bad-enc",
   535  			},
   536  			err: true,
   537  		},
   538  		{
   539  			in: Config{
   540  				MaxChunkAge:   time.Minute,
   541  				ChunkEncoding: chunkenc.EncGZIP.String(),
   542  			},
   543  			err: true,
   544  		},
   545  	} {
   546  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   547  			err := tc.in.Validate()
   548  			if tc.err {
   549  				require.NotNil(t, err)
   550  				return
   551  			}
   552  			require.Nil(t, err)
   553  			require.Equal(t, tc.expected, tc.in)
   554  		})
   555  	}
   556  }
   557  
   558  func Test_InMemoryLabels(t *testing.T) {
   559  	ingesterConfig := defaultIngesterTestConfig(t)
   560  	limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil)
   561  	require.NoError(t, err)
   562  
   563  	store := &mockStore{
   564  		chunks: map[string][]chunk.Chunk{},
   565  	}
   566  
   567  	i, err := New(ingesterConfig, client.Config{}, store, limits, runtime.DefaultTenantConfigs(), nil)
   568  	require.NoError(t, err)
   569  	defer services.StopAndAwaitTerminated(context.Background(), i) //nolint:errcheck
   570  
   571  	req := logproto.PushRequest{
   572  		Streams: []logproto.Stream{
   573  			{
   574  				Labels: `{foo="bar",bar="baz1"}`,
   575  			},
   576  			{
   577  				Labels: `{foo="bar",bar="baz2"}`,
   578  			},
   579  		},
   580  	}
   581  	for i := 0; i < 10; i++ {
   582  		req.Streams[0].Entries = append(req.Streams[0].Entries, logproto.Entry{
   583  			Timestamp: time.Unix(0, 0),
   584  			Line:      fmt.Sprintf("line %d", i),
   585  		})
   586  		req.Streams[1].Entries = append(req.Streams[1].Entries, logproto.Entry{
   587  			Timestamp: time.Unix(0, 0),
   588  			Line:      fmt.Sprintf("line %d", i),
   589  		})
   590  	}
   591  
   592  	ctx := user.InjectOrgID(context.Background(), "test")
   593  	_, err = i.Push(ctx, &req)
   594  	require.NoError(t, err)
   595  
   596  	start := time.Unix(0, 0)
   597  	res, err := i.Label(ctx, &logproto.LabelRequest{
   598  		Start:  &start,
   599  		Name:   "bar",
   600  		Values: true,
   601  	})
   602  
   603  	require.NoError(t, err)
   604  	require.Equal(t, []string{"baz1", "baz2"}, res.Values)
   605  
   606  	res, err = i.Label(ctx, &logproto.LabelRequest{Start: &start})
   607  	require.NoError(t, err)
   608  	require.Equal(t, []string{"bar", "foo"}, res.Values)
   609  }
   610  
   611  func Test_DedupeIngester(t *testing.T) {
   612  	var (
   613  		requests      = int64(400)
   614  		streamCount   = int64(20)
   615  		streams       []labels.Labels
   616  		streamHashes  []uint64
   617  		ingesterCount = 100
   618  
   619  		ingesterConfig = defaultIngesterTestConfig(t)
   620  		ctx, _         = user.InjectIntoGRPCRequest(user.InjectOrgID(context.Background(), "foo"))
   621  	)
   622  	// make sure we will cut blocks and chunks and use head chunks
   623  	ingesterConfig.TargetChunkSize = 800
   624  	ingesterConfig.BlockSize = 300
   625  
   626  	// created many different ingesters
   627  	ingesterSet, closer := createIngesterSets(t, ingesterConfig, ingesterCount)
   628  	defer closer()
   629  
   630  	for i := int64(0); i < streamCount; i++ {
   631  		s := labels.FromStrings("foo", "bar", "bar", fmt.Sprintf("baz%d", i))
   632  		streams = append(streams, s)
   633  		streamHashes = append(streamHashes, s.Hash())
   634  	}
   635  	sort.Slice(streamHashes, func(i, j int) bool { return streamHashes[i] < streamHashes[j] })
   636  
   637  	for i := int64(0); i < requests; i++ {
   638  		for _, ing := range ingesterSet {
   639  			_, err := ing.Push(ctx, buildPushRequest(i, streams))
   640  			require.NoError(t, err)
   641  		}
   642  	}
   643  
   644  	t.Run("backward log", func(t *testing.T) {
   645  		iterators := make([]iter.EntryIterator, 0, len(ingesterSet))
   646  		for _, client := range ingesterSet {
   647  			stream, err := client.Query(ctx, &logproto.QueryRequest{
   648  				Selector:  `{foo="bar"} | label_format bar=""`, // making it difficult to dedupe by removing uncommon label.
   649  				Start:     time.Unix(0, 0),
   650  				End:       time.Unix(0, requests+1),
   651  				Limit:     uint32(requests * streamCount),
   652  				Direction: logproto.BACKWARD,
   653  			})
   654  			require.NoError(t, err)
   655  			iterators = append(iterators, iter.NewQueryClientIterator(stream, logproto.BACKWARD))
   656  		}
   657  		it := iter.NewMergeEntryIterator(ctx, iterators, logproto.BACKWARD)
   658  
   659  		for i := requests - 1; i >= 0; i-- {
   660  			actualHashes := []uint64{}
   661  			for j := 0; j < int(streamCount); j++ {
   662  				require.True(t, it.Next())
   663  				require.Equal(t, fmt.Sprintf("line %d", i), it.Entry().Line)
   664  				require.Equal(t, i, it.Entry().Timestamp.UnixNano())
   665  				require.Equal(t, `{bar="", foo="bar"}`, it.Labels())
   666  				actualHashes = append(actualHashes, it.StreamHash())
   667  			}
   668  			sort.Slice(actualHashes, func(i, j int) bool { return actualHashes[i] < actualHashes[j] })
   669  			require.Equal(t, streamHashes, actualHashes)
   670  		}
   671  		require.False(t, it.Next())
   672  		require.NoError(t, it.Error())
   673  	})
   674  	t.Run("forward log", func(t *testing.T) {
   675  		iterators := make([]iter.EntryIterator, 0, len(ingesterSet))
   676  		for _, client := range ingesterSet {
   677  			stream, err := client.Query(ctx, &logproto.QueryRequest{
   678  				Selector:  `{foo="bar"} | label_format bar=""`, // making it difficult to dedupe by removing uncommon label.
   679  				Start:     time.Unix(0, 0),
   680  				End:       time.Unix(0, requests+1),
   681  				Limit:     uint32(requests * streamCount),
   682  				Direction: logproto.FORWARD,
   683  			})
   684  			require.NoError(t, err)
   685  			iterators = append(iterators, iter.NewQueryClientIterator(stream, logproto.FORWARD))
   686  		}
   687  		it := iter.NewMergeEntryIterator(ctx, iterators, logproto.FORWARD)
   688  
   689  		for i := int64(0); i < requests; i++ {
   690  			actualHashes := []uint64{}
   691  			for j := 0; j < int(streamCount); j++ {
   692  				require.True(t, it.Next())
   693  				require.Equal(t, fmt.Sprintf("line %d", i), it.Entry().Line)
   694  				require.Equal(t, i, it.Entry().Timestamp.UnixNano())
   695  				require.Equal(t, `{bar="", foo="bar"}`, it.Labels())
   696  				actualHashes = append(actualHashes, it.StreamHash())
   697  			}
   698  			sort.Slice(actualHashes, func(i, j int) bool { return actualHashes[i] < actualHashes[j] })
   699  			require.Equal(t, streamHashes, actualHashes)
   700  		}
   701  		require.False(t, it.Next())
   702  		require.NoError(t, it.Error())
   703  	})
   704  	t.Run("sum by metrics", func(t *testing.T) {
   705  		iterators := make([]iter.SampleIterator, 0, len(ingesterSet))
   706  		for _, client := range ingesterSet {
   707  			stream, err := client.QuerySample(ctx, &logproto.SampleQueryRequest{
   708  				Selector: `sum(rate({foo="bar"}[1m])) by (bar)`,
   709  				Start:    time.Unix(0, 0),
   710  				End:      time.Unix(0, requests+1),
   711  			})
   712  			require.NoError(t, err)
   713  			iterators = append(iterators, iter.NewSampleQueryClientIterator(stream))
   714  		}
   715  		it := iter.NewMergeSampleIterator(ctx, iterators)
   716  		var expectedLabels []string
   717  		for _, s := range streams {
   718  			expectedLabels = append(expectedLabels, labels.NewBuilder(s).Del("foo").Labels().String())
   719  		}
   720  		sort.Strings(expectedLabels)
   721  		for i := int64(0); i < requests; i++ {
   722  			labels := []string{}
   723  			actualHashes := []uint64{}
   724  			for j := 0; j < int(streamCount); j++ {
   725  				require.True(t, it.Next())
   726  				require.Equal(t, float64(1), it.Sample().Value)
   727  				require.Equal(t, i, it.Sample().Timestamp)
   728  				labels = append(labels, it.Labels())
   729  				actualHashes = append(actualHashes, it.StreamHash())
   730  			}
   731  			sort.Strings(labels)
   732  			sort.Slice(actualHashes, func(i, j int) bool { return actualHashes[i] < actualHashes[j] })
   733  			require.Equal(t, expectedLabels, labels)
   734  			require.Equal(t, streamHashes, actualHashes)
   735  		}
   736  		require.False(t, it.Next())
   737  		require.NoError(t, it.Error())
   738  	})
   739  	t.Run("sum metrics", func(t *testing.T) {
   740  		iterators := make([]iter.SampleIterator, 0, len(ingesterSet))
   741  		for _, client := range ingesterSet {
   742  			stream, err := client.QuerySample(ctx, &logproto.SampleQueryRequest{
   743  				Selector: `sum(rate({foo="bar"}[1m]))`,
   744  				Start:    time.Unix(0, 0),
   745  				End:      time.Unix(0, requests+1),
   746  			})
   747  			require.NoError(t, err)
   748  			iterators = append(iterators, iter.NewSampleQueryClientIterator(stream))
   749  		}
   750  		it := iter.NewMergeSampleIterator(ctx, iterators)
   751  		for i := int64(0); i < requests; i++ {
   752  			actualHashes := []uint64{}
   753  			for j := 0; j < int(streamCount); j++ {
   754  				require.True(t, it.Next())
   755  				require.Equal(t, float64(1), it.Sample().Value)
   756  				require.Equal(t, i, it.Sample().Timestamp)
   757  				require.Equal(t, "{}", it.Labels())
   758  				actualHashes = append(actualHashes, it.StreamHash())
   759  			}
   760  			sort.Slice(actualHashes, func(i, j int) bool { return actualHashes[i] < actualHashes[j] })
   761  			require.Equal(t, streamHashes, actualHashes)
   762  		}
   763  		require.False(t, it.Next())
   764  		require.NoError(t, it.Error())
   765  	})
   766  }
   767  
   768  func Test_DedupeIngesterParser(t *testing.T) {
   769  	var (
   770  		requests      = 100
   771  		streamCount   = 10
   772  		streams       []labels.Labels
   773  		ingesterCount = 30
   774  
   775  		ingesterConfig = defaultIngesterTestConfig(t)
   776  		ctx, _         = user.InjectIntoGRPCRequest(user.InjectOrgID(context.Background(), "foo"))
   777  	)
   778  	// make sure we will cut blocks and chunks and use head chunks
   779  	ingesterConfig.TargetChunkSize = 800
   780  	ingesterConfig.BlockSize = 300
   781  
   782  	// created many different ingesters
   783  	ingesterSet, closer := createIngesterSets(t, ingesterConfig, ingesterCount)
   784  	defer closer()
   785  
   786  	for i := 0; i < streamCount; i++ {
   787  		streams = append(streams, labels.FromStrings("foo", "bar", "bar", fmt.Sprintf("baz%d", i)))
   788  	}
   789  
   790  	for i := 0; i < requests; i++ {
   791  		for _, ing := range ingesterSet {
   792  			_, err := ing.Push(ctx, buildPushJSONRequest(int64(i), streams))
   793  			require.NoError(t, err)
   794  		}
   795  	}
   796  
   797  	t.Run("backward log", func(t *testing.T) {
   798  		iterators := make([]iter.EntryIterator, 0, len(ingesterSet))
   799  		for _, client := range ingesterSet {
   800  			stream, err := client.Query(ctx, &logproto.QueryRequest{
   801  				Selector:  `{foo="bar"} | json`,
   802  				Start:     time.Unix(0, 0),
   803  				End:       time.Unix(0, int64(requests+1)),
   804  				Limit:     uint32(requests * streamCount * 2),
   805  				Direction: logproto.BACKWARD,
   806  			})
   807  			require.NoError(t, err)
   808  			iterators = append(iterators, iter.NewQueryClientIterator(stream, logproto.BACKWARD))
   809  		}
   810  		it := iter.NewMergeEntryIterator(ctx, iterators, logproto.BACKWARD)
   811  
   812  		for i := requests - 1; i >= 0; i-- {
   813  			for j := 0; j < streamCount; j++ {
   814  				for k := 0; k < 2; k++ { // 2 line per entry
   815  					require.True(t, it.Next())
   816  					require.Equal(t, int64(i), it.Entry().Timestamp.UnixNano())
   817  				}
   818  			}
   819  		}
   820  		require.False(t, it.Next())
   821  		require.NoError(t, it.Error())
   822  	})
   823  
   824  	t.Run("forward log", func(t *testing.T) {
   825  		iterators := make([]iter.EntryIterator, 0, len(ingesterSet))
   826  		for _, client := range ingesterSet {
   827  			stream, err := client.Query(ctx, &logproto.QueryRequest{
   828  				Selector:  `{foo="bar"} | json`, // making it difficult to dedupe by removing uncommon label.
   829  				Start:     time.Unix(0, 0),
   830  				End:       time.Unix(0, int64(requests+1)),
   831  				Limit:     uint32(requests * streamCount * 2),
   832  				Direction: logproto.FORWARD,
   833  			})
   834  			require.NoError(t, err)
   835  			iterators = append(iterators, iter.NewQueryClientIterator(stream, logproto.FORWARD))
   836  		}
   837  		it := iter.NewMergeEntryIterator(ctx, iterators, logproto.FORWARD)
   838  
   839  		for i := 0; i < requests; i++ {
   840  			for j := 0; j < streamCount; j++ {
   841  				for k := 0; k < 2; k++ { // 2 line per entry
   842  					require.True(t, it.Next())
   843  					require.Equal(t, int64(i), it.Entry().Timestamp.UnixNano())
   844  				}
   845  			}
   846  		}
   847  		require.False(t, it.Next())
   848  		require.NoError(t, it.Error())
   849  	})
   850  	t.Run("no sum metrics", func(t *testing.T) {
   851  		iterators := make([]iter.SampleIterator, 0, len(ingesterSet))
   852  		for _, client := range ingesterSet {
   853  			stream, err := client.QuerySample(ctx, &logproto.SampleQueryRequest{
   854  				Selector: `rate({foo="bar"} | json [1m])`,
   855  				Start:    time.Unix(0, 0),
   856  				End:      time.Unix(0, int64(requests+1)),
   857  			})
   858  			require.NoError(t, err)
   859  			iterators = append(iterators, iter.NewSampleQueryClientIterator(stream))
   860  		}
   861  		it := iter.NewMergeSampleIterator(ctx, iterators)
   862  
   863  		for i := 0; i < requests; i++ {
   864  			for j := 0; j < streamCount; j++ {
   865  				for k := 0; k < 2; k++ { // 2 line per entry
   866  					require.True(t, it.Next())
   867  					require.Equal(t, float64(1), it.Sample().Value)
   868  					require.Equal(t, int64(i), it.Sample().Timestamp)
   869  				}
   870  			}
   871  		}
   872  		require.False(t, it.Next())
   873  		require.NoError(t, it.Error())
   874  	})
   875  	t.Run("sum metrics", func(t *testing.T) {
   876  		iterators := make([]iter.SampleIterator, 0, len(ingesterSet))
   877  		for _, client := range ingesterSet {
   878  			stream, err := client.QuerySample(ctx, &logproto.SampleQueryRequest{
   879  				Selector: `sum by (c,d,e,foo) (rate({foo="bar"} | json [1m]))`,
   880  				Start:    time.Unix(0, 0),
   881  				End:      time.Unix(0, int64(requests+1)),
   882  			})
   883  			require.NoError(t, err)
   884  			iterators = append(iterators, iter.NewSampleQueryClientIterator(stream))
   885  		}
   886  		it := iter.NewMergeSampleIterator(ctx, iterators)
   887  
   888  		for i := 0; i < requests; i++ {
   889  			for j := 0; j < streamCount; j++ {
   890  				for k := 0; k < 2; k++ { // 2 line per entry
   891  					require.True(t, it.Next())
   892  					require.Equal(t, float64(1), it.Sample().Value)
   893  					require.Equal(t, int64(i), it.Sample().Timestamp)
   894  				}
   895  			}
   896  		}
   897  		require.False(t, it.Next())
   898  		require.NoError(t, it.Error())
   899  	})
   900  }
   901  
   902  type ingesterClient struct {
   903  	logproto.PusherClient
   904  	logproto.QuerierClient
   905  }
   906  
   907  func createIngesterSets(t *testing.T, config Config, count int) ([]ingesterClient, func()) {
   908  	result := make([]ingesterClient, count)
   909  	closers := make([]func(), count)
   910  	for i := 0; i < count; i++ {
   911  		ingester, closer := createIngesterServer(t, config)
   912  		result[i] = ingester
   913  		closers[i] = closer
   914  	}
   915  	return result, func() {
   916  		for _, closer := range closers {
   917  			closer()
   918  		}
   919  	}
   920  }
   921  
   922  func createIngesterServer(t *testing.T, ingesterConfig Config) (ingesterClient, func()) {
   923  	t.Helper()
   924  	limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil)
   925  	require.NoError(t, err)
   926  
   927  	ing, err := New(ingesterConfig, client.Config{}, &mockStore{}, limits, runtime.DefaultTenantConfigs(), nil)
   928  	require.NoError(t, err)
   929  
   930  	listener := bufconn.Listen(1024 * 1024)
   931  
   932  	server := grpc.NewServer(grpc.ChainStreamInterceptor(func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
   933  		return middleware.StreamServerUserHeaderInterceptor(srv, ss, info, handler)
   934  	}), grpc.ChainUnaryInterceptor(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
   935  		return middleware.ServerUserHeaderInterceptor(ctx, req, info, handler)
   936  	}))
   937  
   938  	logproto.RegisterPusherServer(server, ing)
   939  	logproto.RegisterQuerierServer(server, ing)
   940  	go func() {
   941  		if err := server.Serve(listener); err != nil {
   942  			log.Fatal(err)
   943  		}
   944  	}()
   945  	conn, err := grpc.DialContext(context.Background(), "", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
   946  		return listener.Dial()
   947  	}))
   948  	require.NoError(t, err)
   949  
   950  	return ingesterClient{
   951  			PusherClient:  logproto.NewPusherClient(conn),
   952  			QuerierClient: logproto.NewQuerierClient(conn),
   953  		}, func() {
   954  			_ = services.StopAndAwaitTerminated(context.Background(), ing)
   955  			server.Stop()
   956  			_ = listener.Close()
   957  		}
   958  }
   959  
   960  func buildPushRequest(ts int64, streams []labels.Labels) *logproto.PushRequest {
   961  	req := &logproto.PushRequest{}
   962  
   963  	for _, stream := range streams {
   964  		req.Streams = append(req.Streams, logproto.Stream{
   965  			Labels: stream.String(),
   966  			Entries: []logproto.Entry{
   967  				{
   968  					Timestamp: time.Unix(0, ts),
   969  					Line:      fmt.Sprintf("line %d", ts),
   970  				},
   971  			},
   972  		})
   973  	}
   974  
   975  	return req
   976  }
   977  
   978  func buildPushJSONRequest(ts int64, streams []labels.Labels) *logproto.PushRequest {
   979  	req := &logproto.PushRequest{}
   980  
   981  	for _, stream := range streams {
   982  		req.Streams = append(req.Streams, logproto.Stream{
   983  			Labels: stream.String(),
   984  			Entries: []logproto.Entry{
   985  				{
   986  					Timestamp: time.Unix(0, ts),
   987  					Line:      jsonLine(ts, 0),
   988  				},
   989  				{
   990  					Timestamp: time.Unix(0, ts),
   991  					Line:      jsonLine(ts, 1),
   992  				},
   993  			},
   994  		})
   995  	}
   996  
   997  	return req
   998  }
   999  
  1000  func jsonLine(ts int64, i int) string {
  1001  	if i%2 == 0 {
  1002  		return fmt.Sprintf(`{"a":"b", "c":"d", "e":"f", "g":"h", "ts":"%d"}`, ts)
  1003  	}
  1004  	return fmt.Sprintf(`{"e":"f", "h":"i", "j":"k", "g":"h", "ts":"%d"}`, ts)
  1005  }