trpc.group/trpc-go/trpc-go@v1.0.3/rpcz/rpcz_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  	"fmt"
    18  	"testing"
    19  
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"trpc.group/trpc-go/trpc-go/log"
    23  )
    24  
    25  func TestRPCZ_NewChildSpan(t *testing.T) {
    26  	t.Run("disable rpcz", func(t *testing.T) {
    27  		rpcz := NewRPCZ(&Config{Fraction: 0.0, Capacity: 10})
    28  		s, _ := rpcz.NewChild("server")
    29  		require.Equal(t, rpcz, s)
    30  	})
    31  	t.Run("New span", func(t *testing.T) {
    32  		rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: 10})
    33  		s, _ := rpcz.NewChild("server")
    34  		sp := s.(*span)
    35  		require.Equal(t, rpcz, sp.parent)
    36  		require.Equal(t, "server", sp.name)
    37  	})
    38  }
    39  
    40  func TestRPCZ_Query(t *testing.T) {
    41  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: 10})
    42  	t.Run("span is not in rpcz", func(t *testing.T) {
    43  		_, ok := rpcz.Query(0)
    44  		require.False(t, ok)
    45  	})
    46  	t.Run("span is in rpcz", func(t *testing.T) {
    47  		s := newSpan("server", 1, rpcz)
    48  		s.End()
    49  		readOnlySpan, ok := rpcz.Query(1)
    50  		require.True(t, ok)
    51  		require.Equal(t, "server", readOnlySpan.Name)
    52  		require.Equal(t, SpanID(1), readOnlySpan.ID)
    53  	})
    54  	t.Run("span that contains child span is in rpcz", func(t *testing.T) {
    55  		s := newSpan("server", 2, rpcz)
    56  		_, end := s.NewChild("client")
    57  		end.End()
    58  		s.End()
    59  		readOnlySpan, ok := rpcz.Query(2)
    60  		require.True(t, ok)
    61  		childSpan := readOnlySpan.ChildSpans[0]
    62  		require.Equal(t, "client", childSpan.Name)
    63  		require.Equal(t, SpanID(2), childSpan.ID, "child span id is equal parent span")
    64  	})
    65  }
    66  
    67  func TestRPCZ_BatchQuery(t *testing.T) {
    68  	const insertedNum = 10
    69  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: insertedNum})
    70  
    71  	t.Run("query num less than zero", func(t *testing.T) {
    72  		require.Empty(t, rpcz.BatchQuery(-1))
    73  	})
    74  	t.Run("query num equals zero", func(t *testing.T) {
    75  		require.Empty(t, rpcz.BatchQuery(0))
    76  	})
    77  
    78  	recordSpansToRPCZ(insertedNum, rpcz)
    79  	t.Run("returns newly inserted span", func(t *testing.T) {
    80  		readOnlySpans := rpcz.BatchQuery(5)
    81  		require.Len(t, readOnlySpans, 5)
    82  		for i, s := range readOnlySpans {
    83  			require.Equal(t, fmt.Sprintf("server-%d", insertedNum-i), s.Name)
    84  		}
    85  	})
    86  	t.Run("query num greater than insertedNum of stored span", func(t *testing.T) {
    87  		readOnlySpans := rpcz.BatchQuery(insertedNum + 1)
    88  		require.Len(t, readOnlySpans, insertedNum)
    89  		for i, s := range readOnlySpans {
    90  			require.Equal(t, fmt.Sprintf("server-%d", insertedNum-i), s.Name)
    91  		}
    92  	})
    93  }
    94  
    95  func TestRPCZ_RecordManySpans(t *testing.T) {
    96  	t.Run("RPCZ has small capacity", func(t *testing.T) {
    97  		const num = 1000000
    98  		rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: 1})
    99  		recordSpansToRPCZ(num, rpcz)
   100  		readOnlySpans := rpcz.BatchQuery(num)
   101  		require.Len(t, readOnlySpans, 1)
   102  		require.Equal(t, fmt.Sprintf("server-%d", num), readOnlySpans[0].Name)
   103  	})
   104  	t.Run("RPCZ has large capacity", func(t *testing.T) {
   105  		const largeCapacity = 100000
   106  		rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: largeCapacity})
   107  		recordSpansToRPCZ(10*largeCapacity, rpcz)
   108  		readOnlySpans := rpcz.BatchQuery(10 * largeCapacity)
   109  		require.Len(t, readOnlySpans, largeCapacity)
   110  	})
   111  	t.Run("RPCZ is configured with exporter", func(t *testing.T) {
   112  		const maxCapacity = 100000
   113  		exporter := newSliceSpanExporter(maxCapacity)
   114  		rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: maxCapacity + 1, Exporter: exporter})
   115  
   116  		recordSpansToRPCZ(maxCapacity+1, rpcz)
   117  		readOnlySpans := rpcz.BatchQuery(10 * maxCapacity)
   118  
   119  		require.Len(t, readOnlySpans, maxCapacity+1)
   120  		require.Len(t, exporter.spans, maxCapacity, "the rpcz still stores a copy of the exported span")
   121  	})
   122  }
   123  
   124  type sliceSpanExporter struct {
   125  	spans       []ReadOnlySpan
   126  	maxCapacity uint64
   127  }
   128  
   129  func newSliceSpanExporter(maxCapacity uint64) *sliceSpanExporter {
   130  	return &sliceSpanExporter{maxCapacity: maxCapacity}
   131  }
   132  
   133  func (e *sliceSpanExporter) Export(span *ReadOnlySpan) {
   134  	if uint64(len(e.spans)) >= e.maxCapacity {
   135  		log.Info("exporter has been filled, and no more spans can be received")
   136  		return
   137  	}
   138  	e.spans = append(e.spans, *span)
   139  }
   140  
   141  func recordSpansToRPCZ(num int, rpcz *RPCZ) {
   142  	for i := 1; i <= num; i++ {
   143  		s := newSpan(fmt.Sprintf("server-%d", i), SpanID(i), rpcz)
   144  		s.End()
   145  	}
   146  }
   147  
   148  const smallCapacity = 1
   149  
   150  func BenchmarkRPCZSmallCapacity_NewSpanEndZero(b *testing.B) {
   151  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: smallCapacity})
   152  	for i := 0; i < b.N; i++ {
   153  		_, _ = rpcz.NewChild("")
   154  	}
   155  }
   156  
   157  func BenchmarkRPCZSmallCapacity_NewSpanAndEndHalf(b *testing.B) {
   158  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: smallCapacity})
   159  	for i := 0; i < b.N; i++ {
   160  		_, end := rpcz.NewChild("")
   161  		if i%2 == 0 {
   162  			end.End()
   163  		}
   164  	}
   165  }
   166  
   167  func BenchmarkRPCZSmallCapacity_NewSpanAndEndAll(b *testing.B) {
   168  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: smallCapacity})
   169  	for i := 0; i < b.N; i++ {
   170  		_, end := rpcz.NewChild("")
   171  		end.End()
   172  	}
   173  }
   174  
   175  const largeCapacity = 1000000
   176  
   177  func BenchmarkRPCZLargeCapacity_NewSpanEndZero(b *testing.B) {
   178  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: largeCapacity})
   179  	for i := 0; i < largeCapacity; i++ {
   180  		_, _ = rpcz.NewChild("")
   181  	}
   182  }
   183  
   184  func BenchmarkRPCZLargeCapacity_NewSpanAndEndHalf(b *testing.B) {
   185  	rpcz := NewRPCZ(&Config{Fraction: 1.0, Capacity: largeCapacity})
   186  	for i := 0; i < largeCapacity; i++ {
   187  		_, end := rpcz.NewChild("")
   188  		if i%2 == 0 {
   189  			end.End()
   190  		}
   191  	}
   192  }
   193  
   194  func BenchmarkRPCZLargeCapacity_NewSpanAndEndAll(b *testing.B) {
   195  	var rpcz Span = NewRPCZ(&Config{Fraction: 1.0, Capacity: largeCapacity})
   196  	for i := 0; i < largeCapacity; i++ {
   197  		_, end := rpcz.NewChild("")
   198  		end.End()
   199  	}
   200  }