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 }