proxygen
HTTP2PriorityQueueTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
10 #include <list>
11 #include <map>
12 #include <thread>
13 
14 #include <folly/Random.h>
19 
20 using namespace std::placeholders;
21 using namespace testing;
22 using folly::Random;
25 
26 namespace {
27 static char* fakeTxn = (char*)0xface0000;
28 
30  return (proxygen::HTTPTransaction*)(fakeTxn + id);
31 }
32 
34  return (proxygen::HTTPCodec::StreamID)((char*)txn - fakeTxn);
35 }
36 
37 // folly::Random::rand32 is broken because it takes RNG by value
39  if (max == 0) {
40  return 0;
41  }
42 
43  return std::uniform_int_distribution<uint32_t>(0, max - 1)(rng);
44  }
45 
46 }
47 
48 namespace proxygen {
49 
50 using IDList = std::list<std::pair<HTTPCodec::StreamID, uint8_t>>;
51 
52 class QueueTest : public testing::Test {
53  public:
54  explicit QueueTest(HHWheelTimer* timer=nullptr)
55  : q_(WheelTimerInstance(timer)) {
56  }
57 
58  protected:
60  bool pnode=false, uint64_t* depth = nullptr) {
62  q_.addTransaction(
63  id, pri, pnode ? nullptr : makeFakeTxn(id), false, depth);
64  handles_.insert(std::make_pair(id, h));
65  if (!pnode) {
66  signalEgress(id, 1);
67  }
68  }
69 
71  q_.removeTransaction(handles_[id]);
72  }
73 
75  uint64_t* depth = nullptr) {
76  handles_[id] = q_.updatePriority(handles_[id], pri, depth);
77  }
78 
79  void signalEgress(HTTPCodec::StreamID id, bool mark) {
80  if (mark) {
81  q_.signalPendingEgress(handles_[id]);
82  } else {
83  q_.clearPendingEgress(handles_[id]);
84  }
85  }
86 
87  void buildSimpleTree() {
88  addTransaction(1, {0, false, 15});
89  addTransaction(3, {1, false, 3});
90  addTransaction(5, {1, false, 3});
91  addTransaction(7, {1, false, 7});
92  addTransaction(9, {5, false, 7});
93  }
94 
96  HTTPTransaction*, double r) {
97  nodes_.push_back(std::make_pair(id, r * 100));
98  return false;
99  }
100 
101  void dump() {
102  nodes_.clear();
103  q_.iterate(std::bind(&QueueTest::visitNode, this, std::ref(q_), _1, _2, _3),
104  [] { return false; }, true);
105  }
106 
107  void dumpBFS(const std::function<bool()>& stopFn) {
108  nodes_.clear();
109  q_.iterateBFS(std::bind(&QueueTest::visitNode, this, _1, _2, _3, _4),
110  stopFn, true);
111  }
112 
113  void nextEgress(bool spdyMode=false) {
114  HTTP2PriorityQueue::NextEgressResult nextEgressResults;
115  q_.nextEgress(nextEgressResults, spdyMode);
116  nodes_.clear();
117  for (auto p: nextEgressResults) {
118  nodes_.push_back(std::make_pair(getTxnID(p.first), p.second * 100));
119  }
120  }
121 
123  std::map<HTTPCodec::StreamID, HTTP2PriorityQueue::Handle> handles_;
125 };
126 
127 
128 TEST_F(QueueTest, Basic) {
129  buildSimpleTree();
130  dump();
131  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 25}, {5, 25}, {9, 100}, {7, 50}}));
132 
133  // Add another node, make sure we get the correct depth.
134  uint64_t depth;
135  addTransaction(11, {7, false, 15}, false, &depth);
136  EXPECT_EQ(depth, 3);
137 }
138 
139 TEST_F(QueueTest, RemoveLeaf) {
140  buildSimpleTree();
141 
142  removeTransaction(3);
143  dump();
144 
145  EXPECT_EQ(nodes_, IDList({{1, 100}, {5, 33}, {9, 100}, {7, 66}}));
146 }
147 
148 TEST_F(QueueTest, RemoveParent) {
149  buildSimpleTree();
150 
151  removeTransaction(5);
152  dump();
153 
154  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 25}, {7, 50}, {9, 25}}));
155 }
156 
157 TEST_F(QueueTest, RemoveParentWeights) {
158  // weight_ / totalChildWeight_ < 1
159  addTransaction(1, {0, false, 0});
160  addTransaction(3, {1, false, 255});
161  addTransaction(5, {1, false, 255});
162 
163  removeTransaction(1);
164  dump();
165 
166  EXPECT_EQ(nodes_, IDList({{3, 50}, {5, 50}}));
167 }
168 
169 TEST_F(QueueTest, NodeDepth) {
170  uint64_t depth{33}; // initialize to some wrong value
171  addTransaction(1, {0, false, 15}, false, &depth);
172  EXPECT_EQ(depth, 1);
173 
174  addTransaction(3, {1, false, 3}, false, &depth);
175  EXPECT_EQ(depth, 2);
176 
177  addTransaction(5, {3, true, 7}, false, &depth);
178  EXPECT_EQ(depth, 3);
179 
180  addTransaction(9, {1, false, 3}, true, &depth);
181  EXPECT_EQ(depth, 2);
182  EXPECT_EQ(q_.numPendingEgress(), 3);
183  EXPECT_EQ(q_.numVirtualNodes(), 1);
184 
185  depth = 55; // some unlikely depth
186  addTransaction(9, {1, false, 31}, false, &depth);
187  EXPECT_EQ(depth, 2);
188  EXPECT_EQ(q_.numPendingEgress(), 4);
189  EXPECT_EQ(q_.numVirtualNodes(), 0);
190 
191  addTransaction(11, {1, true, 7}, false, &depth);
192  EXPECT_EQ(depth, 2);
193  EXPECT_EQ(q_.numPendingEgress(), 5);
194  EXPECT_EQ(q_.numVirtualNodes(), 0);
195 
196  addTransaction(13, {0, true, 23}, true, &depth);
197  EXPECT_EQ(depth, 1);
198  EXPECT_EQ(q_.numPendingEgress(), 5);
199  EXPECT_EQ(q_.numVirtualNodes(), 1);
200 
201  depth = 77; // some unlikely depth
202  addTransaction(13, {0, true, 33}, false, &depth);
203  EXPECT_EQ(depth, 1);
204  EXPECT_EQ(q_.numPendingEgress(), 6);
205  EXPECT_EQ(q_.numVirtualNodes(), 0);
206 }
207 
208 TEST_F(QueueTest, UpdateWeight) {
209  buildSimpleTree();
210 
211  uint64_t depth = 0;
212  updatePriority(5, {1, false, 7}, &depth);
213  dump();
214 
215  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 20}, {5, 40}, {9, 100}, {7, 40}}));
216  EXPECT_EQ(depth, 2);
217 }
218 
219 // Previously the code would allow duplicate entries in the priority tree under
220 // certain circumstances.
221 TEST_F(QueueTest, DuplicateID) {
222  q_.addOrUpdatePriorityNode(1, {0, false, 15});
223  addTransaction(1, {0, true, 15});
224  q_.addOrUpdatePriorityNode(3, {1, false, 15});
225  addTransaction(5, {3, false, 15});
226  addTransaction(3, {5, false, 15});
227  removeTransaction(5);
228  auto stopFn = [] {
229  return false;
230  };
231 
232  dumpBFS(stopFn);
233  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 100}}));
234 }
235 
236 TEST_F(QueueTest, UpdateWeightNotEnqueued) {
237  addTransaction(1, {0, false, 7});
238  addTransaction(3, {0, false, 7});
239 
240  signalEgress(1, false);
241  signalEgress(3, false);
242  uint64_t depth = 0;
243  updatePriority(1, {3, false, 7}, &depth);
244  dump();
245 
246  EXPECT_EQ(nodes_, IDList({{3, 100}, {1, 100}}));
247  EXPECT_EQ(depth, 2);
248 }
249 
250 TEST_F(QueueTest, UpdateWeightExcl) {
251  buildSimpleTree();
252 
253  updatePriority(5, {1, true, 7});
254  dump();
255 
256  EXPECT_EQ(nodes_, IDList({{1, 100}, {5, 100}, {9, 40}, {3, 20}, {7, 40}}));
257  signalEgress(1, false);
258  nextEgress();
259  EXPECT_EQ(nodes_, IDList({{5, 100}}));
260 }
261 
262 TEST_F(QueueTest, UpdateWeightExclDequeued) {
263  buildSimpleTree();
264 
265  signalEgress(5, false);
266  updatePriority(5, {1, true, 7});
267  signalEgress(1, false);
268  nextEgress();
269 
270  EXPECT_EQ(nodes_, IDList({{9, 40}, {7, 40}, {3, 20}}));
271 }
272 
273 TEST_F(QueueTest, UpdateWeightUnknownParent) {
274  buildSimpleTree();
275 
276  uint64_t depth = 0;
277  updatePriority(5, {97, false, 15}, &depth);
278  dump();
279 
280  EXPECT_EQ(
281  nodes_,
282  IDList({{1, 50}, {3, 33}, {7, 66}, {97, 50}, {5, 100}, {9, 100}})
283  );
284  EXPECT_EQ(depth, 2);
285 
286  depth = 0;
287  updatePriority(9, {99, false, 15}, &depth);
288  dump();
289 
290  EXPECT_EQ(
291  nodes_,
292  IDList({{1, 33}, {3, 33}, {7, 66}, {97, 33}, {5, 100}, {99, 33}, {9, 100}})
293  );
294  EXPECT_EQ(depth, 2);
295 }
296 
297 TEST_F(QueueTest, UpdateParentSibling) {
298  buildSimpleTree();
299 
300  updatePriority(5, {3, false, 3});
301  dump();
302 
303  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 33}, {5, 100},
304  {9, 100}, {7, 66}}));
305  signalEgress(1, false);
306  nextEgress();
307  EXPECT_EQ(nodes_, IDList({{7, 66}, {3, 33}}));
308 
309  // Clear 5's egress (so it is only in the tree because 9 has egress) and move
310  // it back. Hit's a slightly different code path in reparent
311  signalEgress(5, false);
312  updatePriority(5, {1, false, 3});
313  dump();
314 
315  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 25}, {7, 50}, {5, 25}, {9, 100}}));
316 
317  nextEgress();
318  EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {9, 25}}));
319 }
320 
321 TEST_F(QueueTest, UpdateParentSiblingExcl) {
322  buildSimpleTree();
323 
324  updatePriority(7, {5, true, 3});
325  dump();
326 
327  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 50}, {5, 50},
328  {7, 100}, {9, 100}}));
329  signalEgress(1, false);
330  signalEgress(3, false);
331  signalEgress(5, false);
332  nextEgress();
333  EXPECT_EQ(nodes_, IDList({{7, 100}}));
334 }
335 
336 TEST_F(QueueTest, UpdateParentAncestor) {
337  buildSimpleTree();
338 
339  updatePriority(9, {0, false, 15});
340  dump();
341 
342  EXPECT_EQ(nodes_, IDList({{1, 50}, {3, 25}, {5, 25}, {7, 50}, {9, 50}}));
343  nextEgress();
344  EXPECT_EQ(nodes_, IDList({{1, 50}, {9, 50}}));
345 }
346 
347 TEST_F(QueueTest, UpdateParentAncestorExcl) {
348  buildSimpleTree();
349 
350  updatePriority(9, {0, true, 15});
351  dump();
352 
353  EXPECT_EQ(nodes_, IDList({{9, 100}, {1, 100}, {3, 25}, {5, 25}, {7, 50}}));
354  nextEgress();
355  EXPECT_EQ(nodes_, IDList({{9, 100}}));
356 }
357 
358 TEST_F(QueueTest, UpdateParentDescendant) {
359  buildSimpleTree();
360 
361  updatePriority(1, {5, false, 7});
362  dump();
363 
364  EXPECT_EQ(nodes_, IDList({{5, 100}, {9, 50}, {1, 50}, {3, 33}, {7, 66}}));
365  nextEgress();
366  EXPECT_EQ(nodes_, IDList({{5, 100}}));
367  signalEgress(5, false);
368  nextEgress();
369  EXPECT_EQ(nodes_, IDList({{9, 50}, {1, 50}}));
370 }
371 
372 TEST_F(QueueTest, UpdateParentDescendantExcl) {
373  buildSimpleTree();
374 
375  updatePriority(1, {5, true, 7});
376  dump();
377 
378  EXPECT_EQ(nodes_, IDList({{5, 100}, {1, 100}, {3, 20}, {7, 40}, {9, 40}}));
379  nextEgress();
380  EXPECT_EQ(nodes_, IDList({{5, 100}}));
381  signalEgress(5, false);
382  signalEgress(1, false);
383  nextEgress();
384  EXPECT_EQ(nodes_, IDList({{7, 40}, {9, 40}, {3, 20}}));
385 }
386 
387 TEST_F(QueueTest, ExclusiveAdd) {
388  buildSimpleTree();
389 
390  addTransaction(11, {1, true, 100});
391 
392  dump();
393  EXPECT_EQ(nodes_, IDList({
394  {1, 100}, {11, 100}, {3, 25}, {5, 25}, {9, 100}, {7, 50}
395  }));
396 }
397 
398 TEST_F(QueueTest, AddUnknown) {
399  buildSimpleTree();
400 
401  addTransaction(11, {75, false, 15});
402 
403  dump();
404  EXPECT_EQ(nodes_, IDList({
405  {1, 50}, {3, 25}, {5, 25}, {9, 100}, {7, 50}, {75, 50}, {11, 100}
406  }));
407 
408  // Now let's add the missing parent node and check if it was
409  // relocated properly
410  addTransaction(75, {1, false, 7});
411 
412  dump();
413  EXPECT_EQ(nodes_, IDList({
414  {1, 100}, {3, 16}, {5, 16}, {9, 100}, {7, 33}, {75, 33}, {11, 100}
415  }));
416 }
417 
418 TEST_F(QueueTest, AddMax) {
419  addTransaction(1, {0, false, 255});
420 
421  nextEgress();
422  EXPECT_EQ(nodes_, IDList({{1, 100}}));
423 }
424 
426  buildSimpleTree();
427 
428  EXPECT_FALSE(q_.empty());
429  EXPECT_EQ(q_.numPendingEgress(), 5);
430  signalEgress(1, false);
431  EXPECT_EQ(q_.numPendingEgress(), 4);
432  EXPECT_FALSE(q_.empty());
433  removeTransaction(9);
434  removeTransaction(1);
435  dump();
436  EXPECT_EQ(nodes_, IDList({{3, 25}, {5, 25}, {7, 50}}));
437 }
438 
439 TEST_F(QueueTest, IterateBFS) {
440  buildSimpleTree();
441 
442  auto stopFn = [this] {
443  return nodes_.size() > 2;
444  };
445 
446  dumpBFS(stopFn);
447  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 25}, {5, 25}, {7, 50}}));
448 }
449 
450 TEST_F(QueueTest, NextEgress) {
451  buildSimpleTree();
452 
453  nextEgress();
454  EXPECT_EQ(nodes_, IDList({{1, 100}}));
455 
456  addTransaction(11, {7, false, 15});
457  signalEgress(1, false);
458 
459  nextEgress();
460  EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {5, 25}}));
461 
462  signalEgress(5, false);
463  nextEgress();
464  EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {9, 25}}));
465  signalEgress(5, true);
466 
467  signalEgress(3, false);
468  nextEgress();
469  EXPECT_EQ(nodes_, IDList({{7, 66}, {5, 33}}));
470 
471  signalEgress(5, false);
472  nextEgress();
473  EXPECT_EQ(nodes_, IDList({{7, 66}, {9, 33}}));
474 
475  signalEgress(7, false);
476  nextEgress();
477  EXPECT_EQ(nodes_, IDList({{11, 66}, {9, 33}}));
478 
479  signalEgress(9, false);
480  nextEgress();
481  EXPECT_EQ(nodes_, IDList({{11, 100}}));
482 
483  signalEgress(3, true);
484  signalEgress(7, true);
485  signalEgress(9, true);
486  nextEgress();
487  EXPECT_EQ(nodes_, IDList({{7, 50}, {3, 25}, {9, 25}}));
488 }
489 
490 TEST_F(QueueTest, NextEgressExclusiveAdd) {
491  buildSimpleTree();
492 
493  // clear all egress
494  signalEgress(1, false);
495  signalEgress(3, false);
496  signalEgress(5, false);
497  signalEgress(7, false);
498  signalEgress(9, false);
499 
500  // Add a transaction with exclusive dependency, clear its egress
501  addTransaction(11, {1, true, 100});
502  signalEgress(11, false);
503 
504  // signal egress for a child that got moved via exclusive dep
505  signalEgress(3, true);
506  nextEgress();
507  EXPECT_EQ(nodes_, IDList({{3, 100}}));
508  EXPECT_EQ(q_.numPendingEgress(), 1);
509 }
510 
511 TEST_F(QueueTest, NextEgressExclusiveAddWithEgress) {
512  buildSimpleTree();
513 
514  // clear all egress, except 3
515  signalEgress(1, false);
516  signalEgress(5, false);
517  signalEgress(7, false);
518  signalEgress(9, false);
519 
520  // Add a transaction with exclusive dependency, clear its egress
521  addTransaction(11, {1, true, 100});
522  signalEgress(11, false);
523  nextEgress();
524  EXPECT_EQ(nodes_, IDList({{3, 100}}));
525  EXPECT_EQ(q_.numPendingEgress(), 1);
526 }
527 
528 TEST_F(QueueTest, UpdatePriorityReparentSubtree) {
529  buildSimpleTree();
530 
531  // clear all egress, except 9
532  signalEgress(1, false);
533  signalEgress(3, false);
534  signalEgress(5, false);
535  signalEgress(7, false);
536 
537  // Update priority of non-enqueued but in egress tree node
538  updatePriority(5, {1, false, 14}, nullptr);
539 
540  // update 9's weight and reparent
541  updatePriority(9, {3, false, 14}, nullptr);
542 
543  nextEgress();
544  EXPECT_EQ(nodes_, IDList({{9, 100}}));
545 }
546 
547 TEST_F(QueueTest, NextEgressRemoveParent) {
548  buildSimpleTree();
549 
550  // Clear egress for all except txn=9
551  signalEgress(1, false);
552  signalEgress(3, false);
553  signalEgress(5, false);
554  signalEgress(7, false);
555 
556  // Remove parent of 9 (5)
557  removeTransaction(5);
558  nextEgress();
559  EXPECT_EQ(nodes_, IDList({{9, 100}}));
560 
561  // signal egress for 9's new siblings to verify weights
562  signalEgress(3, true);
563  signalEgress(7, true);
564 
565  nextEgress();
566  EXPECT_EQ(nodes_, IDList({{7, 50}, {9, 25}, {3, 25}}));
567 }
568 
569 TEST_F(QueueTest, AddExclusiveDescendantEnqueued) {
570  addTransaction(1, {0, false, 100});
571  addTransaction(3, {1, false, 100});
572  addTransaction(5, {3, false, 100});
573  signalEgress(1, false);
574  signalEgress(3, false);
575  // add a new exclusive child of 1. 1's child 3 is not enqueued but is in the
576  // the egress tree.
577  addTransaction(7, {1, true, 100});
578  nextEgress();
579  EXPECT_EQ(nodes_, IDList({{7, 100}}));
580 }
581 
582 TEST_F(QueueTest, NextEgressRemoveParentEnqueued) {
583  addTransaction(1, {0, false, 100});
584  addTransaction(3, {1, false, 100});
585  addTransaction(5, {3, false, 100});
586  signalEgress(3, false);
587  // When 3's children (5) are added to 1, both are already in the egress tree
588  // and the signal does not need to propagate
589  removeTransaction(3);
590  signalEgress(1, false);
591  nextEgress();
592  EXPECT_EQ(nodes_, IDList({{5, 100}}));
593 }
594 
595 TEST_F(QueueTest, NextEgressRemoveParentEnqueuedIndirect) {
596  addTransaction(1, {0, false, 100});
597  addTransaction(3, {1, false, 100});
598  addTransaction(5, {3, false, 100});
599  addTransaction(7, {1, false, 100});
600  signalEgress(3, false);
601  signalEgress(1, false);
602  // When 3's children (5) are added to 1, both are already in the egress tree
603  // and the signal does not need to propagate
604  removeTransaction(3);
605  nextEgress();
606  EXPECT_EQ(nodes_, IDList({{7, 50}, {5, 50}}));
607 }
608 
609 TEST_F(QueueTest, ChromeTest) {
610  // Tries to simulate Chrome's current behavior by performing pseudo-random
611  // add-exclusive, signal, clear and remove with 3 insertion points
612  // (hi,mid,low). Note the test uses rand32() with a particular seed so the
613  // output is predictable.
614  HTTPCodec::StreamID pris[3] = {1, 3, 5};
615  addTransaction(1, {0, true, 99});
616  signalEgress(1, false);
617  addTransaction(3, {1, true, 99});
618  signalEgress(3, false);
619  addTransaction(5, {3, true, 99});
620  signalEgress(5, false);
621 
622  std::vector<HTTPCodec::StreamID> txns;
623  std::vector<HTTPCodec::StreamID> active;
624  std::vector<HTTPCodec::StreamID> inactive;
625  HTTPCodec::StreamID txn = 0;
626  uint64_t idx = 0;
627  HTTPCodec::StreamID nextId = 7;
628  auto gen = Random::create();
629  gen.seed(12345); // luggage combo
630  for (auto i = 4; i < 1000; i++) {
631  uint8_t action = rand32(4, gen);
632  if (action == 0) {
633  // add exclusive on pseudo-random priority anchor
634  uint8_t pri = rand32(3, gen);
635  HTTPCodec::StreamID dep = pris[pri];
636  txn = nextId;
637  nextId += 2;
638  VLOG(2) << "Adding txn=" << txn << " with dep=" << dep;
639  addTransaction(txn, {(uint32_t)dep, true, 99});
640  txns.push_back(txn);
641  active.push_back(txn);
642  } else if (action == 1 && !inactive.empty()) {
643  // signal an inactive txn
644  idx = rand32(inactive.size(), gen);
645  txn = inactive[idx];
646  VLOG(2) << "Activating txn=" << txn;
647  signalEgress(txn, true);
648  inactive.erase(inactive.begin() + idx);
649  active.push_back(txn);
650  } else if (action == 2 && !active.empty()) {
651  // clear an active transaction
652  idx = rand32(active.size(), gen);
653  txn = active[idx];
654  VLOG(2) << "Deactivating txn=" << txn;
655  signalEgress(txn, false);
656  active.erase(active.begin() + idx);
657  inactive.push_back(txn);
658  } else if (action == 3 && !txns.empty()) {
659  // remove a transaction
660  idx = rand32(txns.size(), gen);
661  txn = txns[idx];
662  VLOG(2) << "Removing txn=" << txn;
663  removeTransaction(txn);
664  txns.erase(txns.begin() + idx);
665  auto it = std::find(active.begin(), active.end(), txn);
666  if (it != active.end()) {
667  active.erase(it);
668  }
669  it = std::find(inactive.begin(), inactive.end(), txn);
670  if (it != inactive.end()) {
671  inactive.erase(it);
672  }
673  }
674  VLOG(2) << "Active nodes=" << q_.numPendingEgress();
675  if (!q_.empty()) {
676  nextEgress();
677  EXPECT_GT(nodes_.size(), 0);
678  }
679 
680  }
681 }
682 
683 TEST_F(QueueTest, NextEgressSpdy) {
684  // 1 and 3 are vnodes representing pri 0 and 1
685  addTransaction(1, {0, false, 0}, true);
686  addTransaction(3, {1, false, 0}, true);
687 
688  // 7 and 9 are pri 0, 11 and 13 are pri 1
689  addTransaction(7, {1, false, 15});
690  addTransaction(9, {1, false, 15});
691  addTransaction(11, {3, false, 15});
692  addTransaction(13, {3, false, 15});
693 
694  nextEgress(true);
695  EXPECT_EQ(nodes_, IDList({{7, 50}, {9, 50}}));
696 
697  signalEgress(7, false);
698  nextEgress(true);
699  EXPECT_EQ(nodes_, IDList({{9, 100}}));
700 
701  signalEgress(9, false);
702  nextEgress(true);
703  EXPECT_EQ(nodes_, IDList({{11, 50}, {13, 50}}));
704 }
705 
706 TEST_F(QueueTest, AddOrUpdate) {
707  q_.addOrUpdatePriorityNode(1, {0, false, 15});
708  q_.addOrUpdatePriorityNode(3, {0, false, 15});
709  dump();
710  EXPECT_EQ(nodes_, IDList({{1, 50}, {3, 50}}));
711  q_.addOrUpdatePriorityNode(1, {0, false, 3});
712  dump();
713  EXPECT_EQ(nodes_, IDList({{1, 20}, {3, 80}}));
714 }
715 
717  public:
719  // Just under two ticks
720  HTTP2PriorityQueue::setNodeLifetime(
721  std::chrono::milliseconds(2 * HHWheelTimer::DEFAULT_TICK_INTERVAL - 1));
722  EXPECT_CALL(timeoutManager_, scheduleTimeout(_, _))
723  .WillRepeatedly(Invoke([this] (folly::AsyncTimeout* timeout,
724  std::chrono::milliseconds) {
725  timeouts_.push_back(timeout);
726  return true;
727  }));
728  }
729 
730  protected:
731  void expireNodes() {
732  std::this_thread::sleep_for(
733  std::chrono::milliseconds(2 * HHWheelTimer::DEFAULT_TICK_INTERVAL));
734  // Node lifetime it just under two ticks, so firing twice expires all nodes
735  tick();
736  tick();
737  }
738 
739  void tick() {
740  std::list<folly::AsyncTimeout*> timeouts;
741  std::swap(timeouts_, timeouts);
742  for (auto timeout: timeouts) {
743  timeout->timeoutExpired();
744  }
745  }
746 
747  std::list<folly::AsyncTimeout*> timeouts_;
750 };
751 
752 // Order declaration of the base classes for this fixture matters here: we want
753 // to pass the timer initialized as part of DanglingQueueTest into to QueueTest,
754 // so it must be initialized first.
756  public:
759  QueueTest(&timer_) {
760  }
761 };
762 
764  addTransaction(1, {0, false, 15});
765  removeTransaction(1);
766  dump();
767  EXPECT_EQ(nodes_, IDList({{1, 100}}));
768  expireNodes();
769  dump();
770  EXPECT_EQ(nodes_, IDList({}));
771 }
772 
774  addTransaction(1, {0, false, 15}, true);
775  addTransaction(3, {1, false, 15}, true);
776  addTransaction(5, {3, false, 15}, true);
777  dump();
778  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 100}, {5, 100}}));
779  expireNodes();
780  dump();
781  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 100}}));
782  expireNodes();
783  dump();
784  EXPECT_EQ(nodes_, IDList({{1, 100}}));
785  expireNodes();
786  dump();
787  EXPECT_EQ(nodes_, IDList({}));
788 }
789 
791  addTransaction(1, {0, false, 15}, true);
792  addTransaction(3, {0, false, 15}, true);
793  addTransaction(5, {1, false, 15}, true);
794  dump();
795  q_.dropPriorityNodes();
796  dump();
797  EXPECT_EQ(nodes_, IDList({}));
798 }
799 
800 TEST_F(DanglingQueueTest, ExpireParentOfMismatchedTwins) {
801  addTransaction(1, {0, true, 219}, false);
802  addTransaction(3, {1, false, 146}, false);
803  addTransaction(5, {1, false, 146}, false);
804  signalEgress(3, false);
805  signalEgress(5, true);
806  removeTransaction(1);
807  dump();
808  tick();
809  expireNodes();
810  dump();
811  EXPECT_EQ(nodes_, IDList({{3, 50}, {5, 50}}));
812 }
813 
814 
815 class DummyTimeout: public HHWheelTimer::Callback {
816  void timeoutExpired() noexcept override {
817  }
818 };
819 
821  // Having a long running timeout prevents HHWheelTimer::Callback::setScheduled
822  // from checking the real time
823  DummyTimeout t;
824  timer_.scheduleTimeout(&t, std::chrono::seconds(300));
825  addTransaction(1, {0, false, 15});
826  addTransaction(3, {0, false, 15});
827  // 1 is now virtual
828  removeTransaction(1);
829  dump();
830  EXPECT_EQ(nodes_, IDList({{1, 50}, {3, 50}}));
831  tick();
832  // before 1 times out, change it's priority, should still be there
833  updatePriority(1, {0, false, 3});
834  dump();
835  EXPECT_EQ(nodes_, IDList({{1, 20}, {3, 80}}));
836 
837  tick();
838  dump();
839  EXPECT_EQ(nodes_, IDList({{1, 20}, {3, 80}}));
840  expireNodes();
841  dump();
842  EXPECT_EQ(nodes_, IDList({{3, 100}}));
843 }
844 
846  buildSimpleTree();
847  q_.setMaxVirtualNodes(3);
848  for (auto i = 1; i <= 9; i += 2) {
849  removeTransaction(i);
850  }
851  dump();
852  EXPECT_EQ(nodes_, IDList({{1, 100}, {3, 50}, {5, 50}}));
853  // 1 expires first and it re-weights 3 and 5, which extends their lifetime
854  expireNodes();
855  dump();
856  EXPECT_EQ(nodes_, IDList({{3, 50}, {5, 50}}));
857  expireNodes();
858  dump();
859  EXPECT_EQ(nodes_, IDList());
860 }
861 
862 TEST_F(QueueTest, Rebuild) {
863  buildSimpleTree();
864  q_.rebuildTree();
865  dump();
866  EXPECT_EQ(nodes_, IDList({{3, 20}, {9, 20}, {5, 20}, {7, 20}, {1, 20}}));
867 }
868 
869 }
std::list< std::pair< HTTPCodec::StreamID, uint8_t >> IDList
*than *hazptr_holder h
Definition: Hazptr.h:116
std::list< folly::AsyncTimeout * > timeouts_
QueueTest(HHWheelTimer *timer=nullptr)
bool visitNode(HTTP2PriorityQueue &, HTTPCodec::StreamID id, HTTPTransaction *, double r)
LogLevel max
Definition: LogLevel.cpp:31
std::vector< std::pair< HTTPTransaction *, double >> NextEgressResult
void timeoutExpired() noexceptoverride
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
requires E e noexcept(noexcept(s.error(std::move(e))))
stop_watch< std::chrono::milliseconds > timer_
auto rng
Definition: CollectTest.cpp:31
void updatePriority(HTTPCodec::StreamID id, http2::PriorityUpdate pri, uint64_t *depth=nullptr)
PolymorphicAction< internal::InvokeAction< FunctionImpl > > Invoke(FunctionImpl function_impl)
void nextEgress(bool spdyMode=false)
std::array< uint8_t, 32 > Random
Definition: Types.h:184
void addTransaction(HTTPCodec::StreamID id, http2::PriorityUpdate pri, bool pnode=false, uint64_t *depth=nullptr)
void dumpBFS(const std::function< bool()> &stopFn)
std::mt19937 DefaultGenerator
Definition: Random.h:97
int bind(NetworkSocket s, const sockaddr *name, socklen_t namelen)
Definition: NetOps.cpp:76
#define EXPECT_CALL(obj, call)
uint64_t StreamID
Definition: HTTPCodec.h:49
const internal::AnythingMatcher _
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
void signalEgress(HTTPCodec::StreamID id, bool mark)
testing::NiceMock< MockTimeoutManager > timeoutManager_
std::map< HTTPCodec::StreamID, HTTP2PriorityQueue::Handle > handles_
void removeTransaction(HTTPCodec::StreamID id)
TEST_F(QueueTest, Rebuild)
#define EXPECT_GT(val1, val2)
Definition: gtest.h:1934
action
Definition: upload.py:393