Тестирование распределенных систем существенно отличается от тестирования централизованных. Немногие тестировщики могут похвастаться серьезными знаниями и опытом в этой области.
Я пообщался со спикером конференции Heisenbug 2016 Moscow Андреем Сатариным (twitter.com/asatarin). Андрей участвовал в проектах по тестированию в Mail.ru, в Лаборатории Касперского, в Deutsche Bank, а сейчас тестирует распределенные системы в Яндексе. Статья будет полезна не только людям, которые занимаются тестированием, но и разработчикам. Если вы ни разу не касались вопроса тестирования распределенных систем, добро пожаловать под капот.
Андрей Сатарин:
… они убивают ноды прямо в рабочее время и разработчики наблюдают за...
Способы и особенности тестирования распределенных систем
– Какие методики и стратегии тестирования распределенных систем существуют? Чем они отличаются?
– Кроме всем известных классических подходов (модульное тестирование, системное тестирование, интеграционное тестирование) для распределенных систем есть дополнительные подходы, которые призваны обнаруживать сложные дефекты.
Очень популярен подход внедрения сбоя (fault injection). Когда система работает, мы при помощи специальных программ и механизмов добавляем сбои: сбои дисков или целых машин, может быть, сетевые сбои, сбои внутренних компонентов тестируемой системы. Поскольку подавляющее большинство распределенных систем должны обладать устойчивостью к подобного рода сбоям, по крайней мере в каких-то ограниченных масштабах, то система не должна прекращать свою работу или проявлять каких-то аномалий в работе. По сути это тестирование на отказоустойчивость, поскольку это одно из важнейших нефункциональных требований к распределенным системам. Чем больше машин работает, тем выше вероятность индивидуальной проблемы на какой-то из них. Например, если задействована тысяча машин, то, условно говоря, диски будут вылетать раз в неделю. Система обязана переживать такие ситуации никоим образом их не замечая.
Есть более академические подходы, например, формальная верификация (formal verification). В распределенных системах есть внутренние алгоритмы и протоколы, которые позволяют ей работать. Они сами по себе достаточно сложные, но гарантируют некоторые инварианты, которые должны достигаться всегда, независимо от каких-либо сбоев в системе, переупорядочивания пакетов в сети и чего-либо ещё. Суть подхода заключается в том, что на основе только описания алгоритма на специальном языке проверяется его корректность. Это дает уверенность в том, что тот алгоритм, который используется, при условии, что он корректно реализован, будет работать.
В 2015 году вышла академическая статья от Microsoft Research «Proving Practical Distributed Systems Correct», где они описали модель системы распределенного хранилища, после чего с помощью специальных инструментов проверили эту модель на корректность, а затем сгенерировали код, который сразу же заработал.
– Какие особенности необходимо учитывать при тестировании распределенных систем?
– Особенность в том, что важно понимать какие именно инварианты гарантирует тестируемая система. Например, сейчас популярны nosql базы данных, которые могут быть более высокопроизводительными, но они не поддерживают транзакции. То есть их уровень консистентности ниже, чем у классических (MySql, PostgreSQL, Oracle). И, когда происходит тестирование такой распределенной системы, типа nosql базы данных, важно, чтобы было понимание какие именно инварианты она поддерживает. От этого зависят аномалии, которые будут наблюдаться в тестах. В сложных тестах, например, когда есть несколько конкурентных писателей и читателей, можно увидеть много различных состояний. Другими словами, нужно понимать, какие эффекты можно наблюдать в системе, а какие — нет.
Нефункциональные требования играют наиболее существенную роль
– Какие типичные ошибки совершают сами люди при тестировании распределенных систем?
– Самая распространенная ошибка — это проверка не всех гарантий, которые система должна предоставлять, в этом случае система становится недотестирована. Вторая ошибка, которая может дорого стоить, это не тестирование на отказ какой-то части системы. По опыту, если в распределенной системе какая-то подсистема не тестировалась на fault injection, то багов там чуть более, чем очень много.
– Какие метрики и характеристики распределенной системы важно тестировать и почему?
– Из нефункциональных требований это, во-первых, отказоустойчивость (fault tolerance), а во-вторых, это производительность (perfomance). Для распределенных систем нефункциональные требования играют наиболее существенную роль по сравнению с функциональными требованиями. Fault tolerance на первом месте, потому что сначала система должна работать, а если она не работает, то остальное уже не так важно.
– Насколько важна производительность тестов? Нужно ли учитывать возможные задержки сети при разработке тестов для распределенной системы?
– Это зависит от видов тестов, о которых идет речь. Если это модульные тесты, то производительность важна. В общем случае, конечно, лучше иметь быстрые тесты (как говорится, лучше быть здоровым и богатым). Это верно для функциональных тестов. Для нефункциональных, которые проверяют, например, консистентность или устойчивость к сбоям, производительность тестов важна для более частого проявления дефектов. Например, если дефект проявляется один раз на миллион операций, то чем чаще происходят эти операции, тем чаще проявляется и дефект. Если это занимает час, то это вполне приемлемо. Если это занимает несколько дней, то поиск таких дефектов становится проблемой.
98% всех дефектов можно воспроизвести всего лишь на 3 нодах
– Нужно ли создавать специальные кластера для тестирования или можно использовать «боевые» кластера, которые находятся в продакшене? Как определить оптимальный размер тестового кластера?
– Чаще всего применяют именно тестовые кластера. Если говорить про тестирование на боевых серверах, то самый широко известный пример — это компания Netflix, которая активно пропагандирует свой подход, называемый «simian army», то есть «армия макак». Он заключается в том, что они в продакшене делают fault injection. Они убивают ноды прямо в рабочее время и разработчики наблюдают за тем, чтобы система никак при этом не деградировала. Но здесь надо понимать, что такая возможность появляется только начиная с какого-то масштаба. Если система работает на 10-20 нодах, то тестирование подобным образом означает, что будет деградация на 5-10%. В продакшене не все готовы на такие жертвы. Кроме того, может быть какой-то service level agreement (SLA) и такое тестирование может быть дорого из-за его нарушения. В любом случае, даже если встречается практика тестирования на продакшене, то существует перед этим огромная тестовая инфраструктура, которая отлавливает большую часть дефектов. Преимущество тестирования в продакшене только в том, что нет необходимости повторять продуктивное окружение.
По поводу размера тестового кластера. Если система распределенная, то он должен быть больше единицы — это ограничение снизу. На тему ограничений сверху есть статья «Simple Testing Can Prevent Most Critical Failures», в которой исследуется вопрос о том, какие ошибки есть в распределенных системах. Согласно статье, исследователи пришли к выводу, что 98% всех дефектов можно воспроизвести всего лишь на 3 нодах. Конкретно в нашей работе мы используем больше, обычно тестовый кластер состоит из 8 нод, но это связано с внутренним устройством нашей системы.
– Как бороться с полными или частичными отказами распределенной системы во время тестирования?
– Каких-то особых способов борьбы с этим, наверно, нет, потому что в тестовом окружении масштабы сильно меньше. Если сбойное железо сильно мешает, его можно просто исключить из тестового окружения. У нас был случай, когда в тестовом железе происходил сбой, но мы были скорее этому рады, поскольку это позволило нам найти некоторые необычные дефекты. Так как распределенная система должна быть устойчивой к сбоям, то даже в тестах это не должно вызывать никаких проблем.
– Какие конкретно технологии и инструменты используются для создания тестового окружения? Для автоматизации тестирования?
– Тестовое окружение зависит от технологий, которые используются для разработки, и от технологий, которые знакомы команде. Мы, например, активно используем Python, потому что он хорошо подходит для таких задач и наши тестировщики его знают. Он простой в плане написания тестов, достаточно высокоуровневый, чтобы на нем можно было писать понятно. На мой взгляд, у него немного «беда» с concurrency, но эта проблема решаема. Сама система разрабатывается на C++, но его использовать для высокоуровневых тестов достаточно тяжело, так как быстро и легко разрабатывать на нем не получится, а в тестах бывает важна скорость разработки.
По поводу автоматизации тестирования. Обычно строится репозиторий тестов, которые автоматически запускаются на специальном сервере. Мы для этого используем TeamCity и некоторые наши внутренние разработки.
– У тебя есть еще что-нибудь, что ты бы хотел добавить по теме?
– Я бы хотел добавить, что на тему тестирования распределенных систем есть огромное количество материалов как академических, так и близких к индустрии, огромное множество подходов и способов тестирования. Поиск методов и их совершенствование не останавливается ни на день. Эта тема постоянно развивается – и именно этим она интересна.
Послушать больше докладов на тему тестирования можно будет 10 декабря в гостинице «Radisson Славянская» на конференции Гейзенбаг. Регистрация еще открыта.
Темы докладов:
- No Such Thing as Manual Testing and Other Confusions
- Appium: Automation for Apps
- Как научить роботов играть в игры?
- Hero’s Journey to Perfect System Tests — Eight Assessment Criteria for Tests’ Architecture Design
- Page Objects — лучше меньше, да лучше
- Тестирование распределенных систем
- Тестирование Android–приложения Juno с ️: CI, Unit, Integration и Functional (UI) тесты. 100% Kotlin, 90%+ RxJava, Spek, JUnit, DSL для UI тестов
- Combining manual and automated testing: process and tools
- Список покупок: что нужно не забыть при запуске JMeter-тестов
- Статический вынос мозга: что скрывают анализаторы кода?
Автор: JUG.ru Group