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_; };