github.com/GuanceCloud/cliutils@v1.1.21/point/new_point_test.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package point
     7  
     8  import (
     9  	"fmt"
    10  	"math"
    11  	"math/rand"
    12  	"sort"
    13  	"testing"
    14  	T "testing"
    15  	"time"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func getNFields(n int) map[string]interface{} {
    21  	i := 0
    22  	fields := map[string]interface{}{}
    23  	for {
    24  		var v interface{}
    25  		v = i // int
    26  
    27  		if i%2 == 0 { // string
    28  			v = fmt.Sprintf("fieldv-%d", i)
    29  		}
    30  
    31  		if i%3 == 0 { // float
    32  			v = rand.NormFloat64()
    33  		}
    34  
    35  		if i%4 == 0 { // bool
    36  			if i/2%2 == 0 {
    37  				v = false
    38  			} else {
    39  				v = true
    40  			}
    41  		}
    42  
    43  		fields[fmt.Sprintf("field-%d", i)] = v
    44  		if i > n {
    45  			return fields
    46  		}
    47  
    48  		i++
    49  	}
    50  }
    51  
    52  func TestV2NewPoint(t *T.T) {
    53  	t.Run("valid-fields", func(t *T.T) {
    54  		pt := NewPointV2("abc", NewKVs(
    55  			map[string]interface{}{
    56  				"[]byte":  []byte("abc"),
    57  				"[]uint8": []uint8("abc"),
    58  
    59  				"b-false":   false,
    60  				"b-true":    true,
    61  				"float":     1.0,
    62  				"float32":   float32(1.0),
    63  				"float64":   float64(1.0),
    64  				"float64-2": float64(1.1),
    65  				"i":         int(1),
    66  				"i16":       int16(1),
    67  				"i32":       int32(1),
    68  				"i64":       int64(1),
    69  				"i8":        int8(1),
    70  				"u":         uint(1),
    71  				"u16":       uint16(1),
    72  				"u32":       uint32(1),
    73  				"u64-large": uint64(math.MaxInt64 + 1), // skipped in expect string
    74  				"u64":       uint64(1),
    75  				"u8":        uint8(1),
    76  			}), WithTime(time.Unix(0, 123)))
    77  
    78  		assert.Equal(t, `abc []byte="YWJj"b,[]uint8="YWJj"b,b-false=false,b-true=true,float=1,float32=1,float64=1,float64-2=1.1,i=1i,i16=1i,i32=1i,i64=1i,i8=1i,u=1u,u16=1u,u32=1u,u64=1u,u64-large=9223372036854775808u,u8=1u 123`,
    79  			pt.LineProto())
    80  	})
    81  
    82  	t.Run("valid-fields-under-pb", func(t *T.T) {
    83  		kvs := map[string]interface{}{
    84  			"[]byte":    []byte("abc"),
    85  			"[]uint8":   []uint8("abc"),
    86  			"b-false":   false,
    87  			"b-true":    true,
    88  			"float":     1.0,
    89  			"float32":   float32(1.0),
    90  			"float64":   float64(1.0),
    91  			"float64-2": float64(1.1),
    92  			"i":         int(1),
    93  			"i16":       int16(1),
    94  			"i32":       int32(1),
    95  			"i64":       int64(1),
    96  			"i8":        int8(1),
    97  			"u":         uint(1),
    98  			"u16":       uint16(1),
    99  			"u32":       uint32(1),
   100  			"u64":       uint64(1),
   101  			"u64-large": uint64(math.MaxInt64 + 1), // skipped in expect string
   102  			"u8":        uint8(1),
   103  		}
   104  		pt := NewPointV2(`abc`, NewKVs(kvs), WithTime(time.Unix(0, 123)), WithEncoding(Protobuf))
   105  		expect := `abc []byte="YWJj"b,[]uint8="YWJj"b,b-false=false,b-true=true,float=1,float32=1,float64=1,float64-2=1.1,i=1i,i16=1i,i32=1i,i64=1i,i8=1i,u=1u,u16=1u,u32=1u,u64=1u,u64-large=9223372036854775808u,u8=1u 123`
   106  		assert.Equal(t, expect, pt.LineProto())
   107  	})
   108  
   109  	t.Run("basic", func(t *T.T) {
   110  		kvs := NewKVs(map[string]interface{}{"f1": 12}).MustAddTag(`t1`, `tval1`)
   111  		pt := NewPointV2(`abc`, kvs, WithTime(time.Unix(0, 123)))
   112  
   113  		assert.Equal(t, "abc,t1=tval1 f1=12i 123", pt.LineProto())
   114  	})
   115  }
   116  
   117  func TestNewPoint(t *T.T) {
   118  	cases := []struct {
   119  		opts []Option
   120  
   121  		tname, name, expect string
   122  
   123  		t map[string]string
   124  		f map[string]interface{}
   125  
   126  		warns    int
   127  		withPool bool
   128  	}{
   129  		{
   130  			tname:    "valid-fields-with-point-pool",
   131  			opts:     []Option{WithTime(time.Unix(0, 123))},
   132  			name:     "valid-fields",
   133  			withPool: true,
   134  			f: map[string]interface{}{
   135  				"[]byte":  []byte("abc"),
   136  				"[]uint8": []uint8("abc"),
   137  
   138  				"b-false":   false,
   139  				"b-true":    true,
   140  				"float":     1.0,
   141  				"float32":   float32(1.0),
   142  				"float64":   float64(1.0),
   143  				"float64-2": float64(1.1),
   144  				"i":         int(1),
   145  				"i16":       int16(1),
   146  				"i32":       int32(1),
   147  				"i64":       int64(1),
   148  				"i8":        int8(1),
   149  				"u":         uint(1),
   150  				"u16":       uint16(1),
   151  				"u32":       uint32(1),
   152  				"u64":       uint64(1),
   153  				"u64-large": uint64(math.MaxInt64 + 1), // skipped in expect string
   154  				"u8":        uint8(1),
   155  			},
   156  			expect: `valid-fields []byte="YWJj"b,[]uint8="YWJj"b,b-false=false,b-true=true,float=1,float32=1,float64=1,float64-2=1.1,i=1i,i16=1i,i32=1i,i64=1i,i8=1i,u=1u,u16=1u,u32=1u,u64=1u,u64-large=9223372036854775808u,u8=1u 123`,
   157  		},
   158  
   159  		{
   160  			tname: "valid-fields",
   161  			opts:  []Option{WithTime(time.Unix(0, 123))},
   162  			name:  "valid-fields",
   163  			f: map[string]interface{}{
   164  				"[]byte":  []byte("abc"),
   165  				"[]uint8": []uint8("abc"),
   166  
   167  				"b-false":   false,
   168  				"b-true":    true,
   169  				"float":     1.0,
   170  				"float32":   float32(1.0),
   171  				"float64":   float64(1.0),
   172  				"float64-2": float64(1.1),
   173  				"i":         int(1),
   174  				"i16":       int16(1),
   175  				"i32":       int32(1),
   176  				"i64":       int64(1),
   177  				"i8":        int8(1),
   178  				"u":         uint(1),
   179  				"u16":       uint16(1),
   180  				"u32":       uint32(1),
   181  				"u64":       uint64(1),
   182  				"u64-large": uint64(math.MaxInt64 + 1), // skipped in expect string
   183  				"u8":        uint8(1),
   184  			},
   185  			expect: `valid-fields []byte="YWJj"b,[]uint8="YWJj"b,b-false=false,b-true=true,float=1,float32=1,float64=1,float64-2=1.1,i=1i,i16=1i,i32=1i,i64=1i,i8=1i,u=1u,u16=1u,u32=1u,u64=1u,u64-large=9223372036854775808u,u8=1u 123`,
   186  		},
   187  
   188  		{
   189  			tname: "valid-fields-under-pb",
   190  			opts:  []Option{WithTime(time.Unix(0, 123)), WithEncoding(Protobuf)},
   191  			name:  "valid-fields",
   192  			f: map[string]interface{}{
   193  				"[]byte":    []byte("abc"),
   194  				"[]uint8":   []uint8("abc"),
   195  				"b-false":   false,
   196  				"b-true":    true,
   197  				"float":     1.0,
   198  				"float32":   float32(1.0),
   199  				"float64":   float64(1.0),
   200  				"float64-2": float64(1.1),
   201  				"i":         int(1),
   202  				"i16":       int16(1),
   203  				"i32":       int32(1),
   204  				"i64":       int64(1),
   205  				"i8":        int8(1),
   206  				"u":         uint(1),
   207  				"u16":       uint16(1),
   208  				"u32":       uint32(1),
   209  				"u64":       uint64(1),
   210  				"u64-large": uint64(math.MaxInt64 + 1), // skipped in expect string
   211  				"u8":        uint8(1),
   212  			},
   213  			expect: `valid-fields []byte="YWJj"b,[]uint8="YWJj"b,b-false=false,b-true=true,float=1,float32=1,float64=1,float64-2=1.1,i=1i,i16=1i,i32=1i,i64=1i,i8=1i,u=1u,u16=1u,u32=1u,u64=1u,u64-large=9223372036854775808u,u8=1u 123`,
   214  		},
   215  
   216  		{
   217  			tname: "exceed-measurement-len",
   218  			opts:  []Option{WithTime(time.Unix(0, 123)), WithMaxMeasurementLen(10)},
   219  
   220  			name:   "name-exceed-len",
   221  			f:      map[string]interface{}{"f1": 123},
   222  			expect: `name-excee f1=123i 123`,
   223  			warns:  1,
   224  		},
   225  
   226  		{
   227  			tname:  "empty-measurement",
   228  			opts:   []Option{WithTime(time.Unix(0, 123))},
   229  			name:   "",
   230  			f:      map[string]interface{}{"f1": 123},
   231  			expect: fmt.Sprintf(`%s f1=123i 123`, DefaultMeasurementName),
   232  			warns:  1,
   233  		},
   234  
   235  		//{
   236  		//	tname:  "exceed-tag-kv-compose",
   237  		//	opts:   []Option{WithTime(time.Unix(0, 123)), WithMaxKVComposeLen(10)},
   238  		//	name:   "abc",
   239  		//	t:      map[string]string{"t1": "12345", "t2": "ssclh"},
   240  		//	f:      map[string]interface{}{"f1": 123},
   241  		//	expect: `abc,t1=12345 f1=123i 123`,
   242  		//	warns:  1,
   243  		// },
   244  
   245  		{
   246  			tname:  "basic",
   247  			opts:   []Option{WithTime(time.Unix(0, 123))},
   248  			name:   "abc",
   249  			t:      map[string]string{"t1": "tval1"},
   250  			f:      map[string]interface{}{"f1": 12},
   251  			expect: "abc,t1=tval1 f1=12i 123",
   252  		},
   253  		{
   254  			tname:  "metric-with-dot-in-field-key",
   255  			name:   "abc",
   256  			opts:   append(DefaultMetricOptions(), WithTime(time.Unix(0, 123))),
   257  			t:      map[string]string{"t1": "tval1"},
   258  			f:      map[string]interface{}{"f.1": 12},
   259  			expect: "abc,t1=tval1 f.1=12i 123",
   260  		},
   261  		{
   262  			tname:  "metric-with-dot-in-tag-key",
   263  			name:   "abc",
   264  			opts:   append(DefaultMetricOptions(), WithTime(time.Unix(0, 123))),
   265  			t:      map[string]string{"t.1": "tval1"},
   266  			f:      map[string]interface{}{"f1": 12},
   267  			expect: "abc,t.1=tval1 f1=12i 123",
   268  		},
   269  		{
   270  			tname: "with-dot-in-t-f-key-on-non-metric-type",
   271  			name:  "abc",
   272  			opts:  append(DefaultObjectOptions(), WithTime(time.Unix(0, 123))),
   273  
   274  			t:      map[string]string{"t1": "tval1"},
   275  			f:      map[string]interface{}{"f.1": 12},
   276  			expect: fmt.Sprintf(`abc,t1=tval1 f_1=12i,name="%s" 123`, defaultObjectName),
   277  			warns:  2,
   278  		},
   279  
   280  		{
   281  			tname:  "with-dot-in-tag-field-key",
   282  			name:   "abc",
   283  			opts:   append(DefaultObjectOptions(), WithTime(time.Unix(0, 123))),
   284  			t:      map[string]string{"t1": "abc", "t.2": "xyz"},
   285  			f:      map[string]interface{}{"f1": 123, "f.2": "def"},
   286  			expect: fmt.Sprintf(`abc,t1=abc,t_2=xyz f1=123i,f_2="def",name="%s" 123`, defaultObjectName),
   287  			warns:  3,
   288  		},
   289  
   290  		{
   291  			tname: "both-exceed-max-field-tag-count",
   292  			name:  "abc",
   293  			t: map[string]string{
   294  				"t1": "abc",
   295  				"t2": "xyz",
   296  				"t3": "abc",
   297  				"t4": "xyz",
   298  				"t5": "abc",
   299  				"t6": "abc",
   300  				"t7": "abc",
   301  				"t8": "abc",
   302  				"t9": "abc",
   303  			},
   304  			f: map[string]interface{}{
   305  				"f1": 123,
   306  				"f2": "def",
   307  				"f3": "def",
   308  				"f4": "def",
   309  				"f5": "def",
   310  				"f6": "def",
   311  				"f7": "def",
   312  				"f8": "def",
   313  				"f9": "def",
   314  			},
   315  			opts: []Option{
   316  				WithTime(time.Unix(0, 123)),
   317  				WithMaxTags(1),
   318  				WithMaxFields(1),
   319  				WithKeySorted(true),
   320  			},
   321  			expect: `abc,t1=abc f1=123i 123`,
   322  			warns:  2,
   323  		},
   324  
   325  		{
   326  			tname: "exceed-max-field-count",
   327  			name:  "abc",
   328  			opts:  []Option{WithTime(time.Unix(0, 123)), WithMaxFields(1), WithKeySorted(true)},
   329  			t: map[string]string{
   330  				"t1": "abc",
   331  				"t2": "xyz",
   332  			},
   333  			f: map[string]interface{}{
   334  				"f1": 123,
   335  				"f2": "def",
   336  				"f3": "def",
   337  				"f4": "def",
   338  				"f5": "def",
   339  				"f6": "def",
   340  				"f7": "def",
   341  				"f8": "def",
   342  				"f9": "def",
   343  			},
   344  			expect: `abc,t1=abc,t2=xyz f1=123i 123`,
   345  			warns:  1,
   346  		},
   347  
   348  		{
   349  			tname: "exceed-max-tag-count",
   350  			opts:  []Option{WithTime(time.Unix(0, 123)), WithMaxTags(1), WithKeySorted(true)},
   351  			name:  "abc",
   352  			t: map[string]string{
   353  				"t1": "abc",
   354  				"t2": "xyz",
   355  				"t3": "abc",
   356  				"t4": "xyz",
   357  				"t5": "abc",
   358  				"t6": "abc",
   359  				"t7": "abc",
   360  				"t8": "abc",
   361  				"t9": "abc",
   362  			},
   363  			f: map[string]interface{}{
   364  				"f1": 123,
   365  			},
   366  			expect: `abc,t1=abc f1=123i 123`,
   367  			warns:  1,
   368  		},
   369  
   370  		{
   371  			tname:  "exceed-max-tag-key-len",
   372  			opts:   []Option{WithTime(time.Unix(0, 123)), WithMaxTagKeyLen(1)},
   373  			name:   "abc",
   374  			t:      map[string]string{"t1": "x"},
   375  			f:      map[string]interface{}{"f1": 123},
   376  			expect: `abc,t=x f1=123i 123`,
   377  			warns:  1,
   378  		},
   379  
   380  		{
   381  			tname:  "exceed-max-tag-value-len",
   382  			opts:   []Option{WithTime(time.Unix(0, 123)), WithMaxTagValLen(3)},
   383  			name:   "abc",
   384  			t:      map[string]string{"t": "1234"},
   385  			f:      map[string]interface{}{"f1": 123},
   386  			expect: `abc,t=123 f1=123i 123`,
   387  			warns:  1,
   388  		},
   389  
   390  		{
   391  			tname:  "exceed-max-field-key-len",
   392  			name:   "abc",
   393  			opts:   []Option{WithTime(time.Unix(0, 123)), WithMaxFieldKeyLen(3)},
   394  			f:      map[string]interface{}{"f123": 123},
   395  			expect: `abc f12=123i 123`,
   396  			warns:  1,
   397  		},
   398  
   399  		{
   400  			tname:  "exceed-max-field-val-len",
   401  			name:   "abc",
   402  			opts:   []Option{WithTime(time.Unix(0, 123)), WithMaxFieldValLen(3)},
   403  			f:      map[string]interface{}{"f1": "hello"},
   404  			expect: `abc f1="hel" 123`,
   405  			warns:  1,
   406  		},
   407  
   408  		{
   409  			tname: "with-disabled-tag-key-source",
   410  			name:  "abc",
   411  			opts:  append(DefaultLoggingOptions(), WithTime(time.Unix(0, 123))),
   412  
   413  			t:      map[string]string{"source": "s1"},
   414  			f:      map[string]interface{}{"f1": 123},
   415  			expect: fmt.Sprintf(`abc f1=123i,status="%s" 123`, defaultLoggingStatus),
   416  			warns:  2,
   417  		},
   418  		{
   419  			tname:  "with-disabled-field-key",
   420  			name:   "abc",
   421  			opts:   append(DefaultObjectOptions(), WithTime(time.Unix(0, 123))),
   422  			t:      map[string]string{"class": "xyz"},
   423  			f:      map[string]interface{}{"class": 123, "f1": 1},
   424  			expect: fmt.Sprintf(`abc f1=1i,name="%s" 123`, defaultObjectName),
   425  
   426  			// NOTE: tag key `class` override field `class`, then the tag disabled
   427  			warns: 2,
   428  		},
   429  
   430  		{
   431  			tname:  "normal",
   432  			opts:   []Option{WithTime(time.Unix(0, 123))},
   433  			name:   "abc",
   434  			t:      map[string]string{},
   435  			f:      map[string]interface{}{"f1": 123},
   436  			expect: "abc f1=123i 123",
   437  		},
   438  
   439  		{
   440  			tname:  "invalid-category",
   441  			opts:   []Option{WithTime(time.Unix(0, 123))},
   442  			name:   "abc",
   443  			f:      map[string]interface{}{"f1": 123},
   444  			expect: `abc f1=123i 123`,
   445  		},
   446  
   447  		{
   448  			tname: "nil-opiton",
   449  			name:  "abc",
   450  			t:     map[string]string{},
   451  			f:     map[string]interface{}{"f1": 123},
   452  		},
   453  	}
   454  
   455  	for _, tc := range cases {
   456  		t.Run(tc.tname, func(t *T.T) {
   457  			var (
   458  				pt  *Point
   459  				err error
   460  			)
   461  
   462  			if tc.withPool {
   463  				pp := NewReservedCapPointPool(100)
   464  				SetPointPool(pp)
   465  				defer func() {
   466  					ClearPointPool()
   467  
   468  					assert.True(t, pt.HasFlag(Ppooled))
   469  					pp.Put(pt)
   470  				}()
   471  			}
   472  
   473  			pt, err = NewPoint(tc.name, tc.t, tc.f, tc.opts...)
   474  
   475  			assert.NoError(t, err)
   476  
   477  			x := pt.LineProto()
   478  
   479  			if tc.expect != "" {
   480  				assert.Equal(t, tc.expect, x, "pt: %s, kvs: %s", pt.Pretty(), KVs(pt.pt.Fields).Pretty())
   481  			} else {
   482  				assert.NotEqual(t, x, "", "got %s", pt.Pretty())
   483  				t.Logf("got %s", x)
   484  			}
   485  
   486  			assert.Equal(t, tc.warns, len(pt.pt.Warns), "pt: %s", pt.Pretty())
   487  		})
   488  	}
   489  }
   490  
   491  func TestPointKeySorted(t *testing.T) {
   492  	t.Run("sorted", func(t *testing.T) {
   493  		pt, err := NewPoint("basic",
   494  			map[string]string{
   495  				"t1": "v1",
   496  				"t2": "v2",
   497  				"t3": "v1",
   498  				"t4": "v2",
   499  				"t5": "v1",
   500  				"t6": "v2",
   501  				"t7": "v1",
   502  				"t8": "v2",
   503  			},
   504  			map[string]any{
   505  				"f1": 1,
   506  				"f2": 2,
   507  				"f3": 3,
   508  				"f4": 1,
   509  				"f5": 2,
   510  				"f6": 3,
   511  				"f7": 1,
   512  				"f8": 2,
   513  				"f9": 3,
   514  			},
   515  			WithKeySorted(true),
   516  		)
   517  
   518  		assert.NoError(t, err)
   519  
   520  		assert.True(t, sort.IsSorted(KVs(pt.pt.Fields)))
   521  
   522  		t.Logf("pt: %s", pt.Pretty())
   523  	})
   524  
   525  	t.Run("not-sorted", func(t *testing.T) {
   526  		pt, err := NewPoint("basic",
   527  			map[string]string{
   528  				"t1": "v1",
   529  				"t2": "v2",
   530  				"t3": "v1",
   531  				"t4": "v2",
   532  				"t5": "v1",
   533  				"t6": "v2",
   534  				"t7": "v1",
   535  				"t8": "v2",
   536  			},
   537  			map[string]any{
   538  				"f1": 1,
   539  				"f2": 2,
   540  				"f3": 3,
   541  				"f4": 1,
   542  				"f5": 2,
   543  				"f6": 3,
   544  				"f7": 1,
   545  				"f8": 2,
   546  				"f9": 3,
   547  			},
   548  			WithKeySorted(false),
   549  		)
   550  
   551  		assert.NoError(t, err)
   552  
   553  		assert.False(t, sort.IsSorted(KVs(pt.pt.Fields)))
   554  
   555  		t.Logf("pt: %s", pt.Pretty())
   556  	})
   557  }
   558  
   559  func BenchmarkV2NewPoint(b *T.B) {
   560  	b.Run(`with-maps`, func(b *T.B) {
   561  		for i := 0; i < b.N; i++ {
   562  			tags := map[string]string{
   563  				"t1": "val1",
   564  				"t2": "val2",
   565  				"t3": "val3",
   566  				"t4": "val4",
   567  				"t5": "val5",
   568  				"t6": "val6",
   569  				"t7": "val7",
   570  				"t8": "val8",
   571  				"t9": "val9",
   572  				"t0": "val0",
   573  			}
   574  			fields := map[string]interface{}{
   575  				"f1":  123,
   576  				"f2":  "abc",
   577  				"f3":  45.6,
   578  				"f4":  123,
   579  				"f5":  "abc",
   580  				"f6":  45.6,
   581  				"f7":  123,
   582  				"f8":  "abc",
   583  				"f9":  45.6,
   584  				"f10": false,
   585  			}
   586  
   587  			NewPointV2("abc", append(NewTags(tags), NewKVs(fields)...))
   588  		}
   589  	})
   590  
   591  	b.Run(`without-map-without-prealloc`, func(b *T.B) {
   592  		for i := 0; i < b.N; i++ {
   593  			var kvs KVs
   594  			kvs = kvs.AddTag("t1", "val1")
   595  			kvs = kvs.AddTag("t2", "val2")
   596  			kvs = kvs.AddTag("t3", "val3")
   597  			kvs = kvs.AddTag("t4", "val4")
   598  			kvs = kvs.AddTag("t5", "val5")
   599  			kvs = kvs.AddTag("t6", "val6")
   600  			kvs = kvs.AddTag("t7", "val7")
   601  			kvs = kvs.AddTag("t8", "val8")
   602  			kvs = kvs.AddTag("t9", "val9")
   603  			kvs = kvs.AddTag("t0", "val0")
   604  
   605  			kvs = kvs.Add("f1", 123, false, false)
   606  			kvs = kvs.Add("f2", "abc", false, false)
   607  			kvs = kvs.Add("f3", 45.6, false, false)
   608  			kvs = kvs.Add("f4", 123, false, false)
   609  			kvs = kvs.Add("f5", "abc", false, false)
   610  			kvs = kvs.Add("f6", 45.6, false, false)
   611  			kvs = kvs.Add("f7", 123, false, false)
   612  			kvs = kvs.Add("f8", "abc", false, false)
   613  			kvs = kvs.Add("f9", 45.6, false, false)
   614  			kvs = kvs.Add("f10", false, false, false)
   615  
   616  			NewPointV2("abc", kvs)
   617  		}
   618  	})
   619  
   620  	b.Run(`without-map-with-prealloc`, func(b *T.B) {
   621  		for i := 0; i < b.N; i++ {
   622  			kvs := make(KVs, 0, 20)
   623  			kvs = kvs.AddTag("t1", "val1")
   624  			kvs = kvs.AddTag("t2", "val2")
   625  			kvs = kvs.AddTag("t3", "val3")
   626  			kvs = kvs.AddTag("t5", "val4")
   627  			kvs = kvs.AddTag("t5", "val5")
   628  			kvs = kvs.AddTag("t6", "val6")
   629  			kvs = kvs.AddTag("t7", "val7")
   630  			kvs = kvs.AddTag("t8", "val8")
   631  			kvs = kvs.AddTag("t9", "val9")
   632  			kvs = kvs.AddTag("t0", "val0")
   633  
   634  			kvs = kvs.Add("f1", 123, false, false)
   635  			kvs = kvs.Add("f2", "abc", false, false)
   636  			kvs = kvs.Add("f3", 45.6, false, false)
   637  			kvs = kvs.Add("f4", 123, false, false)
   638  			kvs = kvs.Add("f5", "abc", false, false)
   639  			kvs = kvs.Add("f6", 45.6, false, false)
   640  			kvs = kvs.Add("f7", 123, false, false)
   641  			kvs = kvs.Add("f8", "abc", false, false)
   642  			kvs = kvs.Add("f9", 45.6, false, false)
   643  			kvs = kvs.Add("f10", false, false, false)
   644  
   645  			NewPointV2("abc", kvs)
   646  		}
   647  	})
   648  
   649  	b.Run(`without-map-with-prealloc-and-MUST`, func(b *T.B) {
   650  		for i := 0; i < b.N; i++ {
   651  			kvs := make(KVs, 0, 20)
   652  			kvs = kvs.MustAddTag("t1", "val1")
   653  			kvs = kvs.MustAddTag("t2", "val2")
   654  			kvs = kvs.MustAddTag("t3", "val3")
   655  			kvs = kvs.MustAddTag("t4", "val4")
   656  			kvs = kvs.MustAddTag("t5", "val5")
   657  			kvs = kvs.MustAddTag("t6", "val6")
   658  			kvs = kvs.MustAddTag("t7", "val7")
   659  			kvs = kvs.MustAddTag("t8", "val8")
   660  			kvs = kvs.MustAddTag("t9", "val9")
   661  			kvs = kvs.MustAddTag("t0", "val0")
   662  
   663  			kvs = kvs.Add("f1", 123, false, true)
   664  			kvs = kvs.Add("f2", "abc", false, true)
   665  			kvs = kvs.Add("f3", 45.6, false, true)
   666  			kvs = kvs.Add("f4", 123, false, true)
   667  			kvs = kvs.Add("f5", "abc", false, true)
   668  			kvs = kvs.Add("f6", 45.6, false, true)
   669  			kvs = kvs.Add("f7", 123, false, true)
   670  			kvs = kvs.Add("f8", "abc", false, true)
   671  			kvs = kvs.Add("f9", 45.6, false, true)
   672  			kvs = kvs.Add("f10", false, false, true)
   673  
   674  			NewPointV2("abc", kvs)
   675  		}
   676  	})
   677  
   678  	b.Run("with-point-pool-level3", func(b *T.B) {
   679  		pp := NewReservedCapPointPool(100)
   680  		SetPointPool(pp)
   681  
   682  		defer func() {
   683  			SetPointPool(nil)
   684  		}()
   685  
   686  		b.ResetTimer()
   687  		for i := 0; i < b.N; i++ {
   688  			kvs := make(KVs, 0, 20)
   689  			kvs = kvs.MustAddTag("t1", "val1")
   690  			kvs = kvs.MustAddTag("t2", "val2")
   691  			kvs = kvs.MustAddTag("t3", "val3")
   692  			kvs = kvs.MustAddTag("t4", "val4")
   693  			kvs = kvs.MustAddTag("t5", "val5")
   694  			kvs = kvs.MustAddTag("t6", "val6")
   695  			kvs = kvs.MustAddTag("t7", "val7")
   696  			kvs = kvs.MustAddTag("t8", "val8")
   697  			kvs = kvs.MustAddTag("t9", "val9")
   698  			kvs = kvs.MustAddTag("t0", "val0")
   699  
   700  			kvs = kvs.Add("f1", 123, false, true)
   701  			kvs = kvs.Add("f2", "abc", false, true)
   702  			kvs = kvs.Add("f3", 45.6, false, true)
   703  			kvs = kvs.Add("f4", 123, false, true)
   704  			kvs = kvs.Add("f5", "abc", false, true)
   705  			kvs = kvs.Add("f6", 45.6, false, true)
   706  			kvs = kvs.Add("f7", 123, false, true)
   707  			kvs = kvs.Add("f8", "abc", false, true)
   708  			kvs = kvs.Add("f9", 45.6, false, true)
   709  			kvs = kvs.Add("f10", false, false, true)
   710  
   711  			pt := NewPointV2("abc", kvs)
   712  			pp.Put(pt)
   713  		}
   714  	})
   715  
   716  	b.Run(`key-sorted`, func(b *T.B) {
   717  		ptName := `abc`
   718  		kvs := NewKVs(map[string]any{
   719  			"f1": 123,
   720  			"f2": 3.14,
   721  			"f3": "str",
   722  
   723  			"_f1": 123,
   724  			"_f2": 3.14,
   725  			"_f3": "str",
   726  
   727  			"_f1_": 123,
   728  			"_f2_": 3.14,
   729  			"_f3_": "str",
   730  		})
   731  
   732  		for i := 0; i < b.N; i++ {
   733  			NewPointV2(ptName, kvs, WithKeySorted(true))
   734  		}
   735  	})
   736  
   737  	b.Run(`key-not-sorted`, func(b *T.B) {
   738  		ptName := `abc`
   739  		kvs := NewKVs(map[string]any{
   740  			"f1": 123,
   741  			"f2": 3.14,
   742  			"f3": "str",
   743  
   744  			"_f1": 123,
   745  			"_f2": 3.14,
   746  			"_f3": "str",
   747  
   748  			"_f1_": 123,
   749  			"_f2_": 3.14,
   750  			"_f3_": "str",
   751  		})
   752  
   753  		for i := 0; i < b.N; i++ {
   754  			NewPointV2(ptName, kvs)
   755  		}
   756  	})
   757  }
   758  
   759  func FuzzPLPBEquality(f *testing.F) {
   760  	cases := []struct {
   761  		measurement string
   762  		tagk        string
   763  		tagv        string
   764  
   765  		i64  int64
   766  		u64  uint64
   767  		str  string
   768  		b    bool
   769  		f    float64
   770  		d    []byte
   771  		time int64
   772  	}{
   773  		{
   774  			measurement: "",
   775  			tagk:        "tag",
   776  			tagv:        "tval",
   777  
   778  			i64:  int64(1),
   779  			u64:  uint64(123),
   780  			str:  "hello",
   781  			b:    false,
   782  			f:    3.14,
   783  			d:    []byte("world"),
   784  			time: 123,
   785  		},
   786  	}
   787  
   788  	for _, tc := range cases {
   789  		f.Add(tc.measurement, tc.tagk, tc.tagv, tc.i64, tc.u64, tc.str, tc.b, tc.f, tc.d, tc.time)
   790  	}
   791  
   792  	f.Fuzz(func(t *testing.T,
   793  		measurement, tagk, tagv string,
   794  		i64 int64, u64 uint64, str string, b bool, f float64, d []byte, ts int64,
   795  	) {
   796  		if ts < 0 { // force ts > 0 to make 2 point's time are equal. under ts < 0, NewPoint will use time.Now()
   797  			ts = 0
   798  		}
   799  
   800  		lppt, err := NewPoint(measurement,
   801  			map[string]string{tagk: tagv},
   802  			map[string]interface{}{
   803  				"i64": i64,
   804  				"u64": u64,
   805  				"str": str,
   806  				"b":   b,
   807  				"f":   f,
   808  				"d":   d,
   809  			},
   810  			WithTimestamp(ts),
   811  			WithDotInKey(true) /* random string may contains '.' */)
   812  
   813  		assert.NoError(t, err)
   814  
   815  		pbpt, err := NewPoint(measurement,
   816  			map[string]string{tagk: tagv},
   817  			map[string]interface{}{
   818  				"i64": i64,
   819  				"u64": u64,
   820  				"str": str,
   821  				"b":   b,
   822  				"f":   f,
   823  				"d":   d,
   824  			},
   825  			WithTimestamp(ts),
   826  			WithDotInKey(true), // random string may contains '.'
   827  			WithEncoding(Protobuf))
   828  
   829  		assert.NoError(t, err)
   830  
   831  		_ = pbpt
   832  		_ = lppt
   833  
   834  		ok, why := lppt.EqualWithReason(pbpt)
   835  		assert.Truef(t, ok, "why: %s, ts: %d", why, ts)
   836  	})
   837  }
   838  
   839  func TestTimeUnix(t *T.T) {
   840  	t.Run("-1", func(t *T.T) {
   841  		t.Logf("date: %s", time.Unix(0, -1000).UTC())
   842  	})
   843  }