Add minimal spdy priority set by gws response header for initial cwnd bootstrapping for http2. also store spdy priority within spdydispatcher::sessiondata. no production behavior change unless we start an additional gws experiment. protected by gfe2_restart_flag_h2_adjust_initial_cwnd_by_gws.
While QUIC stores SPDY within spdy::PriorityWriteScheduler<...>, SPDY priority is stored in a map from stream_id to SessionData owned by SpdyClientDispatcher in this change because
(1) H2 uses LIFOWriteScheduler<...> by default, which ignores precedence (http://shortn/_2Ny3X9xBh6).
(2) We only need CWND bootstrapping for traffic from client, and hence only SpdyClientDispatcher populates the SPDY priority.
PiperOrigin-RevId: 316117083
Change-Id: I232e421b16edecc5f7146ce4fb36592a71a7e86f
diff --git a/spdy/core/fifo_write_scheduler.h b/spdy/core/fifo_write_scheduler.h
index a2c6989..7b31891 100644
--- a/spdy/core/fifo_write_scheduler.h
+++ b/spdy/core/fifo_write_scheduler.h
@@ -29,6 +29,8 @@
const StreamPrecedenceType& precedence) override;
void UnregisterStream(StreamIdType stream_id) override;
bool StreamRegistered(StreamIdType stream_id) const override;
+ // Stream precedence is available but note that it is not used for scheduling
+ // in this scheduler.
StreamPrecedenceType GetStreamPrecedence(
StreamIdType stream_id) const override;
void UpdateStreamPrecedence(StreamIdType stream_id,
@@ -51,20 +53,26 @@
std::string DebugString() const override;
private:
+ struct StreamInfo {
+ SpdyPriority priority;
+ int64_t event_time; // read/write event time (us since Unix epoch).
+ };
+
std::set<StreamIdType> ready_streams_;
- // This map maps stream ID to read/write event time (us since Unix epoch).
- std::map<StreamIdType, int64_t> registered_streams_;
+ std::map<StreamIdType, StreamInfo> registered_streams_;
};
template <typename StreamIdType>
void FifoWriteScheduler<StreamIdType>::RegisterStream(
StreamIdType stream_id,
- const StreamPrecedenceType& /*precedence*/) {
+ const StreamPrecedenceType& precedence) {
if (StreamRegistered(stream_id)) {
SPDY_BUG << "Stream " << stream_id << " already registered";
return;
}
- registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0);
+ registered_streams_.emplace_hint(
+ registered_streams_.end(), stream_id,
+ StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0});
}
template <typename StreamIdType>
@@ -88,14 +96,26 @@
template <typename StreamIdType>
typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType
FifoWriteScheduler<StreamIdType>::GetStreamPrecedence(
- StreamIdType /*stream_id*/) const {
- return StreamPrecedenceType(kV3LowestPriority);
+ StreamIdType stream_id) const {
+ auto it = registered_streams_.find(stream_id);
+ if (it == registered_streams_.end()) {
+ SPDY_DVLOG(1) << "Stream " << stream_id << " not registered";
+ return StreamPrecedenceType(kV3LowestPriority);
+ }
+ return StreamPrecedenceType(it->second.priority);
}
template <typename StreamIdType>
void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence(
- StreamIdType /*stream_id*/,
- const StreamPrecedenceType& /*precedence*/) {}
+ StreamIdType stream_id,
+ const StreamPrecedenceType& precedence) {
+ auto it = registered_streams_.find(stream_id);
+ if (it == registered_streams_.end()) {
+ SPDY_DVLOG(1) << "Stream " << stream_id << " not registered";
+ return;
+ }
+ it->second.priority = precedence.spdy3_priority();
+}
template <typename StreamIdType>
std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren(
@@ -109,7 +129,7 @@
int64_t now_in_usec) {
auto it = registered_streams_.find(stream_id);
if (it != registered_streams_.end()) {
- it->second = now_in_usec;
+ it->second.event_time = now_in_usec;
} else {
SPDY_BUG << "Stream " << stream_id << " is not registered";
}
@@ -128,7 +148,8 @@
if (stream_id <= it->first) {
break;
}
- latest_event_time_us = std::max(latest_event_time_us, it->second);
+ latest_event_time_us =
+ std::max(latest_event_time_us, it->second.event_time);
}
return latest_event_time_us;
}
diff --git a/spdy/core/fifo_write_scheduler_test.cc b/spdy/core/fifo_write_scheduler_test.cc
index 950b641..d1dd066 100644
--- a/spdy/core/fifo_write_scheduler_test.cc
+++ b/spdy/core/fifo_write_scheduler_test.cc
@@ -80,6 +80,24 @@
EXPECT_EQ(0, fifo.GetLatestEventWithPrecedence(1));
}
+TEST(FifoWriteSchedulerTest, GetStreamPrecedence) {
+ FifoWriteScheduler<SpdyStreamId> fifo;
+ // Return lowest priority for unknown stream.
+ EXPECT_EQ(kV3LowestPriority, fifo.GetStreamPrecedence(1).spdy3_priority());
+
+ fifo.RegisterStream(1, SpdyStreamPrecedence(3));
+ EXPECT_TRUE(fifo.GetStreamPrecedence(1).is_spdy3_priority());
+ EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority());
+
+ // Redundant registration shouldn't change stream priority.
+ EXPECT_SPDY_BUG(fifo.RegisterStream(1, SpdyStreamPrecedence(4)),
+ "Stream 1 already registered");
+ EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority());
+
+ fifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5));
+ EXPECT_EQ(5, fifo.GetStreamPrecedence(1).spdy3_priority());
+}
+
} // namespace test
} // namespace spdy
diff --git a/spdy/core/lifo_write_scheduler.h b/spdy/core/lifo_write_scheduler.h
index 405ccf5..d2e7fcf 100644
--- a/spdy/core/lifo_write_scheduler.h
+++ b/spdy/core/lifo_write_scheduler.h
@@ -5,6 +5,7 @@
#ifndef QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_
#define QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_
+#include <cstdint>
#include <map>
#include <set>
#include <string>
@@ -40,15 +41,13 @@
return registered_streams_.find(stream_id) != registered_streams_.end();
}
- // Stream precedence is not supported by this scheduler.
+ // Stream precedence is available but note that it is not used for scheduling
+ // in this scheduler.
StreamPrecedenceType GetStreamPrecedence(
- StreamIdType /*stream_id*/) const override {
- return StreamPrecedenceType(kV3LowestPriority);
- }
+ StreamIdType stream_id) const override;
- void UpdateStreamPrecedence(
- StreamIdType /*stream_id*/,
- const StreamPrecedenceType& /*precedence*/) override {}
+ void UpdateStreamPrecedence(StreamIdType stream_id,
+ const StreamPrecedenceType& precedence) override;
std::vector<StreamIdType> GetStreamChildren(
StreamIdType /*stream_id*/) const override {
@@ -85,19 +84,26 @@
private:
friend class test::LifoWriteSchedulerPeer<StreamIdType>;
+ struct StreamInfo {
+ SpdyPriority priority;
+ int64_t event_time; // read/write event time (us since Unix epoch).
+ };
+
std::set<StreamIdType> ready_streams_;
- std::map<StreamIdType, int64_t> registered_streams_;
+ std::map<StreamIdType, StreamInfo> registered_streams_;
};
template <typename StreamIdType>
void LifoWriteScheduler<StreamIdType>::RegisterStream(
StreamIdType stream_id,
- const StreamPrecedenceType& /*precedence*/) {
+ const StreamPrecedenceType& precedence) {
if (StreamRegistered(stream_id)) {
SPDY_BUG << "Stream " << stream_id << " already registered";
return;
}
- registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0);
+ registered_streams_.emplace_hint(
+ registered_streams_.end(), stream_id,
+ StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0});
}
template <typename StreamIdType>
@@ -112,12 +118,36 @@
}
template <typename StreamIdType>
+typename LifoWriteScheduler<StreamIdType>::StreamPrecedenceType
+LifoWriteScheduler<StreamIdType>::GetStreamPrecedence(
+ StreamIdType stream_id) const {
+ auto it = registered_streams_.find(stream_id);
+ if (it == registered_streams_.end()) {
+ SPDY_DVLOG(1) << "Stream " << stream_id << " not registered";
+ return StreamPrecedenceType(kV3LowestPriority);
+ }
+ return StreamPrecedenceType(it->second.priority);
+}
+
+template <typename StreamIdType>
+void LifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence(
+ StreamIdType stream_id,
+ const StreamPrecedenceType& precedence) {
+ auto it = registered_streams_.find(stream_id);
+ if (it == registered_streams_.end()) {
+ SPDY_DVLOG(1) << "Stream " << stream_id << " not registered";
+ return;
+ }
+ it->second.priority = precedence.spdy3_priority();
+}
+
+template <typename StreamIdType>
void LifoWriteScheduler<StreamIdType>::RecordStreamEventTime(
StreamIdType stream_id,
int64_t now_in_usec) {
auto it = registered_streams_.find(stream_id);
if (it != registered_streams_.end()) {
- it->second = now_in_usec;
+ it->second.event_time = now_in_usec;
} else {
SPDY_BUG << "Stream " << stream_id << " is not registered";
}
@@ -134,8 +164,8 @@
for (auto it = registered_streams_.rbegin(); it != registered_streams_.rend();
++it) {
if (stream_id < it->first) {
- if (it->second > latest_event_time_us) {
- latest_event_time_us = it->second;
+ if (it->second.event_time > latest_event_time_us) {
+ latest_event_time_us = it->second.event_time;
}
} else {
break;
diff --git a/spdy/core/lifo_write_scheduler_test.cc b/spdy/core/lifo_write_scheduler_test.cc
index 1d7ecbf..744ee13 100644
--- a/spdy/core/lifo_write_scheduler_test.cc
+++ b/spdy/core/lifo_write_scheduler_test.cc
@@ -151,6 +151,24 @@
"Stream 11 is not registered");
}
+TEST(LifoWriteSchedulerTest, GetStreamPrecedence) {
+ LifoWriteScheduler<SpdyStreamId> lifo;
+ // Return lowest priority for unknown stream.
+ EXPECT_EQ(kV3LowestPriority, lifo.GetStreamPrecedence(1).spdy3_priority());
+
+ lifo.RegisterStream(1, SpdyStreamPrecedence(3));
+ EXPECT_TRUE(lifo.GetStreamPrecedence(1).is_spdy3_priority());
+ EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority());
+
+ // Redundant registration shouldn't change stream priority.
+ EXPECT_SPDY_BUG(lifo.RegisterStream(1, SpdyStreamPrecedence(4)),
+ "Stream 1 already registered");
+ EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority());
+
+ lifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5));
+ EXPECT_EQ(5, lifo.GetStreamPrecedence(1).spdy3_priority());
+}
+
} // namespace test
} // namespace spdy