Как стать автором
Обновить

Комментарии 14

Привет, правильно я понял что error injections реализован через гитовые патчи кода?

Если да, то почему не через тестовые имплементации интерфейсов (если это применимо для LUA)?

Или, скажем, не через через фреймворки врапперы диска и прочих систем (как например сделано в Кафке - https://cwiki.apache.org/confluence/display/KAFKA/Fault+Injection)?

Привет, правильно я понял что error injections реализован через гитовые патчи кода?

Не понял, что такое гитовые патчи.

Если да, то почему не через тестовые имплементации интерфейсов (если это применимо для LUA)?

Наверное потому, что так было проще сделать. То, что вы описываете используется в SQLite, там есть набор вызовов, которые нужны библиотеке от ОС и легко можно реализацию менять на свою, см. https://www.sqlite.org/vfs.html.

Или, скажем, не через через фреймворки врапперы диска и прочих систем (как например сделано в Кафке - https://cwiki.apache.org/confluence/display/KAFKA/Fault+Injection)?

Обычно используют два подхода для внедрения сбоев: со стороны приложения и со стороны тестового окружения. У обоих подходов есть как плюсы так и минусы.

Для внедрения сбоев со стороны приложения большой плюс в простоте реализации, но есть минус - мы меняем код приложения и тем самым тестируем не тот код, который пойдёт в релиз. Есть даже специальные библиотеки для внедрения сбоев на уровне приложения - например https://github.com/pingcap/failpoint или https://github.com/albertito/libfiu. Соответственно для внедрения сбоев со стороны тестового окружения минус в том, что надо каким-то образом проксировать функции ОС, которые использует приложение (например сделать специальную ФС, использовать LD_PRELOAD и т.к.).

тем самым тестируем не тот код

Да, это именно то - что подтолкнуло меня к вопросу.

Не понял, что такое гитовые патчи.

Выглядит - как будто вы применяете гитовый патч на коде в релизной ветке, но не пушите, перекомпиливаете, и получаете измененную версию, в данном случае - с задержкой.

код из статьи напоминающий git patch
код из статьи напоминающий git patch

Если все так - то при рефакторинге все рискует поехать, как этот риск закрываете?

Выглядит - как будто вы применяете гитовый патч на коде в релизной ветке, но не пушите, перекомпиливаете, и получаете измененную версию, в данном случае - с задержкой.

Реализация сбоев всегда в коде основной ветки проекта.

Так все-же, что на картинке?

Кто и как вносит изменения в код, и когда?

Так все-же, что на картинке?

Пример кода, который добавляет новый сбой в код Тарантула.

Кто и как вносит изменения в код, и когда?

Автор патча может добавить новый сбой тогда, когда посчитает нужным.

Если посмотрите на пример такого патча, то думаю все вопросы отпадут - https://github.com/tarantool/tarantool/commit/a82ec30466

Если посмотрите на пример такого патча

Суровые мерж-комментарии однако :)

то думаю все вопросы отпадут

К сожалению нет. Моего знания С и Lua явно недостаточно для понимания всех нюансов фикса.

Из того что вижу (скорректируйте где ошибся)

1) В продакшен код (в кору) добавляются методы вида `ERROR_INJECT(XXX, ...`

