ЗАМЕР
bench/stretched-cache/runs/relaycheck.txt
Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.
- Цитируется в статье
- /ru/system-design/caching/stretched-cache
Запись прогона
У этого замера записи прогона нет — только скрипт.
Скрипт
159 строкRedis 7.0.15 · один хост, два процесса
проверяется не Redis, а измерительный стенд: ретранслятор,
который во всех остальных замерах изображает канал между ДЦ
11. ЧТО СТОИТ САМ СТЕНД, ЕСЛИ ЗАДЕРЖКУ ВЫКРУТИТЬ В НОЛЬ
------------------------------------------------------------------
без канала вовсе: обычная запись, мс 0.20
без канала вовсе: запись с WAIT 1, мс 0.42
канал 0 мс, БЕЗ TCP_NODELAY (стенд, на котором снят блок 10):
обычная запись, мс 0.24
запись с WAIT 1, мс 44.00
канал 0 мс, С TCP_NODELAY (исправленный стенд):
обычная запись, мс 0.18
запись с WAIT 1, мс 0.70
цена прежнего стенда на одной записи, мс +43.30
Вот и ответ на вопрос, который блок 10 оставил открытым, — и ответ
неприятный. Там нашлось слагаемое в 42-44 мс сверх круга, одинаковое
на всех трёх расстояниях, и было честно сказано, что замер его
происхождения не устанавливает. Устанавливает этот: слагаемое целиком
принадлежит ИЗМЕРИТЕЛЬНОМУ ПРИБОРУ.
Три строки читаются подряд. Без канала вовсе `SET` + `WAIT 1` стоит
0.42 мс. Через канал с НУЛЕВОЙ задержкой, но без
TCP_NODELAY — 44.00 мс. Тот же канал, та же нулевая задержка,
флаг включён — 0.70 мс. Расстояния в опыте нет ни в одной из
трёх строк.
Механизм известный. Redis ставит TCP_NODELAY на своих соединениях сам;
ретранслятор создаёт ДВА НОВЫХ соединения, и на них флага не было.
Дальше работает пара «алгоритм Нейгла на отправителе — отложенное
подтверждение на получателе»: мелкая порция придерживается до
подтверждения предыдущей, а подтверждение придерживается до сорока
миллисекунд. Отсюда и ровно сорок с небольшим, и их независимость от
расстояния, и то, что они появлялись только там, где по каналу шло
несколько мелких сообщений подряд.
ЧТО ЭТО ЗНАЧИТ ДЛЯ СТАТЬИ. Числа блока 10 (51,99 / 84,05 / 124,01 мс)
измеряли Redis плюс расстояние плюс залипание в сорок миллисекунд на
собственном стенде. Вывод «с расстоянием цена растёт на один круг, а
не на два» устоял — он держится на разностях, а слагаемое было
постоянным, — но сами числа и «слагаемое неизвестной природы» надо
заменить. Ниже они пересняты.
12. СКОЛЬКО РАЗ ЗАДЕРЖКА ПРИМЕНЯЕТСЯ К ОДНОЙ ОПЕРАЦИИ
------------------------------------------------------------------
канал сериализующий, 20 мс в одну сторону, 20 раз SET+WAIT:
порций мастер → реплика вхолостую 0
порций реплика → мастер вхолостую 1
порций мастер → реплика с нагрузкой 40
порций реплика → мастер с нагрузкой 21
порций сверх фона, всего 60
то же на одну операцию 3.00
задержка применена, мс на операцию 60.0
измерено на операцию, мс 61.81
канал конвейерный, 20 мс в одну сторону, 20 раз SET+WAIT:
порций мастер → реплика вхолостую 0
порций реплика → мастер вхолостую 1
порций мастер → реплика с нагрузкой 39
порций реплика → мастер с нагрузкой 21
порций сверх фона, всего 59
то же на одну операцию 2.95
задержка применена, мс на операцию 59.0
измерено на операцию, мс 41.67
Вот в чём была претензия разбора, выраженная числом. Задержка ставится
не на операцию, а на ПОРЦИЮ, и порций на одну операцию три: сама
запись, запрос подтверждения и подтверждение. Число это не
гарантировано: TCP границ сообщений не хранит, и соседние сообщения
могут слиться в одну порцию или разойтись по двум.
Три порции на операцию — но на критическом пути их две, а не три:
запись и запрос подтверждения идут в одну сторону и уходят вместе,
подтверждение возвращается. Отсюда и круг у конвейерного канала.
Разница между каналами ровно в том, складываются ли задержки. У
сериализующего поток останавливается на время каждого полёта, поэтому
запись и запрос подтверждения, ушедшие бы вместе, идут по очереди — и
операция стоит на один перелёт дороже. Насколько именно, печатает
следующий блок.
13. СЕРИАЛИЗУЮЩИЙ КАНАЛ ПРОТИВ КОНВЕЙЕРНОГО НА ТРЁХ РАССТОЯНИЯХ
------------------------------------------------------------------
в одну сторону круг сериализующий конвейерный разница
5 мс 10 мс 16.06 мс 11.06 мс +5.00
20 мс 40 мс 61.64 мс 41.48 мс +20.16
40 мс 80 мс 121.86 мс 81.36 мс +40.51
сверх круга (цена минус круг):
при 5 мс в одну сторону сериализующий 6.06 · конвейерный 1.06
при 20 мс в одну сторону сериализующий 21.64 · конвейерный 1.48
при 40 мс в одну сторону сериализующий 41.86 · конвейерный 1.36
Это и есть цена ошибки, на которую указал разбор, — в миллисекундах, а
не в рассуждении. Столбец разницы и есть ответ: сериализующий канал
стоит ровно на ОДИН перелёт в одну сторону дороже на каждом
расстоянии. Не на случайную величину и не на долю процента — на ту
самую задержку, которую он применяет к порции, потому что ровно одна
порция из трёх из-за него идёт по очереди вместо того, чтобы уйти
вместе с соседней.
Пока залипание в сорок миллисекунд из блока 11 сидело в обоих
каналах, этой разницы не было видно вовсе: она тонула в артефакте,
который был вчетверо больше её самой. Два дефекта стенда маскировали
друг друга — и это, пожалуй, главный урок всего разбора.
Числа надо публиковать по конвейерному каналу: у него цена `WAIT` —
это круг плюс около полутора миллисекунд, то есть ровно то, чем она и
должна быть по устройству команды.
Чего этот блок НЕ доказывает: что конвейерный ретранслятор равен
`netem`. Задержка здесь по-прежнему применяется к порциям потока, а не
к пакетам; нет ни очередей, ни потерь, ни джиттера, ни ограничения
полосы. На нагрузке, где порций много, расхождение с настоящей сетью
вырастет, и проверить его в этой среде нечем.
14. КАЛИБРОВКА: КАКУЮ ЗАДЕРЖКУ КАНАЛ ДАЁТ НА «ЗАПРОС — ОТВЕТ»
------------------------------------------------------------------
GET напрямую, мс 0.10
через канал, GET мимо ретранслятора к тому же мастеру:
в одну сторону круг сериализующий конвейерный
5 мс 10 мс 10.81 мс 10.79 мс
20 мс 40 мс 40.98 мс 41.00 мс
40 мс 80 мс 81.10 мс 81.11 мс
На простом «запрос — ответ» оба канала дают одно и то же, и дают ровно
круг плюс около миллисекунды: одна порция туда, одна обратно,
складывать нечего. Это граница применимости стенда, названная замером,
а не оговоркой.
Читать её так. Там, где за операцию по каналу проходит одна порция в
каждую сторону, ретранслятор — хорошее приближение расстояния, и числа
блоков 7 и 1 можно брать как есть. Там, где по каналу идёт несколько
сообщений подряд — репликация, конвейер команд, `WAIT`, — прибор
начинает добавлять своё, и добавлял: сорок миллисекунд залипания и
лишний перелёт на сериализации. Оба слагаемых теперь измерены и
убраны, но третьего рода погрешность — отсутствие очередей, потерь,
джиттера и ограничения полосы — остаётся, и снять её в этой среде
нечем.
Отсюда правило для чтения всей статьи: качественные выводы (знак,
кратность, порядок событий, какая половина кластера ответила) держатся;
абсолютные миллисекунды на потоковых операциях — приближение, чья
погрешность известна по знаку, но не по величине.