Timing and automation

Introduction

In the previous section, we detailed the message exchange between both TCP end-points. The ByteBlower server will store this timing information and allow for it to be accessed through the API. We will start with the TCP events and subsequently show how to derive the HTTP events from them. Since TCP clients and TCP servers exchange similar messages, they use the same data structure. At the HTTP layer, this symmetry is not available, we will thus list them in separate sections. As we will hint throughout these specifics, through the delay or even loss in the network either side of the TCP session will step through the transitions in the state diagram at its own pace.

The ByteBlower GUI hides even more complexity and simply returns the TCP flow duration and throughput. The final GUI section will show in detail how these results are generated.

TCP events and timestamps

As mentioned above, TCP is a session-oriented protocol: a connection needs to be established before any data is exchanged between server and client. As mentioned previously, opening and closing a TCP session uses a similar three-way handshake. Because of this similarity, we'll discuss these events first. The next sections detail the timing-values available for an already established TCP session.  Although the next chapter focuses on HTTP, we will reuse some HTTP terminology: the TCP endpoint waiting on incoming connections will be called the [HTTP] server. The other endpoint actively opens the connections, this is the [HTTP] client. These names make this text more readable, of course, TCP is used for far more than HTTP. In the next section, we detail first the general concept of accessing timed events through the API. Next, we'll apply these to opening and closing a TCP connection. The remainder of the section deals with events related to data-transmission.

The TCP session objects are accessible through the HTTP SessionInfo objects found in the API. For both server and client, these are created once their TCP session is established. As we'll explain further in this text, it is common for the HTTP client to be noticeably established earlier than the server. Since a server can serve multiple clients, a text-token is used to recognize a specific client. In brief, the following code-snippets verify and fetch first the TCP session at the client and subsequently retrieve the same session at the other end.

if( [$httpClient HasSession] ) {
set httpClientSessionInfo [ $httpClient \
Http.Session.Info.Get ]
           set httpClientId [ $httpClient ServerClientId.Get ]
           set tcpClientSessionInfo  [ $httpClientSessionInfo \
Tcp.Session.Info.Get ]
           set tcpClientResult [$tcpClientSessionInfo Result.Get ]
           $tcpClientResult Refresh
}
if( [info exists $httpClientId] && \ 
[$httpServer HasSession $httpClientId]) {
           set httpServerSessionInfo [ $httpServer \
Http.Session.Info.Get ]
           set tcpServerSessionInfo  [ $httpServerSessionInfo \
Tcp.Session.Info.Get ]
           set tcpServerResult [$tcpServerSessionInfo Result.Get ]
           $tcpServerResult Refresh
}    

The state diagram shown earlier shows an ideal behaving TCP connection. In practice, there is little guarantee for a message to arrive at its destination. To make matters worse, there is even no guarantee for it to arrive only a single time. In addition, there is a noticeable delay between transmission and reception. This strongly influences the behaviour of a TCP session. The API exports this type of information by instrumenting both the transmission and reception of specific messages. As we will show in the next section, the transmission of the initial TCP message with the SYN-flag is counted, likewise, the receiving endpoint will count its arrival. For timing information, each counter is associated with a timestamp field. Both the counter and timestamp are updated simultaneously. Thus, the moment of the last increment is available in this timestamp field. Initially, the counter has a value of zero. Since no event occurred, the timestamp field has no value, and attempting to read it will throw a domain error. Program-code working with the API is advised to first read out the value of the counter. Only if it is larger than zero, one can also read the corresponding timestamp. These guidelines will become clearer in the next sections, where we discuss the available counters and timestamps.

Establishing TCP connections

For both the HTTP client and server, their first TCP message has the SYN flag enabled. This message is part of the previously described three-way handshake. In general, it contains no payload and has a random sequence number. In short, we'll call it the SYN-message. The client initiates the session by sending a SYN-message to the server. The server waits for the arrival of such a SYN message and will respond with a SYN+ACK message. In this last message in addition to the SYN flag, also the ACK is enabled. From an API point of view, both the server and client have a very similar interface. The transmission of the syn-message is counted in

  • Layer4.Tcp.ResultSnapshot.NumberOfSynSent.Get
  • Layer4.Tcp.ResultSnapshot.Timestamp.SynSent.Get

Of course, this message also needs to arrive at the other end-point. Reception updates the following fields:

  • Layer4.Tcp.ResultSnapshot.NumberOfSynReceived.Get
  • Layer4.Tcp.ResultSnapshot.Timestamp.SynReceived.Get