2) Срабатывают такие методы если в сценарии сказать `_(XXX, ERRINJ_BOOL, {.bparam = true})\`, видимо это настраивается на CI, так как по дефолту - false.

3) Итого тестовый код перемешан с рабочим, но отделен семантически через `ERROR_INJECT(...`.

В целом вы правильно поняли это работает :)

видимо это настраивается на CI, так как по дефолту - false.

Обычно, да, все сбои выключены, включаются при необходимости в самом тесте.

Супер, но тогда я вижу небольшую проблему.

Дело даже не в том

  • что "продакшен код загрязнен тeстовым" или

  • что "тестируется не то что в продакшене", а немного другой алгоритм (привет сайдэффекты! - в моей практике были случаи когда волатайл чтение помогало тестовому коду не падать, но этого чтения не было в продакшен коде :))

, а что непонятно как вы угадываете где вставить проблему.

Проблема же может случиться между любыми двумя строчками, а если говорим о многопоточке - то от сочетания сайдэффектов/рассинхрона между множеством потоков.

Насколько профитны такие тесты по отношению к "честным внедрениям сбоев со стороны тестового окружения"?

Есть ли Jepsen/Ducktape-like что-то в проекте?

Проблема же может случиться между любыми двумя строчками, а если говорим о многопоточке - то от сочетания сайдэффектов/рассинхрона между множеством потоков.

В Tarantool есть несколько потоков, каждый из них занимается своей задачей: поток для обработки транзакций, поток для работы с запросами из сети, поток для работы с WAL. (подробнее про архитектуру в этой статье). Если мы, например, добавляем сбой для журнала (WAL), то это повлияет только на работу потока, который работает с WAL.

Поэтому в этом плане поведение детерминированное.

Есть ли Jepsen/Ducktape-like что-то в проекте?

Jepsen - да. Цитирую статью: "В 2020 году мы добавили поддержку синхронной репликации и MVCC. Появился запрос на тестирование этой функциональности и мы решили часть тестов сделать на основе фреймворка Jepsen. Консистентность проверяем с помощью анализа истории транзакций. Но рассказ про тестирование с помощью Jepsen вполне потянет на отдельную статью, поэтому об этом в следующий раз.".

Про Ducktape-like не понял, что имеется ввиду.

то это повлияет только на работу потока, который работает с WAL.

Поэтому в этом плане поведение детерминированное.

Не готов поверить что нет влияния одного на другое.

Когда ломается что-то, оно обязательно цепанет что-то еще, если копнуть.

У нас, например, 60к тестов и очень часто вообще несвязанные компоненты аффектятся при багфиксах. Там гонка, тут контеншен, где-то вообще пул кончился, тут вообще читаем с локальной копии (оказывается), а здесь через сеть зацепило и тд.

Про Ducktape-like не понял, что имеется ввиду.

https://ducktape-docs.readthedocs.io/en/latest/

Это, скажем так, альтернативный Jepsen вариант тестирования отказами и не только, где сценарии могут быть любыми, не только проверка ACID.

------

непонятно как вы угадываете где вставить проблему

Насколько профитны такие тесты по отношению к "честным внедрениям сбоев со стороны тестового окружения"?

Вопросы все еще актуальны, очень интересно.

https://ducktape-docs.readthedocs.io/en/latest/

Это, скажем так, альтернативный Jepsen вариант тестирования отказами и не только, где сценарии могут быть любыми, не только проверка ACID.

Не слышал про такой. Судя по документации это набор примитивов для написания кластерных тестов. Оно живое? Там последний коммит был 8 месяцев назад.

Не готов поверить что нет влияния одного на другое.

Когда ломается что-то, оно обязательно цепанет что-то еще, если копнуть.

У нас, например, 60к тестов и очень часто вообще несвязанные компоненты аффектятся при багфиксах. Там гонка, тут контеншен, где-то вообще пул кончился, тут вообще читаем с локальной копии (оказывается), а здесь через сеть зацепило и тд.

Я рассказываю вам о том, что работает и используется в нашем процессе разработки. Очевидно, что если бы это не работало, то этим бы никто и не пользовался. Архитектура и код вашего проекта мне неизвестны, по каким-то причинам у вас это значит не работает.

непонятно как вы угадываете где вставить проблему

Тут, наверное, не совсем правильно использовать слово "угадывать".

ERRINJ вставляем в тех местах, где нужно симулировать сбой. Он просто временно меняет логику, чтобы проверить поведение, которое в нормальной ситуации не должно случиться.

Насколько профитны такие тесты по отношению к "честным внедрениям сбоев со стороны тестового окружения"?

Могу только в теории рассуждать. Частично ответил выше про плюсы и минусы одного и другого подхода. Могу добавить, что для быстрого и фокусного негативного тестирования подходит вариант с fault injection в коде Tarantool, а вариант с fault injection в окружении больше работает когда есть дополнительные ресурсы поддерживать инфраструктуру с этими fault injection. Так сказать первый вариант больше для разработчиков, второй - когда есть ресурсы в команде тестировщиков.

Кстати, fault injection в коде СУБД это не инновация Tarantool, точно такой же подход используется в MySQL (и в MariaDB наверняка).

Оно живое? 

Определенно.

Используется в Kafka и Ignite.

Я на этом чуде ускорил crash recovery с десятков секунд до десятков миллисекунд на кластерах в 100+ узлов.

 Архитектура и код вашего проекта мне неизвестны,

Apache Ignite.

https://ignite.apache.org/

https://github.com/apache/ignite

по каким-то причинам у вас это значит не работает.

Оно не НЕ работает, вопрос в необычном (для меня) подходе.

У нас есть тесты где мы останавливаем/фильтруем/изменяем сообщения например, потому что тестируем какой нить конкретный фэйловер.

При этом, тормозим мы их, реализуя "тормозитор" для конкретного теста/кейса, который тормозит только нужные сообщения или нужное число сообщений или сообщения на конкретный узел. Тоесть мы создаем именно те проблемы которые нужны тесту переопределяя необходимые компонены тестовыми имплементациями, а не располагаем проблемы по коду кроекта (с включением их через матрицу на CI).

Еще есть тесты на Ducktape - где мы "портим" окружение, но тут у нас подходы похожи.

Отсюда мой вопрос - почему был выбран этот вариант и чем он профитнее варианта создавать сложности именно в рамках конретного теста/кейса.

ERRINJ вставляем в тех местах, где нужно симулировать сбой. 

Кажется что сбой может произойти между любыми двумя строчками же. При этом сбой может быть очень разным (задержка сети, диска, памяти, etc). Имея регрессию в тысячи тестов мы, потенциально, покрываем этот момент.

Располагая же инъекции ошибок по коду - невозможно ведь вставить их везде, между всеми строками, или код ошибок будет составлять 99.999% кода проекта.

И мой вопрос здесь именно в том - как вы ухитряетесь делать мало таких вставок и эффективно ловить проблемы.

Я на этом чуде ускорил crash recovery с десятков секунд до десятков миллисекунд на кластерах в 100+ узлов.

Судя по всему вот здесь ваши тесты - https://github.com/apache/ignite/tree/master/modules/ducktests/tests/ignitetest/tests Какие аккуратные тесты.

Отсюда мой вопрос - почему был выбран этот вариант и чем он профитнее варианта создавать сложности именно в рамках конретного теста/кейса.

Я думаю ответ - простота реализации и потому что так привык тестировать человек, который до этого разрабатывал MySQL.

Располагая же инъекции ошибок по коду - невозможно ведь вставить их везде, между всеми строками, или код ошибок будет составлять 99.999% кода проекта.

Да, тут вы правы. В симуляции сбоев в окружении у нас все более реалистично получается: если сбоит дисковая подсистема, то ошибки будут на всех файловых операциях у приложения. А с инъекциями в коде мы покрываем только конкретный кусочек кода. Мне кажется хорошая аналогия это юнит-тесты и интеграционные тесты. Первые быстрые, но далеки от бизнес требований, вторые долгие и сложные, но зато покрывают требования. В идеале лучше комбинировать оба подхода.

И мой вопрос здесь именно в том - как вы ухитряетесь делать мало таких вставок и эффективно ловить проблемы.

Хм, может тут Правило Парето в действии? ;-)

Сомневаюсь, что с инъекциями в коде можно переловить все проблемы, поэтому в тестах Jepsen у нас тоже сбои есть (хотя и не все).

Зарегистрируйтесь на Хабре, чтобы оставить комментарий