trpc.group/trpc-go/trpc-go@v1.0.3/rpcz/span_test.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package rpcz
    15  
    16  import (
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func Test_span_AddEvent(t *testing.T) {
    24  	t.Run("add event sequentially", func(t *testing.T) {
    25  		s := newSpan("cyk", 666, nil)
    26  		events := []string{"sing", "dance", "rap"}
    27  		for _, e := range events {
    28  			time.Sleep(10 * time.Millisecond)
    29  			s.AddEvent(e)
    30  		}
    31  		for i, e := range events {
    32  			require.Equal(t, e, s.events[i].Name)
    33  			if i+1 < len(events) {
    34  				require.True(t, s.events[i].Time.Before(s.events[i+1].Time))
    35  			}
    36  		}
    37  	})
    38  }
    39  
    40  func Test_span_SetAttribute(t *testing.T) {
    41  	t.Run("set value of attribute as slice", func(t *testing.T) {
    42  		sliceValue := []string{"filter1", "filter2"}
    43  		s := &span{}
    44  		s.SetAttribute(TRPCAttributeFilterNames, sliceValue)
    45  
    46  		sliceValue[0] = "filter3"
    47  		require.Equal(t, sliceValue, s.attributes[0].Value)
    48  
    49  		sliceValue = sliceValue[1:]
    50  		require.NotEqual(t, sliceValue, s.attributes[0].Value)
    51  	})
    52  	t.Run("set same attribute twice", func(t *testing.T) {
    53  		s := &span{}
    54  		s.SetAttribute("Decode", "success")
    55  		s.SetAttribute("Decode", "failed")
    56  		require.Len(t, s.attributes, 2)
    57  		attribute, ok := s.Attribute("Decode")
    58  		require.True(t, ok)
    59  		require.Equal(t, "failed", attribute)
    60  	})
    61  	t.Run("set different attribute", func(t *testing.T) {
    62  		s := &span{}
    63  		s.SetAttribute("Decode", "success")
    64  		require.Len(t, s.attributes, 1)
    65  		require.Equal(t, "success", s.attributes[0].Value)
    66  
    67  		s.SetAttribute("Encode", "failed")
    68  		require.Len(t, s.attributes, 2)
    69  		require.Equal(t, "failed", s.attributes[1].Value)
    70  	})
    71  }
    72  
    73  func Test_span_Attribute(t *testing.T) {
    74  	t.Run("modify return directly will affect related Span", func(t *testing.T) {
    75  		s := &span{}
    76  		s.SetAttribute("Decode", &[]int{1})
    77  		aRaw1, ok := s.Attribute("Decode")
    78  		require.True(t, ok)
    79  		a1 := aRaw1.(*[]int)
    80  		*a1 = append(*a1, 2)
    81  
    82  		aRaw2, ok := s.Attribute("Decode")
    83  		require.True(t, ok)
    84  		a2 := aRaw2.(*[]int)
    85  		require.Same(t, a1, a2)
    86  	})
    87  }
    88  
    89  func Test_span_StartTime(t *testing.T) {
    90  	ps := &span{}
    91  	require.Zero(t, ps.StartTime())
    92  
    93  	cs, ender := ps.NewChild("")
    94  	defer ender.End()
    95  	require.NotZero(t, cs.StartTime())
    96  }
    97  
    98  func Test_Span_EndTime(t *testing.T) {
    99  	t.Run("*span new child", func(t *testing.T) {
   100  		ps := &span{}
   101  		require.Zero(t, ps.EndTime())
   102  
   103  		cs, ender := ps.NewChild("")
   104  		require.Zero(t, cs.EndTime())
   105  
   106  		ender.End()
   107  		require.NotZero(t, cs.EndTime())
   108  	})
   109  	t.Run("rpcz new child", func(t *testing.T) {
   110  		rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: 1})
   111  		ps, psEnder := rpcz.NewChild("")
   112  		require.Zero(t, ps.EndTime())
   113  
   114  		cs, csEnder := ps.NewChild("")
   115  		require.Zero(t, cs.EndTime())
   116  
   117  		csEnder.End()
   118  		require.NotZero(t, cs.EndTime())
   119  		require.Zero(t, ps.EndTime())
   120  
   121  		psEnder.End()
   122  		require.NotZero(t, ps.EndTime())
   123  	})
   124  
   125  }
   126  
   127  func Test_span_End(t *testing.T) {
   128  	t.Run("put span to pool directly if span's parent don't have this child span", func(t *testing.T) {
   129  		s := &span{
   130  			id:        1,
   131  			name:      "server",
   132  			parent:    &span{},
   133  			startTime: time.Now(),
   134  			childSpans: []*span{
   135  				{name: "client1"},
   136  				{name: "client2"},
   137  			},
   138  			events: []Event{
   139  				{Name: "locking"},
   140  				{Name: "locked"},
   141  				{Name: "unlocked"},
   142  			},
   143  			attributes: []Attribute{
   144  				{
   145  					Name:  "size",
   146  					Value: 67,
   147  				},
   148  			},
   149  		}
   150  		require.True(t, s.endTime.IsZero())
   151  
   152  		s.End()
   153  		require.Equal(t, &span{
   154  			id:         nilSpanID,
   155  			childSpans: []*span{},
   156  			events:     []Event{},
   157  			attributes: []Attribute{},
   158  		}, s)
   159  	})
   160  	t.Run("put span to pool directly if span's parent is nil", func(t *testing.T) {
   161  		s := &span{
   162  			id:        1,
   163  			name:      "server",
   164  			parent:    nil,
   165  			startTime: time.Now(),
   166  			childSpans: []*span{
   167  				{name: "client1"},
   168  				{name: "client2"},
   169  			},
   170  			events: []Event{
   171  				{Name: "locking"},
   172  				{Name: "locked"},
   173  				{Name: "unlocked"},
   174  			},
   175  			attributes: []Attribute{
   176  				{
   177  					Name:  "size",
   178  					Value: 67,
   179  				},
   180  			},
   181  		}
   182  		require.True(t, s.endTime.IsZero())
   183  		require.Panicsf(t, s.End, "should panic because of nil pointer dereference")
   184  	})
   185  	t.Run("calling End repeatedly won't reset endTime ", func(t *testing.T) {
   186  		ps := newSpan("server", 1, NewRPCZ(&Config{Capacity: 100}))
   187  		cs, end := ps.NewChild("client")
   188  		require.Equal(t, cs, ps.childSpans[0])
   189  
   190  		end.End()
   191  		endTime := ps.childSpans[0].endTime
   192  		require.False(t, endTime.IsZero(), "child span has been record to parent span")
   193  
   194  		end.End()
   195  		require.Equal(t, endTime, cs.(*span).endTime)
   196  	})
   197  	t.Run("record root span to rpcz", func(t *testing.T) {
   198  		rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: 10})
   199  		const spanName = "server"
   200  		s, end := rpcz.NewChild(spanName)
   201  		id := s.ID()
   202  		end.End()
   203  
   204  		readOnlySpan, ok := rpcz.Query(id)
   205  		require.True(t, ok)
   206  		require.Equal(t, id, readOnlySpan.ID)
   207  		require.Equal(t, spanName, readOnlySpan.Name)
   208  	})
   209  	t.Run("try record root span to rpcz more than once", func(t *testing.T) {
   210  		rpcz := NewRPCZ(&Config{Fraction: 1, Capacity: 10})
   211  		s, end := rpcz.NewChild("server")
   212  		id := s.ID()
   213  		require.Zero(t, rpcz.store.spans.length)
   214  
   215  		end.End()
   216  		require.Equal(t, uint32(1), rpcz.store.spans.length)
   217  		readOnlySpan1, ok := rpcz.Query(id)
   218  		require.True(t, ok)
   219  
   220  		end.End()
   221  		require.Equal(t, uint32(1), rpcz.store.spans.length)
   222  		readOnlySpan2, ok := rpcz.Query(id)
   223  		require.True(t, ok)
   224  		require.Equal(t, readOnlySpan1, readOnlySpan2)
   225  	})
   226  	t.Run("record span to root span", func(t *testing.T) {
   227  		ps := newSpan("server", 1, NewRPCZ(&Config{Capacity: 100}))
   228  		cs, end := ps.NewChild("client")
   229  		require.Equal(t, cs, ps.childSpans[0])
   230  
   231  		end.End()
   232  		require.False(t, ps.childSpans[0].endTime.IsZero(), "child span has been record to parent span")
   233  	})
   234  	t.Run("record span trigger reclaims memory of span pool, and parent span has been recycled", func(t *testing.T) {
   235  		rpcz := NewRPCZ(&Config{Capacity: 1, Fraction: 1.0})
   236  		ps1, endPS1 := rpcz.NewChild("server")
   237  		{
   238  			ps1 := ps1.(*span)
   239  			cs1, endCS1 := ps1.NewChild("client")
   240  			cs1Cast := cs1.(*span)
   241  			readOnlySpan := cs1Cast.convertedToReadOnlySpan()
   242  			require.Equal(t, cs1Cast, ps1.childSpans[0])
   243  
   244  			endPS1.End()
   245  			require.Equal(t, cs1, ps1.childSpans[0])
   246  
   247  			_, endPS2 := rpcz.NewChild("server")
   248  			endPS2.End()
   249  			require.Empty(t, ps1.childSpans, "ps1 should been put in pool due limit of rpcz's capacity")
   250  			require.Equal(t, readOnlySpan, cs1Cast.convertedToReadOnlySpan(), "cs shouldn't be put in pool before call end")
   251  
   252  			endCS1.End()
   253  			require.Equal(t, &span{id: nilSpanID}, cs1Cast, "cs should been put in pool if its parent has been recycled")
   254  		}
   255  	})
   256  	t.Run("record span trigger reclaims memory of span pool, and parent span hasn't been recycled", func(t *testing.T) {
   257  		rpcz := NewRPCZ(&Config{Capacity: 1, Fraction: 1.0})
   258  		ps1, endPS1 := rpcz.NewChild("server")
   259  		ps1Cast := ps1.(*span)
   260  		cs1, endCS1 := ps1Cast.NewChild("client")
   261  		cs1Cast := cs1.(*span)
   262  		expectedReadOnlySpan := cs1Cast.convertedToReadOnlySpan()
   263  		require.Equal(t, cs1, ps1Cast.childSpans[0])
   264  
   265  		endPS1.End()
   266  		require.Equal(t, cs1, ps1Cast.childSpans[0])
   267  
   268  		_, endPS2 := rpcz.NewChild("server")
   269  
   270  		endCS1.End()
   271  		require.True(t, cs1Cast.isEnded())
   272  		readOnlySpan := cs1Cast.convertedToReadOnlySpan()
   273  		readOnlySpan.EndTime = expectedReadOnlySpan.EndTime
   274  		require.Equal(
   275  			t,
   276  			expectedReadOnlySpan,
   277  			readOnlySpan,
   278  			"cs shouldn't been put in pool if its parent hasn't been recycled",
   279  		)
   280  
   281  		endPS2.End()
   282  		require.Empty(t, ps1Cast.childSpans, "ps1 should been put in pool due limit of rpcz's capacity")
   283  		require.Equal(t, &span{id: nilSpanID}, cs1, "parent span has put his child span to pool")
   284  	})
   285  }
   286  
   287  func Test_span_ID(t *testing.T) {
   288  	s := &span{}
   289  	require.Equal(t, SpanID(0), s.ID())
   290  
   291  	s = newSpan("", 1, nil)
   292  	require.Equal(t, SpanID(1), s.ID())
   293  }
   294  
   295  func Test_span_Name(t *testing.T) {
   296  	rpcz := NewRPCZ(&Config{Fraction: 1, Capacity: 10})
   297  	s, end := rpcz.NewChild("server")
   298  	defer end.End()
   299  	require.Equal(t, "server", s.Name())
   300  }
   301  
   302  func Test_span_attribute(t *testing.T) {
   303  	s := span{}
   304  
   305  	t.Run("span doesn't have specific attribute", func(t *testing.T) {
   306  		v, ok := s.Attribute("non-exist-attribute")
   307  		require.False(t, ok)
   308  		require.Nil(t, v)
   309  	})
   310  	t.Run("span has specific attribute", func(t *testing.T) {
   311  		const (
   312  			name  = "ResponseSize"
   313  			value = 101
   314  		)
   315  		s.SetAttribute(name, value)
   316  		v, ok := s.Attribute(name)
   317  		require.True(t, ok)
   318  		require.Equal(t, value, v)
   319  	})
   320  }
   321  
   322  func Test_span_NewChild(t *testing.T) {
   323  	t.Run("parent span doesn't have TRPCAttributeFilterNames", func(t *testing.T) {
   324  		ps := newSpan("server", 1, NewRPCZ(&Config{Capacity: 10}))
   325  		cs, _ := ps.NewChild("client")
   326  		{
   327  			cs := cs.(*span)
   328  			require.Equal(t, ps, cs.parent)
   329  			v, ok := cs.Attribute(TRPCAttributeFilterNames)
   330  			require.False(t, ok)
   331  			require.Nil(t, v)
   332  		}
   333  	})
   334  }
   335  
   336  func Test_span_Child(t *testing.T) {
   337  	t.Run("query child ok", func(t *testing.T) {
   338  		rpcz := NewRPCZ(&Config{Capacity: 1, Fraction: 1.0})
   339  		s, ender := rpcz.NewChild("CoV")
   340  		_, csEnder := s.NewChild("alpha")
   341  		_, ok := s.Child("alpha")
   342  		require.True(t, ok)
   343  
   344  		csEnder.End()
   345  		_, ok = s.Child("alpha")
   346  		require.True(t, ok)
   347  
   348  		ender.End()
   349  		_, ok = s.Child("alpha")
   350  		require.True(t, ok)
   351  	})
   352  	t.Run("modify *span.Child return result", func(t *testing.T) {
   353  		rpcz := NewRPCZ(&Config{Capacity: 1, Fraction: 1.0})
   354  		s, ender := rpcz.NewChild("CoV")
   355  		cs, csEnder := s.NewChild("beta")
   356  		{
   357  			cs, _ := s.Child("beta")
   358  			cs.SetAttribute("ATK", "9999+ points")
   359  		}
   360  		_, ok := cs.Attribute("ATK")
   361  		require.True(t, ok)
   362  		csEnder.End()
   363  		ender.End()
   364  	})
   365  	t.Run("query child failed", func(t *testing.T) {
   366  		rpcz := NewRPCZ(&Config{Capacity: 1, Fraction: 1.0})
   367  		s, ender := rpcz.NewChild("CoV")
   368  		defer ender.End()
   369  		_, csEnder := s.NewChild("gamma")
   370  		defer csEnder.End()
   371  		c, ok := s.Child("delta")
   372  		require.False(t, ok)
   373  		require.Nil(t, c)
   374  	})
   375  }