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

     1  package logproto
     2  
     3  import (
     4  	"encoding/json"
     5  	stdlibjson "encoding/json"
     6  	"fmt"
     7  	"math"
     8  	"testing"
     9  	"unsafe"
    10  
    11  	jsoniter "github.com/json-iterator/go"
    12  	"github.com/prometheus/prometheus/model/labels"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  // This test verifies that jsoninter uses our custom method for marshalling.
    18  // We do that by using "test sample" recognized by marshal function when in testing mode.
    19  func TestJsoniterMarshalForSample(t *testing.T) {
    20  	testMarshalling(t, jsoniter.Marshal, "test sample")
    21  }
    22  
    23  func TestStdlibJsonMarshalForSample(t *testing.T) {
    24  	testMarshalling(t, stdlibjson.Marshal, "json: error calling MarshalJSON for type logproto.LegacySample: test sample")
    25  }
    26  
    27  func testMarshalling(t *testing.T, marshalFn func(v interface{}) ([]byte, error), expectedError string) {
    28  	isTesting = true
    29  	defer func() { isTesting = false }()
    30  
    31  	out, err := marshalFn(LegacySample{Value: 12345, TimestampMs: 98765})
    32  	require.NoError(t, err)
    33  	require.Equal(t, `[98.765,"12345"]`, string(out))
    34  
    35  	_, err = marshalFn(LegacySample{Value: math.NaN(), TimestampMs: 0})
    36  	require.EqualError(t, err, expectedError)
    37  
    38  	// If not testing, we get normal output.
    39  	isTesting = false
    40  	out, err = marshalFn(LegacySample{Value: math.NaN(), TimestampMs: 0})
    41  	require.NoError(t, err)
    42  	require.Equal(t, `[0,"NaN"]`, string(out))
    43  }
    44  
    45  // This test verifies that jsoninter uses our custom method for unmarshalling Sample.
    46  // As with Marshal, we rely on testing mode and special value that reports error.
    47  func TestJsoniterUnmarshalForSample(t *testing.T) {
    48  	testUnmarshalling(t, jsoniter.Unmarshal, "test sample")
    49  }
    50  
    51  func TestStdlibJsonUnmarshalForSample(t *testing.T) {
    52  	testUnmarshalling(t, json.Unmarshal, "test sample")
    53  }
    54  
    55  func testUnmarshalling(t *testing.T, unmarshalFn func(data []byte, v interface{}) error, expectedError string) {
    56  	isTesting = true
    57  	defer func() { isTesting = false }()
    58  
    59  	sample := LegacySample{}
    60  
    61  	err := unmarshalFn([]byte(`[98.765,"12345"]`), &sample)
    62  	require.NoError(t, err)
    63  	require.Equal(t, LegacySample{Value: 12345, TimestampMs: 98765}, sample)
    64  
    65  	err = unmarshalFn([]byte(`[0.0,"NaN"]`), &sample)
    66  	require.EqualError(t, err, expectedError)
    67  
    68  	isTesting = false
    69  	err = unmarshalFn([]byte(`[0.0,"NaN"]`), &sample)
    70  	require.NoError(t, err)
    71  	require.Equal(t, int64(0), sample.TimestampMs)
    72  	require.True(t, math.IsNaN(sample.Value))
    73  }
    74  
    75  func TestFromLabelAdaptersToLabels(t *testing.T) {
    76  	input := []LabelAdapter{{Name: "hello", Value: "world"}}
    77  	expected := labels.Labels{labels.Label{Name: "hello", Value: "world"}}
    78  	actual := FromLabelAdaptersToLabels(input)
    79  
    80  	assert.Equal(t, expected, actual)
    81  
    82  	// All strings must NOT be copied.
    83  	assert.Equal(t, uintptr(unsafe.Pointer(&input[0].Name)), uintptr(unsafe.Pointer(&actual[0].Name)))
    84  	assert.Equal(t, uintptr(unsafe.Pointer(&input[0].Value)), uintptr(unsafe.Pointer(&actual[0].Value)))
    85  }
    86  
    87  func TestFromLabelAdaptersToLabelsWithCopy(t *testing.T) {
    88  	input := []LabelAdapter{{Name: "hello", Value: "world"}}
    89  	expected := labels.Labels{labels.Label{Name: "hello", Value: "world"}}
    90  	actual := FromLabelAdaptersToLabelsWithCopy(input)
    91  
    92  	assert.Equal(t, expected, actual)
    93  
    94  	// All strings must be copied.
    95  	assert.NotEqual(t, uintptr(unsafe.Pointer(&input[0].Name)), uintptr(unsafe.Pointer(&actual[0].Name)))
    96  	assert.NotEqual(t, uintptr(unsafe.Pointer(&input[0].Value)), uintptr(unsafe.Pointer(&actual[0].Value)))
    97  }
    98  
    99  func BenchmarkFromLabelAdaptersToLabelsWithCopy(b *testing.B) {
   100  	input := []LabelAdapter{
   101  		{Name: "hello", Value: "world"},
   102  		{Name: "some label", Value: "and its value"},
   103  		{Name: "long long long long long label name", Value: "perhaps even longer label value, but who's counting anyway?"}}
   104  
   105  	for i := 0; i < b.N; i++ {
   106  		FromLabelAdaptersToLabelsWithCopy(input)
   107  	}
   108  }
   109  
   110  func TestLegacySampleCompatibilityMarshalling(t *testing.T) {
   111  	ts := int64(1232132123)
   112  	val := 12345.12345
   113  	legacySample := LegacySample{Value: val, TimestampMs: ts}
   114  	got, err := json.Marshal(legacySample)
   115  	require.NoError(t, err)
   116  
   117  	legacyExpected := fmt.Sprintf("[%d.%d,\"%.5f\"]", ts/1000, 123, val)
   118  	require.Equal(t, []byte(legacyExpected), got)
   119  
   120  	// proving that `logproto.Sample` marshal things differently than `logproto.LegacySample`:
   121  	incompatibleSample := Sample{Value: val, Timestamp: ts}
   122  	gotIncompatibleSample, err := json.Marshal(incompatibleSample)
   123  	require.NoError(t, err)
   124  	require.NotEqual(t, []byte(legacyExpected), gotIncompatibleSample)
   125  }
   126  
   127  func TestLegacySampleCompatibilityUnmarshalling(t *testing.T) {
   128  	serializedSample := "[123123.123,\"12345.12345\"]"
   129  	var legacySample LegacySample
   130  	err := json.Unmarshal([]byte(serializedSample), &legacySample)
   131  	require.NoError(t, err)
   132  	expectedLegacySample := LegacySample{Value: 12345.12345, TimestampMs: 123123123}
   133  	require.EqualValues(t, expectedLegacySample, legacySample)
   134  
   135  	// proving that `logproto.Sample` unmarshal things differently than `logproto.LegacySample`:
   136  	incompatibleSample := Sample{Value: 12345.12345, Timestamp: 123123123}
   137  	require.NotEqualValues(t, expectedLegacySample, incompatibleSample)
   138  }
   139  
   140  func TestLegacyLabelPairCompatibilityMarshalling(t *testing.T) {
   141  	legacyLabelPair := LegacyLabelPair{Name: []byte("labelname"), Value: []byte("labelvalue")}
   142  	got, err := json.Marshal(legacyLabelPair)
   143  	require.NoError(t, err)
   144  
   145  	expectedStr := `{"name":"bGFiZWxuYW1l","value":"bGFiZWx2YWx1ZQ=="}`
   146  	require.Equal(t, []byte(expectedStr), got)
   147  
   148  	// proving that `logproto.LegacyLabelPair` marshal things differently than `logproto.LabelPair`:
   149  	incompatibleLabelPair := LabelPair{Name: "labelname", Value: "labelvalue"}
   150  	gotIncompatible, err := json.Marshal(incompatibleLabelPair)
   151  	require.NoError(t, err)
   152  	require.NotEqual(t, []byte(expectedStr), gotIncompatible)
   153  }
   154  
   155  func TestLegacyLabelPairCompatibilityUnmarshalling(t *testing.T) {
   156  	serializedLabelPair := `{"name":"bGFiZWxuYW1l","value":"bGFiZWx2YWx1ZQ=="}`
   157  	var legacyLabelPair LegacyLabelPair
   158  	err := json.Unmarshal([]byte(serializedLabelPair), &legacyLabelPair)
   159  	require.NoError(t, err)
   160  	expectedLabelPair := LegacyLabelPair{Name: []byte("labelname"), Value: []byte("labelvalue")}
   161  	require.EqualValues(t, expectedLabelPair, legacyLabelPair)
   162  
   163  	// proving that `logproto.LegacyLabelPair` unmarshal things differently than `logproto.LabelPair`:
   164  	var incompatibleLabelPair LabelPair
   165  	err = json.Unmarshal([]byte(serializedLabelPair), &incompatibleLabelPair)
   166  	require.NoError(t, err)
   167  	require.NotEqualValues(t, expectedLabelPair, incompatibleLabelPair)
   168  }
   169  
   170  func TestMergeLabelResponses(t *testing.T) {
   171  	for _, tc := range []struct {
   172  		desc      string
   173  		responses []*LabelResponse
   174  		expected  []*LabelResponse
   175  		err       error
   176  	}{
   177  		{
   178  			desc: "merge two label responses",
   179  			responses: []*LabelResponse{
   180  				{Values: []string{"test"}},
   181  				{Values: []string{"test2"}},
   182  			},
   183  			expected: []*LabelResponse{
   184  				{Values: []string{"test", "test2"}},
   185  			},
   186  		},
   187  		{
   188  			desc: "merge three label responses",
   189  			responses: []*LabelResponse{
   190  				{Values: []string{"test"}},
   191  				{Values: []string{"test2"}},
   192  				{Values: []string{"test3"}},
   193  			},
   194  			expected: []*LabelResponse{
   195  				{Values: []string{"test", "test2", "test3"}},
   196  			},
   197  		},
   198  		{
   199  			desc: "merge three label responses with one non-unique",
   200  			responses: []*LabelResponse{
   201  				{Values: []string{"test"}},
   202  				{Values: []string{"test"}},
   203  				{Values: []string{"test2"}},
   204  				{Values: []string{"test3"}},
   205  			},
   206  			expected: []*LabelResponse{
   207  				{Values: []string{"test", "test2", "test3"}},
   208  			},
   209  		},
   210  		{
   211  			desc: "merge one and expect one",
   212  			responses: []*LabelResponse{
   213  				{Values: []string{"test"}},
   214  			},
   215  			expected: []*LabelResponse{
   216  				{Values: []string{"test"}},
   217  			},
   218  		},
   219  		{
   220  			desc:      "merge empty and expect empty",
   221  			responses: []*LabelResponse{},
   222  			expected:  []*LabelResponse{},
   223  		},
   224  	} {
   225  		t.Run(tc.desc, func(t *testing.T) {
   226  			merged, err := MergeLabelResponses(tc.responses)
   227  			if err != nil {
   228  				require.Equal(t, tc.err, err)
   229  			} else if len(tc.expected) == 0 {
   230  				require.Empty(t, merged)
   231  			} else {
   232  				require.ElementsMatch(t, tc.expected[0].Values, merged.Values)
   233  			}
   234  		})
   235  	}
   236  }
   237  
   238  func TestMergeSeriesResponses(t *testing.T) {
   239  	mockSeriesResponse := func(series []map[string]string) *SeriesResponse {
   240  		resp := &SeriesResponse{}
   241  		for _, s := range series {
   242  			resp.Series = append(resp.Series, SeriesIdentifier{
   243  				Labels: s,
   244  			})
   245  		}
   246  		return resp
   247  	}
   248  
   249  	for _, tc := range []struct {
   250  		desc      string
   251  		responses []*SeriesResponse
   252  		expected  []*SeriesResponse
   253  		err       error
   254  	}{
   255  		{
   256  			desc: "merge one series response and expect one",
   257  			responses: []*SeriesResponse{
   258  				{Series: []SeriesIdentifier{{Labels: map[string]string{"test": "test"}}}},
   259  			},
   260  			expected: []*SeriesResponse{
   261  				mockSeriesResponse([]map[string]string{{"test": "test"}}),
   262  			},
   263  		},
   264  		{
   265  			desc: "merge two series responses",
   266  			responses: []*SeriesResponse{
   267  				{Series: []SeriesIdentifier{{Labels: map[string]string{"test": "test"}}}},
   268  				{Series: []SeriesIdentifier{{Labels: map[string]string{"test2": "test2"}}}},
   269  			},
   270  			expected: []*SeriesResponse{
   271  				mockSeriesResponse([]map[string]string{{"test": "test"}, {"test2": "test2"}}),
   272  			},
   273  		},
   274  		{
   275  			desc: "merge three series responses",
   276  			responses: []*SeriesResponse{
   277  				{Series: []SeriesIdentifier{{Labels: map[string]string{"test": "test"}}}},
   278  				{Series: []SeriesIdentifier{{Labels: map[string]string{"test2": "test2"}}}},
   279  				{Series: []SeriesIdentifier{{Labels: map[string]string{"test3": "test3"}}}},
   280  			},
   281  			expected: []*SeriesResponse{
   282  				mockSeriesResponse([]map[string]string{{"test": "test"}, {"test2": "test2"}, {"test3": "test3"}}),
   283  			},
   284  		},
   285  		{
   286  			desc:      "merge empty and expect empty",
   287  			responses: []*SeriesResponse{},
   288  			expected:  []*SeriesResponse{},
   289  		},
   290  	} {
   291  		t.Run(tc.desc, func(t *testing.T) {
   292  			merged, err := MergeSeriesResponses(tc.responses)
   293  			if err != nil {
   294  				require.Equal(t, tc.err, err)
   295  			} else if len(tc.expected) == 0 {
   296  				require.Empty(t, merged)
   297  			} else {
   298  				require.ElementsMatch(t, tc.expected[0].Series, merged.Series)
   299  			}
   300  		})
   301  	}
   302  }
   303  
   304  func benchmarkMergeLabelResponses(b *testing.B, responses []*LabelResponse) {
   305  	b.ReportAllocs()
   306  	for n := 0; n < b.N; n++ {
   307  		MergeLabelResponses(responses) //nolint:errcheck
   308  	}
   309  }
   310  
   311  func benchmarkMergeSeriesResponses(b *testing.B, responses []*SeriesResponse) {
   312  	b.ReportAllocs()
   313  	for n := 0; n < b.N; n++ {
   314  		MergeSeriesResponses(responses) //nolint:errcheck
   315  	}
   316  }
   317  
   318  func BenchmarkMergeALabelResponse(b *testing.B) {
   319  	response := []*LabelResponse{{Values: []string{"test"}}}
   320  	benchmarkMergeLabelResponses(b, response)
   321  }
   322  
   323  func BenchmarkMergeASeriesResponse(b *testing.B) {
   324  	response := []*SeriesResponse{{Series: []SeriesIdentifier{{Labels: map[string]string{"test": "test"}}}}}
   325  	benchmarkMergeSeriesResponses(b, response)
   326  }
   327  
   328  func BenchmarkMergeSomeLabelResponses(b *testing.B) {
   329  	responses := []*LabelResponse{
   330  		{Values: []string{"test"}},
   331  		{Values: []string{"test2"}},
   332  		{Values: []string{"test3"}},
   333  	}
   334  	benchmarkMergeLabelResponses(b, responses)
   335  }
   336  
   337  func BenchmarkMergeSomeSeriesResponses(b *testing.B) {
   338  	responses := []*SeriesResponse{
   339  		{Series: []SeriesIdentifier{{Labels: map[string]string{"test": "test"}}}},
   340  		{Series: []SeriesIdentifier{{Labels: map[string]string{"test2": "test2"}}}},
   341  		{Series: []SeriesIdentifier{{Labels: map[string]string{"test3": "test3"}}}},
   342  	}
   343  	benchmarkMergeSeriesResponses(b, responses)
   344  }
   345  
   346  func BenchmarkMergeManyLabelResponses(b *testing.B) {
   347  	responses := []*LabelResponse{}
   348  	for i := 0; i < 20; i++ {
   349  		responses = append(responses, &LabelResponse{Values: []string{fmt.Sprintf("test%d", i)}})
   350  	}
   351  	benchmarkMergeLabelResponses(b, responses)
   352  }
   353  
   354  func BenchmarkMergeManySeriesResponses(b *testing.B) {
   355  	responses := []*SeriesResponse{}
   356  	for i := 0; i < 20; i++ {
   357  		test := fmt.Sprintf("test%d", i)
   358  		responses = append(responses, &SeriesResponse{Series: []SeriesIdentifier{{Labels: map[string]string{test: test}}}})
   359  	}
   360  	benchmarkMergeSeriesResponses(b, responses)
   361  }