combineSearchResults overlays the incoming search result with the existing result. This is required for the following reason: a trace may be present in multiple blocks, or in partial segments in live traces. The results should reflect elements of all segments.
(existing *tempopb.TraceSearchMetadata, incoming *tempopb.TraceSearchMetadata)
| 229 | // for the following reason: a trace may be present in multiple blocks, or in partial segments |
| 230 | // in live traces. The results should reflect elements of all segments. |
| 231 | func combineSearchResults(existing *tempopb.TraceSearchMetadata, incoming *tempopb.TraceSearchMetadata) { |
| 232 | if existing.TraceID == "" { |
| 233 | existing.TraceID = incoming.TraceID |
| 234 | } |
| 235 | |
| 236 | if existing.RootServiceName == "" { |
| 237 | existing.RootServiceName = incoming.RootServiceName |
| 238 | } |
| 239 | |
| 240 | if existing.RootTraceName == "" { |
| 241 | existing.RootTraceName = incoming.RootTraceName |
| 242 | } |
| 243 | |
| 244 | // Earliest start time. |
| 245 | if existing.StartTimeUnixNano > incoming.StartTimeUnixNano || existing.StartTimeUnixNano == 0 { |
| 246 | existing.StartTimeUnixNano = incoming.StartTimeUnixNano |
| 247 | } |
| 248 | |
| 249 | // Longest duration |
| 250 | if existing.DurationMs < incoming.DurationMs || existing.DurationMs == 0 { |
| 251 | existing.DurationMs = incoming.DurationMs |
| 252 | } |
| 253 | |
| 254 | // Combine service stats |
| 255 | // It's possible to find multiple trace fragments that satisfy a TraceQL result, |
| 256 | // therefore we use max() to merge the ServiceStats. |
| 257 | for service, incomingStats := range incoming.ServiceStats { |
| 258 | existingStats, ok := existing.ServiceStats[service] |
| 259 | if !ok { |
| 260 | existingStats = &tempopb.ServiceStats{} |
| 261 | if existing.ServiceStats == nil { |
| 262 | existing.ServiceStats = make(map[string]*tempopb.ServiceStats) |
| 263 | } |
| 264 | existing.ServiceStats[service] = existingStats |
| 265 | } |
| 266 | existingStats.SpanCount = max(existingStats.SpanCount, incomingStats.SpanCount) |
| 267 | existingStats.ErrorCount = max(existingStats.ErrorCount, incomingStats.ErrorCount) |
| 268 | } |
| 269 | |
| 270 | // make a map of existing Spansets |
| 271 | existingSS := make(map[string]*tempopb.SpanSet) |
| 272 | for _, ss := range existing.SpanSets { |
| 273 | existingSS[spansetID(ss)] = ss |
| 274 | } |
| 275 | |
| 276 | // add any new spansets |
| 277 | for _, ss := range incoming.SpanSets { |
| 278 | id := spansetID(ss) |
| 279 | // if not found just add directly |
| 280 | if _, ok := existingSS[id]; !ok { |
| 281 | existing.SpanSets = append(existing.SpanSets, ss) |
| 282 | continue |
| 283 | } |
| 284 | |
| 285 | // otherwise combine with existing |
| 286 | combineSpansets(existingSS[id], ss) |
| 287 | } |
| 288 |