Record pending duration histogram for a Quic stream if it was pending for whatever reason. And count how many streams has been pended for a QuicSession.
PiperOrigin-RevId: 589188268
diff --git a/quiche/quic/core/quic_connection_stats.h b/quiche/quic/core/quic_connection_stats.h
index 67a3884..d34b1fb 100644
--- a/quiche/quic/core/quic_connection_stats.h
+++ b/quiche/quic/core/quic_connection_stats.h
@@ -250,6 +250,9 @@
std::optional<TlsServerOperationStats> tls_server_select_cert_stats;
std::optional<TlsServerOperationStats> tls_server_compute_signature_stats;
std::optional<TlsServerOperationStats> tls_server_decrypt_ticket_stats;
+
+ // The total number of streams which were pending from some time.
+ size_t num_total_pending_streams = 0;
};
} // namespace quic
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index c59605e..fe48e83 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -194,6 +194,13 @@
// The pending stream should now be in the scope of normal streams.
QUICHE_DCHECK(IsClosedStream(stream_id) || IsOpenStream(stream_id))
<< "Stream " << stream_id << " not created";
+ if (!stream->pending_duration().IsZero()) {
+ QUIC_SERVER_HISTOGRAM_TIMES("QuicStream.PendingDurationUs",
+ stream->pending_duration().ToMicroseconds(),
+ 0, 1000 * 100, 20,
+ "Time a stream has been pending at server.");
+ ++connection()->mutable_stats().num_total_pending_streams;
+ }
pending_stream_map_.erase(stream_id);
if (stop_sending_error_code) {
stream->OnStopSending(*stop_sending_error_code);
diff --git a/quiche/quic/core/quic_session_test.cc b/quiche/quic/core/quic_session_test.cc
index 079db36..7539658 100644
--- a/quiche/quic/core/quic_session_test.cc
+++ b/quiche/quic/core/quic_session_test.cc
@@ -1940,12 +1940,20 @@
QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id));
EXPECT_EQ(0, session_.num_incoming_streams_created());
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
session_.ProcessAllPendingStreams();
// Both bidirectional and read-unidirectional streams are unbuffered.
EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id));
EXPECT_FALSE(
QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id));
EXPECT_EQ(2, session_.num_incoming_streams_created());
+ EXPECT_EQ(1, QuicSessionPeer::GetStream(&session_, stream_id)
+ ->pending_duration()
+ .ToMilliseconds());
+ EXPECT_EQ(1, QuicSessionPeer::GetStream(&session_, bidirectional_stream_id)
+ ->pending_duration()
+ .ToMilliseconds());
+ EXPECT_EQ(2, session_.connection()->GetStats().num_total_pending_streams);
}
TEST_P(QuicSessionTestServer, RstPendingStreams) {
diff --git a/quiche/quic/core/quic_stream.cc b/quiche/quic/core/quic_stream.cc
index 8bdf9d0..0da973d 100644
--- a/quiche/quic/core/quic_stream.cc
+++ b/quiche/quic/core/quic_stream.cc
@@ -126,7 +126,8 @@
kStreamReceiveWindowLimit,
session->flow_controller()->auto_tune_receive_window(),
session->flow_controller()),
- sequencer_(this) {}
+ sequencer_(this),
+ creation_time_(session->GetClock()->ApproximateNow()) {}
void PendingStream::OnDataAvailable() {
// Data should be kept in the sequencer so that
@@ -284,14 +285,15 @@
QuicStream::QuicStream(PendingStream* pending, QuicSession* session,
bool is_static)
- : QuicStream(pending->id_, session, std::move(pending->sequencer_),
- is_static,
- QuicUtils::GetStreamType(pending->id_, session->perspective(),
- /*peer_initiated = */ true,
- session->version()),
- pending->stream_bytes_read_, pending->fin_received_,
- std::move(pending->flow_controller_),
- pending->connection_flow_controller_) {
+ : QuicStream(
+ pending->id_, session, std::move(pending->sequencer_), is_static,
+ QuicUtils::GetStreamType(pending->id_, session->perspective(),
+ /*peer_initiated = */ true,
+ session->version()),
+ pending->stream_bytes_read_, pending->fin_received_,
+ std::move(pending->flow_controller_),
+ pending->connection_flow_controller_,
+ (session->GetClock()->ApproximateNow() - pending->creation_time())) {
QUICHE_DCHECK(session->version().HasIetfQuicFrames());
sequencer_.set_stream(this);
}
@@ -324,14 +326,15 @@
StreamType type)
: QuicStream(id, session, QuicStreamSequencer(this), is_static, type, 0,
false, FlowController(id, session, type),
- session->flow_controller()) {}
+ session->flow_controller(), QuicTime::Delta::Zero()) {}
QuicStream::QuicStream(QuicStreamId id, QuicSession* session,
QuicStreamSequencer sequencer, bool is_static,
StreamType type, uint64_t stream_bytes_read,
bool fin_received,
std::optional<QuicFlowController> flow_controller,
- QuicFlowController* connection_flow_controller)
+ QuicFlowController* connection_flow_controller,
+ QuicTime::Delta pending_duration)
: sequencer_(std::move(sequencer)),
id_(id),
session_(session),
@@ -369,6 +372,7 @@
session->version())
: type),
creation_time_(session->connection()->clock()->ApproximateNow()),
+ pending_duration_(pending_duration),
perspective_(session->perspective()) {
if (type_ == WRITE_UNIDIRECTIONAL) {
fin_received_ = true;
diff --git a/quiche/quic/core/quic_stream.h b/quiche/quic/core/quic_stream.h
index 3633c3b..b7ea9e7 100644
--- a/quiche/quic/core/quic_stream.h
+++ b/quiche/quic/core/quic_stream.h
@@ -101,6 +101,8 @@
// OnDataAvailable().
void StopReading();
+ QuicTime creation_time() const { return creation_time_; }
+
private:
friend class QuicStream;
@@ -133,6 +135,8 @@
QuicStreamSequencer sequencer_;
// The error code received from QuicStopSendingFrame (if any).
std::optional<QuicResetStreamError> stop_sending_error_code_;
+ // The time when this pending stream is created.
+ const QuicTime creation_time_;
};
class QUICHE_EXPORT QuicStream : public QuicStreamSequencer::StreamInterface {
@@ -394,6 +398,8 @@
// level flow control window size.
QuicByteCount CalculateSendWindowSize() const;
+ const QuicTime::Delta pending_duration() const { return pending_duration_; }
+
protected:
// Called when data of [offset, offset + data_length] is buffered in send
// buffer.
@@ -490,7 +496,8 @@
QuicStreamSequencer sequencer, bool is_static, StreamType type,
uint64_t stream_bytes_read, bool fin_received,
std::optional<QuicFlowController> flow_controller,
- QuicFlowController* connection_flow_controller);
+ QuicFlowController* connection_flow_controller,
+ QuicTime::Delta pending_duration);
// Calls MaybeSendBlocked on the stream's flow controller and the connection
// level flow controller. If the stream is flow control blocked by the
@@ -602,6 +609,10 @@
// Creation time of this stream, as reported by the QuicClock.
const QuicTime creation_time_;
+ // The duration when the data for this stream was stored in a PendingStream
+ // before being moved to this QuicStream.
+ const QuicTime::Delta pending_duration_;
+
Perspective perspective_;
};