As noted previously, the server replies with a TCP message with both SYN and ACK flags enabled. This message is fully counted in the counters listed above. Since the HTTP server also acknowledges the SYN-message received from the HTTP client, it obligates the client to move to the established state. As mentioned earlier, this transition occurs only a single time. It is recorded solely in the following timestamp field:

  • Layer4.Tcp.ResultSnapshot.Timestamp.Established.Get

This Established timestamp has a number important of implications. We'll briefly expand on it here. First, under ideal circumstances, the client will receive only a single SYN+ACK. In this case the SynReceived timestamp and the Established timestamp have the same value. Duplicate SYN+ACK messages are recognized by the SynReceived timestamp being larger than the Established time. The difference between timestamps.SynReceived and timestamp. Established tells exactly over which period this duplication occurred. A small measured period might hint at a misbehaving router close to the client, and values larger than the retransmit time hint to lost ACK-messages. In all cases, this measurement is valuable but needs to be interpreted for the test at hand.
Of course, comparing these timestamps is not the sole way to notice duplication: each duplicated SYN message will also increment the NumberOfSynReceived field. A value larger than one is most certainly suspicious. Some care is necessary though, not all received and thus counted SYN-messages are valid. A NAT device along the path might corrupt sequence numbers. Such behaviour would be noted by a failed session that still managed to add up SYN-messages (.. verify..).

Finally a last remark on the Established state. As noted earlier, receiving the ACK from the HTTP client to the server is the last leg of the three-way handshake. The difference between the SynSent timestamp at the client and the Established timestamp of the server gives a minimum estimate of the whole setup time of the TCP session. If multiple SYN-messages were transmitted, then this value will be a significant underestimate. Given the fairly long retransmit time of the initial SYN-message, one would expect such behaviour to also trigger other warnings.

In summary, this section detailed the events related to opening a TCP session. We've listed five API fields to instrument this process. These are available both in the HTTP server and HTTP client. Next, we've attempted to show how to correlate these values with each other to perform a more complex analysis. Most of these items will be familiar in the next section where the closing of a TCP is explained.

Closing TCP connections

Closing a TCP session is similar to opening one. Both use a similar three-way handshake (<.. four-way handshake ..>). The API is thus also very similar to the previous section. Unlike opening a TCP session, a misbehaving termination of a TCP session has little impact on the user experience (..check..). This state is thus not exported. As noted earlier, in most of the cases, the HTTP server ByteBlower will close the connection.

Enabling the FIN flag on a TCP message indicates the end of data. As the methods below show, similar to the previous section the API exports the amount of FIN-messages transmitted and received at each TCP end-point. For each type, the timestamp of the last message can be requested. It is not possible to request when the TCP session enters a closed state (or a timed wait), for the implemented HTTP requests the timestamps of the FIN messages suffice here.

  • Layer4.Tcp.ResultSnapshot.NumberOfFinSent.Get
  • Layer4.Tcp.ResultSnapshot.Timestamp.FinSent.Get
  • Layer4.Tcp.ResultSnapshot.NumberOfFinReceived.Get
  • Layer4.Tcp.ResultSnapshot.Timestamp.FinReceived.Get

For most configured HTTP requests, the HTTP server will close the connection. A duration-based PUT request is the exception here, the HTTP client will initiate closing the TCP connection. In this last case, no response from the HTTP server follows. This action is perfectly valid at the TCP layer, but from the HTTP protocols point of view, this action can be understood as the client abruptly breaking the connection. To keep matters simple, we'll ignore this edge-case for and assume that the HTTP server always sending out a response and subsequently closes the connection.

Established TCP connections

A session operation offers quite a number of measurements. Solely for completeness, we'll mention the timestamps available from the API. Even though not strictly a timestep, we'll briefly expand on the roundtrip measurements.

All result objects in the ByteBlower API are associated with a timestamp. This timestamp is the start of the measurement period. The value is a multiple of the interval duration.

  • Layer4.Tcp.ResultSnapshot::Timestamp.Get

For a TCP session in operation, one is able to fetch the timestamps of the last received and last transmitted packet within the snapshot. For the cumulative snapshot of a finished flow, this will be the very last received packets of the session. The interval updates offer such a sample once every snapshot duration, thus defaulting once every second. When directly comparing this timestamp to the previously listed types, the last packet snapshot will of course be fairly close to the snapshot boundary. Finally, more timestamp values can be obtained by running an RX capture stream in the ByteBlower ports at the TCP end-points. (see..). Nonetheless, this last packet

  • Tx.Timestamp.Last.Get
  • Rx.Timestamp.Last.Get

Directly comparing the timestamps of the HTTP server with the client is not recommended. Especially in high-latency, high-throughput links at any moment a significant number of packets will still be in flight. There is thus little guarantee for the last transmitted packet at the source to be the same as the last received packet at the drain. A better approach is to use the roundtrip time, which we'll describe in the next section.

The TCP stack will measure the roundtrip time continuously. Its calculation is based on RFC <..>. In brief, any TCP stack needs to keep track of unacknowledged packets. Lost segments need to be retransmitted after a brief period. For frames that do manage to be acknowledged, one can calculate the time it took for ACK to arrive. As described further in the RFC, one can't of course use every packet for the roundtrip time calculation. The API exports the results of this calculation through the following methods. Unlike the time tag fields, the current value is returned. In part because of the measurement approach, estimating the average latency would be highly biased and often give an incorrect impression.

  • Layer4.Tcp.ResultSnapshot::RoundTripTime.Minimum.Get
  • Layer4.Tcp.ResultSnapshot::RoundTripTime.Current.Get
  • Layer4.Tcp.ResultSnapshot::RoundTripTime.Maximum.Get
HTTP timestamps

At the HTTP layer, a number of timestamps are available. Since this is a higher-layer protocol,

At the HTTP layer, Layer5.Http.ResultData class exports the measured test results. Both interval and cumulative results can be obtained by accessing the ResultHistory of the session objects. The example below fetches the ResultHistory and refreshes it to the version stored in the ByteBlower server. Both Interval and Cumulative history are accessible in a very similar way, we show both. In addition, both use the same class.  In the example, the latest cumulative snapshot is requested explicitly, the IntervalSnapshots are returned as a list. With the exception of very brief tests, this list will only contain the last snapshots. It is intended to offer a reasonable window to process the snapshots. Finally, to keep the example brief, no such processing is done on either snapshot.

set httpClientResultHistory [ $httpClientSessionInfo Result.History.Get ]
$httpClientResultHistory Refresh
if [$httpClientResultHistory Cumulative.Length.Get  > 0 ]{
       set cumulative [$httpClientResultHistory Cumulative.Length.Get]
}
set intervalList [$httpClientResultHistory Interval.Get]

The HTTP Result data exports the timestamps of the first and last received frames. For the cumulative data, this is measured over the whole flow. The interval snapshot keeps track of the data received within the particular snapshot. These timestamps are calculated based on the source timestamps of the TCP messages explained earlier. Nonetheless, since HTTP is a higher-layer protocol, not all TCP frames directly be forwarded and this relationship is somewhat complicated. For instance, the TCP frames establishing and closing the connection are not counted, this solely exists at the lower layers. To complicate matters even further, TCP frames might be received out of order (e.g. due to packet-loss). Thus as a suggestion, it is not advised to compare packet-for-packet across OSI layers. In the next section, we'll focus on a number of comparisons that can be easily done. As will be noted, the HTTP request type will play a major role here.

Before continuing with the interaction of the HTTP request methods, we'll detail the API methods first. For each endpoint, one is able to request the timestamp of the first and last HTTP packet received or transmitted. Thus a total of eight parameters are measured. As will be shown below, even for very long requests, a number of these will have the exact same value.

  • Layer5.Http.ResultData::Rx.Timestamp.First.Get
  • Layer5.Http.ResultData::Tx.Timestamp.First.Get
  • Layer5.Http.ResultData::Rx.Timestamp.Last.Get
  • Layer5.Http.ResultData::Tx.Timestamp.Last.Get

As mentioned earlier, the HTTP client is always the originator of the HTTP request. A client can either request to receive a large amount of data from the server ("GET") or it can transmit a lot of data to the server ("PUT"). The request type determines in which direction the main amount of data will flow.

Client/ServerRequest type

 HTTP GETHTTP PUT
HTTP Clientbrief requestDATA
HTTP ServerDATAbrief response

The first point of interest is the setup-time. within this article, we'll call this the time necessary between starting the TCP request and the first.

Next: ðŸ“„ API: Hiding the complexity