Skip to content

Commit 4240f82

Browse files
authored
Add latency calculation tests & logs (#103)
Added witness latency calculation tests. Specifically the following scenarios are now being tested: - the request and the response have 8 ms of latency - the request and the response have 0 ms of latency - the request and the response overlap by -1 ms of latency - the request and the response fully overlap each other have 0 ms of latency - the request and the response are in the wrong order have -4 ms of latency - the request and the response timestamps are the zero value have 0 ms of latency - a stream with two interleaved request and response each with 3 ms of latency - two pipelined requests and responses have 0 ms of latency Added 2 validations in the stream processing layer: - if the first or last packet timestamps are the default zero value, override to `time.Now()`. - if the last packet timestamp is before the first packet timestamp, override last packet timestamp to first packet timestamp. ^These are being logged and printed as part of the `PrintWarnings` report at the end of a session. Added 3 validations in the backend collector layer: - if the collector matches 2 requests or 2 responses together (due to HTTP/1.1 pipelining and matching on `ACK`s), log it and leave latency default value of `0.0`. - if the collector sees timestamp zero values, log it and leave latency default value of `0.0` - if the collector sees negative latency calculation, log and report it.
1 parent 9bbd6a6 commit 4240f82

File tree

4 files changed

+272
-89
lines changed

4 files changed

+272
-89
lines changed

apidump/summary.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,17 @@ func (s *Summary) printHostHighlights(top *client_telemetry.PacketCountSummary)
265265
// any packets, capturing packets but failing to parse them, etc.
266266
func (s *Summary) PrintWarnings() {
267267
// Report on recoverable error counts during trace
268-
if pcap.CountNilAssemblerContext > 0 || pcap.CountNilAssemblerContextAfterParse > 0 || pcap.CountBadAssemblerContextType > 0 {
269-
printer.Stderr.Infof("Detected packet assembly context problems during capture: %v empty, %v bad type, %v empty after parse. ",
268+
if pcap.CountNilAssemblerContext > 0 ||
269+
pcap.CountNilAssemblerContextAfterParse > 0 ||
270+
pcap.CountBadAssemblerContextType > 0 ||
271+
pcap.CountZeroValuePacketTimestamp > 0 ||
272+
pcap.CountLastPacketBeforeFirstPacket > 0 {
273+
printer.Stderr.Infof("Detected packet assembly context problems during capture: %v empty, %v bad type, %v empty after parse, %v zero timestamp, %v last before first timestamp. ",
270274
pcap.CountNilAssemblerContext,
271275
pcap.CountBadAssemblerContextType,
272-
pcap.CountNilAssemblerContextAfterParse)
276+
pcap.CountNilAssemblerContextAfterParse,
277+
pcap.CountZeroValuePacketTimestamp,
278+
pcap.CountLastPacketBeforeFirstPacket)
273279
printer.Stderr.Infof("These errors may cause some packets to be missing from the trace.\n")
274280
}
275281

@@ -383,5 +389,4 @@ func DumpPacketCounters(logf func(f string, args ...interface{}), interfaces map
383389
}
384390

385391
logf("==================================================\n")
386-
387392
}

pcap/stream.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ var CountNilAssemblerContextAfterParse uint64
3131
// happen at all.
3232
var CountBadAssemblerContextType uint64
3333

34+
// Number of times we got parsed network traffic where either the firstPacket or lastPacket
35+
// timestamp is the default zero value.
36+
var CountZeroValuePacketTimestamp uint64
37+
38+
// Number of times we got parsed network traffic where the firstPacket timestamp
39+
// is before the lastPacket timestamp.
40+
var CountLastPacketBeforeFirstPacket uint64
41+
3442
// tcpFlow represents a uni-directional flow of TCP segments along with a
3543
// bidirectional ID that identifies the tcpFlow in the opposite direction.
3644
// Writes come from TCP assembler via tcpStream, while reads come from users
@@ -219,11 +227,24 @@ func (f *tcpFlow) reassemblyComplete() {
219227
}
220228

221229
func (f *tcpFlow) toPNT(firstPacketTime time.Time, lastPacketTime time.Time,
222-
c akinet.ParsedNetworkContent) akinet.ParsedNetworkTraffic {
223-
if firstPacketTime.IsZero() {
224-
firstPacketTime = f.clock.Now()
230+
c akinet.ParsedNetworkContent,
231+
) akinet.ParsedNetworkTraffic {
232+
if firstPacketTime.IsZero() || lastPacketTime.IsZero() {
233+
now := f.clock.Now()
234+
printer.V(6).Infof("ParsedNetworkTraffic with zero value packet timestamps. first: %v last: %v now: %v", firstPacketTime, lastPacketTime, now)
235+
atomic.AddUint64(&CountZeroValuePacketTimestamp, 1)
236+
237+
if firstPacketTime.IsZero() {
238+
firstPacketTime = now
239+
}
240+
if lastPacketTime.IsZero() {
241+
lastPacketTime = now
242+
}
225243
}
226-
if lastPacketTime.IsZero() {
244+
if lastPacketTime.Before(firstPacketTime) {
245+
printer.V(6).Infof("ParsedNetworkTraffic with last packet before first packet. first: %v last: %v", firstPacketTime, lastPacketTime)
246+
atomic.AddUint64(&CountLastPacketBeforeFirstPacket, 1)
247+
227248
lastPacketTime = firstPacketTime
228249
}
229250

trace/backend_collector.go

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ type witnessWithInfo struct {
5252
dstIP net.IP // The HTTP server's IP address.
5353
dstPort uint16 // The HTTP server's port number.
5454
observationTime time.Time
55+
finalPacketTime time.Time
5556
id akid.WitnessID
56-
requestEnd time.Time
57-
responseStart time.Time
57+
isRequest bool
5858

5959
// Mutex protecting witness while it is being processed and/or flushed.
6060
witnessMutex sync.Mutex
@@ -89,36 +89,41 @@ func (r *witnessWithInfo) toReport() (*kgxapi.WitnessReport, error) {
8989
}, nil
9090
}
9191

92-
func (w *witnessWithInfo) recordTimestamp(isRequest bool, t akinet.ParsedNetworkTraffic) {
93-
if isRequest {
94-
w.requestEnd = t.FinalPacketTime
95-
} else {
96-
w.responseStart = t.ObservationTime
92+
func (w *witnessWithInfo) computeProcessingLatency(isRequest bool, t akinet.ParsedNetworkTraffic) {
93+
if w.isRequest == isRequest {
94+
if isRequest {
95+
printer.Debugln("Skipping latency calculation. Matched 2 requests together.")
96+
} else {
97+
printer.Debugln("Skipping latency calculation. Matched 2 responses together.")
98+
}
99+
return
97100
}
98101

99-
}
100-
101-
func (w *witnessWithInfo) computeProcessingLatency(isRequest bool, t akinet.ParsedNetworkTraffic) {
102102
// Processing latency is the time from the last packet of the request,
103103
// to the first packet of the response.
104-
requestEnd := w.requestEnd
105-
responseStart := t.ObservationTime
106-
107-
// handle arrival in opposite order
108-
if isRequest {
104+
var requestEnd, responseStart time.Time
105+
if w.isRequest {
106+
requestEnd = w.finalPacketTime
107+
responseStart = t.ObservationTime
108+
} else {
109109
requestEnd = t.FinalPacketTime
110-
responseStart = w.responseStart
110+
responseStart = w.observationTime
111111
}
112112

113113
// Missing data, leave as default value in protobuf
114114
if requestEnd.IsZero() || responseStart.IsZero() {
115+
printer.Debugf("Skipping latency calculation. Zero value timestamps. RequestEnd: %v ResponseStart %v.\n", requestEnd, responseStart)
115116
return
116117
}
117118

119+
latency := float32(responseStart.Sub(requestEnd).Microseconds()) / 1000.0
120+
if latency < 0.0 {
121+
printer.Debugf("Negative latency calculation: %v\n", latency)
122+
}
123+
118124
// HTTPMethodMetadata only for now
119125
if meta := spec_util.HTTPMetaFromMethod(w.witness.Method); meta != nil {
120-
latency := responseStart.Sub(requestEnd)
121-
meta.ProcessingLatency = float32(latency.Microseconds()) / 1000.0
126+
meta.ProcessingLatency = latency
122127
}
123128
}
124129

@@ -251,10 +256,10 @@ func (c *BackendCollector) Process(t akinet.ParsedNetworkTraffic) error {
251256
dstPort: uint16(t.DstPort),
252257
witness: partial.Witness,
253258
observationTime: t.ObservationTime,
259+
finalPacketTime: t.FinalPacketTime,
254260
id: partial.PairKey,
261+
isRequest: isRequest,
255262
}
256-
// Store whichever timestamp brackets the processing interval.
257-
w.recordTimestamp(isRequest, t)
258263
c.pairCache.Store(partial.PairKey, w)
259264
printer.Debugf("Partial witness %v request=%v at %v -- %v\n",
260265
partial.PairKey, isRequest, t.ObservationTime, t.FinalPacketTime)
@@ -299,8 +304,10 @@ func (c *BackendCollector) processTLSHandshake(tls akinet.TLSHandshakeMetadata)
299304
return nil
300305
}
301306

302-
var cloudAPIEnvironmentsPathRE = regexp.MustCompile(`^/environments/[^/]+$`)
303-
var cloudAPIHostnames = sets.NewSet[string]()
307+
var (
308+
cloudAPIEnvironmentsPathRE = regexp.MustCompile(`^/environments/[^/]+$`)
309+
cloudAPIHostnames = sets.NewSet[string]()
310+
)
304311

305312
func init() {
306313
cloudAPIHostnames.Insert("api.getpostman-stage.com")
@@ -317,7 +324,6 @@ func init() {
317324
// XXX This is a stop-gap hack to exclude certain endpoints for Cloud API from
318325
// Repro Mode.
319326
func excludeWitnessFromReproMode(w *pb.Witness) bool {
320-
321327
httpMeta := w.GetMethod().GetMeta().GetHttp()
322328
if httpMeta == nil {
323329
return false

0 commit comments

Comments
 (0)