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

     1  package ingester
     2  
     3  import (
     4  	"context"
     5  	fmt "fmt"
     6  	"runtime"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/prometheus/prometheus/model/labels"
    13  	"github.com/prometheus/prometheus/tsdb/chunks"
    14  	"github.com/prometheus/prometheus/tsdb/record"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/weaveworks/common/user"
    17  
    18  	"github.com/grafana/loki/pkg/ingester/client"
    19  	"github.com/grafana/loki/pkg/logproto"
    20  	loki_runtime "github.com/grafana/loki/pkg/runtime"
    21  	"github.com/grafana/loki/pkg/storage/chunk"
    22  	"github.com/grafana/loki/pkg/validation"
    23  )
    24  
    25  type MemoryWALReader struct {
    26  	xs [][]byte
    27  
    28  	initialized bool
    29  }
    30  
    31  func (m *MemoryWALReader) Next() bool {
    32  	if len(m.xs) < 1 {
    33  		return false
    34  	}
    35  
    36  	// don't advance on the first call
    37  	if !m.initialized {
    38  		m.initialized = true
    39  		return true
    40  	}
    41  
    42  	m.xs = m.xs[1:]
    43  	return len(m.xs) > 0
    44  }
    45  
    46  func (m *MemoryWALReader) Err() error { return nil }
    47  
    48  func (m *MemoryWALReader) Record() []byte { return m.xs[0] }
    49  
    50  func buildMemoryReader(users, totalStreams, entriesPerStream int) (*MemoryWALReader, []*WALRecord) {
    51  	var recs []*WALRecord
    52  	reader := &MemoryWALReader{}
    53  	for i := 0; i < totalStreams; i++ {
    54  		user := fmt.Sprintf("%d", i%users)
    55  		recs = append(recs, &WALRecord{
    56  			UserID: user,
    57  			Series: []record.RefSeries{
    58  				{
    59  					Ref: chunks.HeadSeriesRef(i),
    60  					Labels: labels.FromMap(
    61  						map[string]string{
    62  							"stream": fmt.Sprint(i),
    63  							"user":   user,
    64  						},
    65  					),
    66  				},
    67  			},
    68  		})
    69  
    70  		var entries []logproto.Entry
    71  		for j := 0; j < entriesPerStream; j++ {
    72  			entries = append(entries, logproto.Entry{
    73  				Timestamp: time.Unix(int64(j), 0),
    74  				Line:      fmt.Sprintf("%d", j),
    75  			})
    76  		}
    77  		recs = append(recs, &WALRecord{
    78  			UserID: user,
    79  			RefEntries: []RefEntries{
    80  				{
    81  					Ref:     chunks.HeadSeriesRef(i),
    82  					Entries: entries,
    83  				},
    84  			},
    85  		})
    86  	}
    87  
    88  	for _, rec := range recs {
    89  		if len(rec.Series) > 0 {
    90  			reader.xs = append(reader.xs, rec.encodeSeries(nil))
    91  		}
    92  
    93  		if len(rec.RefEntries) > 0 {
    94  			reader.xs = append(reader.xs, rec.encodeEntries(CurrentEntriesRec, nil))
    95  		}
    96  	}
    97  
    98  	return reader, recs
    99  }
   100  
   101  type MemRecoverer struct {
   102  	users map[string]map[chunks.HeadSeriesRef][]logproto.Entry
   103  	done  chan struct{}
   104  
   105  	sync.Mutex
   106  	usersCt, streamsCt, seriesCt int
   107  }
   108  
   109  func NewMemRecoverer() *MemRecoverer {
   110  	return &MemRecoverer{
   111  		users: make(map[string]map[chunks.HeadSeriesRef][]logproto.Entry),
   112  		done:  make(chan struct{}),
   113  	}
   114  }
   115  
   116  func (r *MemRecoverer) NumWorkers() int { return runtime.GOMAXPROCS(0) }
   117  
   118  func (r *MemRecoverer) Series(_ *Series) error { return nil }
   119  
   120  func (r *MemRecoverer) SetStream(userID string, series record.RefSeries) error {
   121  	r.Lock()
   122  	defer r.Unlock()
   123  	user, ok := r.users[userID]
   124  	if !ok {
   125  		user = make(map[chunks.HeadSeriesRef][]logproto.Entry)
   126  		r.users[userID] = user
   127  		r.usersCt++
   128  	}
   129  
   130  	if _, exists := user[series.Ref]; exists {
   131  		return errors.Errorf("stream (%d) already exists for user (%s)", series.Ref, userID)
   132  	}
   133  
   134  	user[series.Ref] = make([]logproto.Entry, 0)
   135  	r.streamsCt++
   136  	return nil
   137  }
   138  
   139  func (r *MemRecoverer) Push(userID string, entries RefEntries) error {
   140  	r.Lock()
   141  	defer r.Unlock()
   142  
   143  	user, ok := r.users[userID]
   144  	if !ok {
   145  		return errors.Errorf("unexpected user access (%s)", userID)
   146  	}
   147  
   148  	stream, ok := user[entries.Ref]
   149  	if !ok {
   150  		return errors.Errorf("unexpected stream access")
   151  	}
   152  
   153  	r.seriesCt += len(entries.Entries)
   154  	user[entries.Ref] = append(stream, entries.Entries...)
   155  	return nil
   156  }
   157  
   158  func (r *MemRecoverer) Close() { close(r.done) }
   159  
   160  func (r *MemRecoverer) Done() <-chan struct{} { return r.done }
   161  
   162  func Test_InMemorySegmentRecover(t *testing.T) {
   163  	var (
   164  		users            = 10
   165  		streamsCt        = 1000
   166  		entriesPerStream = 50
   167  	)
   168  	reader, recs := buildMemoryReader(users, streamsCt, entriesPerStream)
   169  
   170  	recoverer := NewMemRecoverer()
   171  
   172  	require.Nil(t, RecoverWAL(reader, recoverer))
   173  	recoverer.Close()
   174  
   175  	require.Equal(t, users, recoverer.usersCt)
   176  	require.Equal(t, streamsCt, recoverer.streamsCt)
   177  	require.Equal(t, streamsCt*entriesPerStream, recoverer.seriesCt)
   178  
   179  	for _, rec := range recs {
   180  		user, ok := recoverer.users[rec.UserID]
   181  		require.Equal(t, true, ok)
   182  
   183  		for _, s := range rec.Series {
   184  			_, ok := user[s.Ref]
   185  			require.Equal(t, true, ok)
   186  		}
   187  
   188  		for _, entries := range rec.RefEntries {
   189  			stream, ok := user[entries.Ref]
   190  			require.Equal(t, true, ok)
   191  
   192  			for i, entry := range entries.Entries {
   193  				require.Equal(t, entry, stream[i])
   194  			}
   195  		}
   196  	}
   197  }
   198  
   199  func TestSeriesRecoveryNoDuplicates(t *testing.T) {
   200  	ingesterConfig := defaultIngesterTestConfig(t)
   201  	limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil)
   202  	require.NoError(t, err)
   203  
   204  	store := &mockStore{
   205  		chunks: map[string][]chunk.Chunk{},
   206  	}
   207  
   208  	i, err := New(ingesterConfig, client.Config{}, store, limits, loki_runtime.DefaultTenantConfigs(), nil)
   209  	require.NoError(t, err)
   210  
   211  	mkSample := func(i int) *logproto.PushRequest {
   212  		return &logproto.PushRequest{
   213  			Streams: []logproto.Stream{
   214  				{
   215  					// Note: must use normalized labels here b/c we expect them
   216  					// sorted but use a string for efficiency.
   217  					Labels: `{bar="baz1", foo="bar"}`,
   218  					Entries: []logproto.Entry{
   219  						{
   220  							Timestamp: time.Unix(int64(i), 0),
   221  							Line:      fmt.Sprintf("line %d", i),
   222  						},
   223  					},
   224  				},
   225  			},
   226  		}
   227  	}
   228  
   229  	req := mkSample(1)
   230  
   231  	ctx := user.InjectOrgID(context.Background(), "test")
   232  	_, err = i.Push(ctx, req)
   233  	require.NoError(t, err)
   234  
   235  	iter := newIngesterSeriesIter(i).Iter()
   236  	require.Equal(t, true, iter.Next())
   237  
   238  	series := iter.Stream()
   239  	require.Equal(t, false, iter.Next())
   240  
   241  	// create a new ingester now
   242  	i, err = New(ingesterConfig, client.Config{}, store, limits, loki_runtime.DefaultTenantConfigs(), nil)
   243  	require.NoError(t, err)
   244  
   245  	// recover the checkpointed series
   246  	recoverer := newIngesterRecoverer(i)
   247  	require.NoError(t, recoverer.Series(series))
   248  
   249  	_, err = i.Push(ctx, req)
   250  	require.NoError(t, err) // we don't error on duplicate pushes
   251  
   252  	result := mockQuerierServer{
   253  		ctx: ctx,
   254  	}
   255  
   256  	// ensure no duplicate log lines exist
   257  	err = i.Query(&logproto.QueryRequest{
   258  		Selector: `{foo="bar",bar="baz1"}`,
   259  		Limit:    100,
   260  		Start:    time.Unix(0, 0),
   261  		End:      time.Unix(10, 0),
   262  	}, &result)
   263  	require.NoError(t, err)
   264  	require.Len(t, result.resps, 1)
   265  	lbls := labels.Labels{{Name: "bar", Value: "baz1"}, {Name: "foo", Value: "bar"}}
   266  	expected := []logproto.Stream{
   267  		{
   268  			Labels: lbls.String(),
   269  			Entries: []logproto.Entry{
   270  				{
   271  					Timestamp: time.Unix(1, 0),
   272  					Line:      "line 1",
   273  				},
   274  			},
   275  			Hash: lbls.Hash(),
   276  		},
   277  	}
   278  	require.Equal(t, expected, result.resps[0].Streams)
   279  }