Skip to content

Commit 5393563

Browse files
committed
http2: fix RFC 9218 write scheduler not being idempotent
Previously, the RFC 9218 write scheduler had a bug where AdjustStream() did not update the stream's metadata after adjusting its priority. This results in the function not being idempotent, where repeated calls to it for the same stream can instead remove an unrelated stream from our scheduler, and duplicate the stream whose priority is being updated. For go/golang#75500 Change-Id: Iaf3dd819d02839bc6cff65027c4916f9f2fa3e5b Reviewed-on: https://go-review.googlesource.com/c/net/+/709477 Reviewed-by: Nicholas Husin <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent b2ab371 commit 5393563

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

http2/writesched_priority_rfc9128.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ func (ws *priorityWriteSchedulerRFC9218) AdjustStream(streamID uint32, priority
124124
q.prev.next = q
125125
q.next.prev = q
126126
}
127+
128+
// Update the metadata.
129+
ws.streams[streamID] = streamMetadata{
130+
location: q,
131+
priority: priority,
132+
}
127133
}
128134

129135
func (ws *priorityWriteSchedulerRFC9218) Push(wr FrameWriteRequest) {

http2/writesched_priority_rfc9128_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,92 @@ func TestPrioritySchedulerUrgencyAndIncremental(t *testing.T) {
235235
t.Fatalf("popped streams %v, want %v", got, want)
236236
}
237237
}
238+
239+
func TestPrioritySchedulerIdempotentUpdate(t *testing.T) {
240+
const maxFrameSize = 16
241+
sc := &serverConn{maxFrameSize: maxFrameSize}
242+
ws := newPriorityWriteSchedulerRFC9128()
243+
streams := make([]*stream, 6)
244+
for i := range streams {
245+
streamID := uint32(i) + 1
246+
streams[i] = &stream{
247+
id: streamID,
248+
sc: sc,
249+
}
250+
streams[i].flow.add(1 << 20) // arbitrary large value
251+
ws.OpenStream(streamID, OpenStreamOptions{
252+
priority: PriorityParam{
253+
urgency: 7,
254+
incremental: 0,
255+
},
256+
})
257+
wr := FrameWriteRequest{
258+
write: &writeData{
259+
streamID: streamID,
260+
p: make([]byte, maxFrameSize*(i+1)),
261+
endStream: false,
262+
},
263+
stream: streams[i],
264+
}
265+
ws.Push(wr)
266+
}
267+
// Make even-numbered streams incremental and of higher urgency.
268+
for i := range streams {
269+
streamID := uint32(i) + 1
270+
if streamID%2 == 1 {
271+
continue
272+
}
273+
ws.AdjustStream(streamID, PriorityParam{
274+
urgency: 0,
275+
incremental: 1,
276+
})
277+
}
278+
ws.CloseStream(1)
279+
// Repeat the same priority update to ensure idempotency.
280+
for i := range streams {
281+
streamID := uint32(i) + 1
282+
if streamID%2 == 1 {
283+
continue
284+
}
285+
ws.AdjustStream(streamID, PriorityParam{
286+
urgency: 0,
287+
incremental: 1,
288+
})
289+
}
290+
ws.CloseStream(2)
291+
const controlFrames = 2
292+
for range controlFrames {
293+
ws.Push(makeWriteNonStreamRequest())
294+
}
295+
296+
// We should get the control frames first.
297+
for range controlFrames {
298+
wr, ok := ws.Pop()
299+
if !ok || wr.StreamID() != 0 {
300+
t.Fatalf("wr.Pop() = stream %v, %v; want 0, true", wr.StreamID(), ok)
301+
}
302+
}
303+
304+
// Each stream should write maxFrameSize bytes until it runs out of data.
305+
// We should:
306+
// - Get even-numbered streams first that are written in a round-robin
307+
// manner as they have higher urgency and are incremental.
308+
// - Get odd-numbered streams after that are written one-by-one to
309+
// completion as they are of lower urgency and are not incremental.
310+
// - Skip stream 1 and 4 that have been closed.
311+
want := []uint32{4, 6, 4, 6, 4, 6, 4, 6, 6, 6, 3, 3, 3, 5, 5, 5, 5, 5}
312+
var got []uint32
313+
for {
314+
wr, ok := ws.Pop()
315+
if !ok {
316+
break
317+
}
318+
if wr.DataSize() != maxFrameSize {
319+
t.Fatalf("wr.Pop() = %v data bytes, want %v", wr.DataSize(), maxFrameSize)
320+
}
321+
got = append(got, wr.StreamID())
322+
}
323+
if !reflect.DeepEqual(got, want) {
324+
t.Fatalf("popped streams %v, want %v", got, want)
325+
}
326+
}

0 commit comments

Comments
 (0)