Тестирование производительности: подводные камни

от автора

Я занимаюсь созданием высоконагруженных приложений для биржевой торговли. Нагруженных как по объёму данных, так и по количеству пользователей и запросов. Естественно, что для таких приложений первостепенное значение имеет производительность, и, как следствие, тестирование оной.

Наблюдая со стороны это тестирование, я накопил некоторый объём информации, который, как я думаю, будет небезынтересен.

Камень 1-й. Коэффициент пересчёта

Тестирование таких приложений требует развёртывания целой сети тестовых машин. В итоге это приводит к образованию тестового кластера. Попытка развёртывания такого кластера на базе машин, физически стоящих в центре разработки, ведёт к созданию собственного датацентра со всеми издержками такого решения. Хорошей альтернативой стало использование решений типа Amazon Web Services.

Естественное желание сэкономить на аренде хостов или на покупке оборудования приводит к выбору таковых с заниженными относительно production инсталляции характеристиками. Заниженными в разы. И тут вступает в действие коэффициент пересчёта между синтетическими индексами производительности. Т.е. процессор в production будет в 2 раза быстрее, количество ядер будет в 4 раза больше, объём оперативной памяти будет в 6 раз больше, производительность дисковой подсистемы будет в 3,5 раза лучше, скорость сети будет в 100 раз больше. Сложим, поделим на количество показателей, умножим на некий поправочный коэффициент и получим коэффициент пересчёта, на который будем умножать результаты тестирования производительности. Можно придумать и более сложную формулу, например, назначить каждому показателю некий вес.

При ближайшем же рассмотрении такой подход оказывается пригодным лишь для подготовки тестовой сюиты для будущего тестирования на инсталляции, близкой к production, и на обнаружение самых очевидных проблем производительности. (Что уже достаточно много и важно.) Почему? Да потому хотя бы, что при таком подходе совершенно не учитывается эффект bottlenecks.

Пример из жизни. Тесты запускались на одном хосте, тестируемое приложение – на другом. Тестовая сюита включала в себя запросы, дающие разный объём данных в ответе. Запросы, дающие относительно немного данных, дали удовлетворительные результаты, запросы же, дающие ответы большого объёма, дали неудовлетворительные результаты. При этом хост тестируемого приложения был далёк от перегрузки. Напрашивается вывод: тестируемое приложение плохо справляется с запросами, дающими большую выдачу, не использует при этом всех ресурсов машины, т.е. нуждается в перепроектировании. А что же на самом деле? А на самом деле выяснилось, что низкая скорость сети приводила к долгой передаче ответов, что особенно сказывалось на ответах большого объёма и, таким образом, просто не позволяла создать большую нагрузку на тестируемое приложение.

Вот пример bottleneck’а, проявившегося на тестовой (медленной) инсталляции, и просто невозможного на production. Назовём его «нисходящий» bottleneck. Как бы там ни было, «нисходящий» bottleneck неопасен и вызывает только непроизводительную потерю времени тестировщика и разработчика.

А может ли быть обратное, возможен ли «восходящий» bottleneck, который по-настоящему опасен и может доставить большие неприятности не только разработчику, тестировщику и т.п., но и клиенту? Т.е. представим себе, что показатели, достигнутые на тестовой инсталляции, нас полностью удовлетворяют. Например, коэффициент пересчёта у нас 5, нам нужно обеспечить 100 000 операций в секунду, тестовая инсталляция дала 25 000. Вроде бы всё ОК, можно спать спокойно? Как бы не так! Точно так же в такой ситуации может проявиться bottleneck, необнаруженный (и принципиально необнаруживаемый!) на тестовой инсталляции. Из-за которого реальный, эффективный коэффициент пересчёта окажется не 5, а 3. Т.е. не 125 000, а только 75 000 – на 25% хуже, чем нужно.

Причём для «восходящих» bottleneck’ов открывается огромный простор возможностей: мы ведь формируем коэффициент пересчёта производительности на основании синтетических индексов, в которых вес отдельных показателей выбирается практически произвольно. Достаточно неверно оценить вес одного из показателей, и…

Камень 2-й. Тестирующее приложение

Тестирование производительности требует создания нагрузки. Нагрузки с нескольких хостов, с нескольких пользовательских аккаунтов, посылки большого количества разных запросов. Возникает естественное желание всё это автоматизировать. Сделать свою утилиту или найти готовую, которых, к счастью, достаточно много.

Но, создавая большую нагрузку тестируемому приложению, само тестирующее приложение оказывается под нагрузкой. Это не совсем очевидно, но, тем не менее, это так. Обратимся к аналогии. Все мы знаем, что любой измерительный прибор воздействует на измеряемый процесс и, таким образом, неизбежно вносит погрешность. Точно также и особенности реализации тестов влияют на результаты тестирования, что не всегда учитывается. Для тестировщиков естественно в первую (и любую) очередь приглядываться к тестируемому приложению, а не к самим тестам. Я здесь ни в коем случае не имею в виду банальные ошибки в тестах. Я имею в виду неочевидное воздействие тестов на процесс тестирования.

Например. Тестирующее приложение посылает тысячи запросов в секунду. Оказывается, что каждый следующий запрос выполняется медленнее предыдущего. Что же, система не выдерживает нагрузки, нарастают задержки, растёт очередь запросов на обработку? А выясняется, что тестирующее приложение (на Java) под каждый запрос-ответ выделяет определённое количество памяти, и чем больше сделано запросов и получено ответов, тем медленнее выделяется в нём память, тем меньше запросов за единицу времени может послать тестирующее приложение.

Камень 3-й. Чёрный ящик

Наиболее общим подходом к тестированию считается представление тестируемого приложения как чёрного ящика. Тестировщики не входят в подробности реализации, не учитывают особенностей и взаимосвязей отдельных компонентов приложения.

Такой подход хорош в общем случае. Причём в необходимые исходные условия этого общего случая входит большое (практически неограниченное) количество времени для тестирования. Что практически никогда не встречается в действительности. Наоборот, тестировщики в большинстве случаев отстают от разработчиков. Потому хотя бы, что разработчики без конца что-то переделывают.

В таких условиях важно максимально сокращать время, необходимое для тестирования, при этом не снижая его качества, разумеется. Для решения этой задачи необходимо тесное взаимодействие тестировщиков и разработчиков, в ходе которого разработчики, естественно, знающие своё приложение, могут заранее указать тестировщикам слабые места, возможные bottleneck’и т.п. При этом, конечно же, теряется подход к тестируемому приложению, как к чёрному ящику. Но, во-первых, это оправдывается, во-вторых, совершенно необязательно все тесты делать во взаимодействии с разработчиками.

В статье описаны 3 подводных камня. Возможно, кто-то хотел бы рассказать про другие неочевидные проблемы тестирования производительности. Жду Ваших отзывов!

ссылка на оригинал статьи http://habrahabr.ru/company/etnasoft/blog/168651/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *