Мы в своей команде пришли к похожей модели, но начали с shadow-дежурств. Механика такая: QA-инженер не несёт формальной ответственности в он-колле, но целую неделю «сидит на плече» у дежурного разработчика. Смотрит в те же дашборды, слушает те же созвоны, задаёт вопросы. Это снимает 90% страха «а вдруг я что-то сломаю». Главный инсайт, который после таких циклов команда тестирования кардинально меняет фокус. Из «почему падает тест?» на «а что будет с очередью, если этот воркер умрёт?». Автоматически рождаются тесты на resilience (Chaos Mesh, кстати, отлично вписался).
Вопрос, который у нас до сих пор вызывает споры как вы балансируете нагрузку? Не возникает ли конфликта между «нужно писать тесты/автоматизацию» и «нужно быть в готовности к инциденту»? Ввели ли вы какую-то formal reduction в планах на время дежурства?И спасибо за список литературы — «Кабанчик» (Release It!) действительно must-read. Добавлю от себя «Building Evolutionary Architectures» — там отлично разобрана идея fitness functions, которые очень близки к мониторингу «здоровья» системы, о котором вы пишете.
У меня несколько вопросов, которые критичны для организации тестирования подобных сред:
Тестирование "побега" из контейнера (container escape): Вы упомянули, что общее ядро — это вектор уязвимости. Какие практические методы и инструменты вы бы рекомендовали для проактивного тестирования изоляции? Помимо сканирования CVEs ядра, стоит ли на регулярной основе запускать в контейнерах инструменты вроде amicontained или использовать chaos-инженерию, чтобы проверить, не "просочились" ли процессы, сетевые сокеты или файлы за пределы namespaces?
Валидация лимитов cgroups в продакшене: Как эффективно тестировать, что лимиты памяти/CPU (--memory, --cpus), установленные через Docker, реально применяются ядром и не могут быть обойдены? Достаточно ли для этого мониторинга метрик (процент использования vs лимит), или нужны нагрузочные тесты, которые намеренно пытаются превысить лимит и проверяют, сработает ли троттлинг/OOM killer? Есть ли тонкости с разными версиями ядра или драйверов cgroups v1 vs v2?
Тестирование многоуровневой файловой системы (OverlayFS): Вы упомянули механизм copy-on-write. Какие edge-cases здесь стоит проверять? Например, как ведёт себя контейнер при переполнении writable-слоя? Или что происходит, если процесс в контейнере держит открытым файл из read-only слоя, а этот слой обновляется (пересобирается образ) на хосте? Может ли это привести к corruption или неожиданному поведению?
Бонус-вопрос про тестирование в CI/CD: Интегрируете ли вы подобные низкоуровневые проверки (тесты на изоляцию, лимиты) в ваш пайплайн сборки образов? Или это удел периодического аудита безопасности?
Ещё раз спасибо за статью, которая переводит теорию ядра в плоскость практической инженерии.
Артём, огромное спасибо за структурированный и честный разбор! Как техлид QA в другой крупной продуктовой компании, мы прошли очень похожий путь: от монстра на 400+ микросервисов к изолированным репозиториям. Подтверждаю каждый из ваших пунктов про боль сборок, рефакторинга и онбординга.
Хочу дополнить ваш кейс одной практикой, которая стала для нас следующей ступенью эволюции:
Мы тоже пришли к шаблонизатору (у нас Cookiecutter) и общим библиотекам. Но со временем обнаружили "расползание" шаблонов: в каждом новом репо команды начинали слегка модифицировать gradle.properties, добавлять свои кастомные утилиты или stages в Jenkinsfile. Консистентность падала.
Наше решение — "фреймворк как a Service" (FaaS). Мы создали отдельный сервис (банальный Spring Boot), который:
По API принимает название сервиса и ссылку на его OpenAPI spec.
Генерирует и сразу пушит в GitLab полноценный репозиторий с тестами, включая ВСЮ конфигурацию (CI/CD, кьюбанернеймспейсы, даже права доступа для групп).
Не дает его модифицировать вручную. Все изменения в шаблонах вносятся централизованно в этот сервис. Обновление существующих репо — через отдельную команду, которая предлагает мерж-реквест с диффами.
Это уже следующий уровень стандартизации, который убивает дрифт конфигов. Возможно, и вам пригодится эта идея.
Главный вопрос по стратегии: Ваша библиотека проверок (initChecksChain().softChecks()) — это, по сути, доменно-специфичный язык (DSL) для ваших тестов. Как вы управляете его эволюцией?
Когда появляется потребность в новой проверке (например, проверка поля по сложной regexp из внешнего конфига), как происходит процесс: команда делает PR в библиотеку, вы как техлид ревьюите? Не возникает ли соблазна у команд дублировать логику прямо в своих репо, чтобы не ждать релиза общей библиотеки?
И еще: планируете ли вы открыть часть этих наработок (например, плагин для генерации или DSL)? Это было бы огромным вкладом в сообщество.
Еще раз спасибо за статью! Обмен подобным опытом бесценен.
Согласен, формулировка получилась шаблонной перечитал, и правда режет глаз :) Спасибо, что заметил. Суть же в том, что статья отличный практический повод проверить, как связанные между собой санитайзеры в стеке .Нейро пользоваться в коментах как-то чересчур. Ладно если статьи еще через GPT строчат.
Отличное исследование, спасибо! Как QA-инженер, специализирующийся на безопасности веб-приложений, вижу в этом материале готовый чек-лист для тестирования.
Вы прекрасно показали, как 11 санитайзеров дают 10 разных результатов. Это — идеальная иллюстрация принципа "не доверяй, а проверяй" при выборе инструментов безопасности.
Для команды тестирования это раскрывает несколько критических сценариев:
Тест на последовательную обработку (pipeline bypass): Нужно обязательно тестировать кейс, когда входные данные проходят через цепочку разных санитайзеров/валидаторов (например, один на бэкенде, другой на фронтенде). Как вы показали на примере DOMPurify -> HTML Purifier, результат может быть катастрофическим. Автоматизированный тест должен подавать <plaintext>malicious</plaintext> и проверять, не "просочилось" ли содержимое на конечную страницу.
Fuzzing-тесты с устаревшими тегами: Помимо <plaintext>, в арсенале пентестера есть <xmp>, <noembed>, <noframes>, <!--...--> с особенностями парсинга. Все они должны быть в наборе данных для фаззинга полей ввода. Особенно в комбинации с UTF-7, тригграми и другими техниками обхода.
Валидация CSP в <meta>: Ваш пример с обнулением CSP через <plaintext> — это готовая test case для проверки уязвимости. Нужно проверить: если злоумышленник может инжектить контент в <head> до мета-тега CSP, сработает ли блокировка? Это прямое указание не полагаться на мета-теги для критической политики безопасности.
Вопрос к автору/сообществу: Встречались ли вам в дикой природе реальные эксплойты, использующие <plaintext> или подобные "зомби-теги"? Или они остаются преимущественно академическим курьёзом, который, однако, прекрасно вскрывает фундаментальные проблемы в конвейерах обработки контента?
Статья — отличный повод пересмотреть тест-кейсы на XSS. Спасибо!
Спасибо за статью, очень наглядно! Хочется немного углубиться в механику PG, описанную в пункте: "После получения блокировки перечитывает строку, перепроверяя условие WHERE".
Насколько я понимаю, это связано с тем, что команда UPDATE в PG использует механизм MVCC (Multi-Version Concurrency Control). В момент, когда третья сессия наконец получает блокировку на строку 4, эта строка уже является новой версией (updated tuple), созданной второй сессией. Третья сессия видит эту новую версию (где n=1) и проверяет условие WHERE n=2 уже для нее. Условие не проходит, и обновления не происходит.
Правильно ли я понимаю, что это "перечитывание" — это не отдельный запрос, а часть алгоритма прохода по выбранным версиям строк внутри одного снимка данных (snapshot)?
Влияет ли на это поведение уровень изоляции транзакции? Например, будет ли разница между READ COMMITTED и REPEATABLE READ в этом сценарии? (В REPEATABLE READ, полагаю, третья сессия вообще не увидела бы новой версии строки от второй сессии и, возможно, повела бы себя иначе?).
Спасибо за разъяснения! Такие детали крайне полезны для понимания того, как проектировать тесты, затрагивающие параллельные изменения данных.
Кирилл, спасибо за глубочайшую статью! Как QA-инженер, работающий с распределёнными системами, особенно ценю разбор таких низкоуровневых деталей, как управление пулом сессий и политики retry.
У меня пара вопросов именно с точки зрения обеспечения качества такого клиента:
Тестирование дрожания (jitter) пула: Вы показали отличные графики, как LIFO-стек убирает "рубцы". Какие метрики и нагрузки вы использовали для этого нагрузочного тестирования? Полагаю, кроме RPS, важно было отслеживать session_creation_rate и pool_contention_time. Использовали ли вы специальные chaos-тесты, чтобы искусственно создавать условия, при которых FIFO-пул начинает "дрожать"?
Валидация политик jitter для backoff: Стратегия с Full Jitter для Aborted и Equal Jitter для Overloaded — это серьезно. Как вы валидировали корректность их работы в продакшене? Например, есть ли мониторинг, который позволяет увидеть, что при всплеске TransactionLockInvalidated ваши клиенты действительно "разъезжаются" по времени ретраев, а не создают новый herd?
Интеграционное тестирование с ORM: Вы упомянули ~750 тестов и AdoNet.Specification.Tests. При интеграции с EF Core/Linq2DB, какие самые неочевидные кейсы всплывали? Например, как ведет себя пул сессий при долгих lazy-loading операциях или при неявном открытии/закрытии транзакций внутри SaveChanges?
Еще раз спасибо за статью, она — готовое ТЗ для тестирования любого подобного клиента.
Информация
В рейтинге
Не участвует
Дата рождения
Зарегистрирован
Активность
Специализация
Инженер по автоматизации тестирования, Инженер по ручному тестированию
Мы в своей команде пришли к похожей модели, но начали с shadow-дежурств. Механика такая: QA-инженер не несёт формальной ответственности в он-колле, но целую неделю «сидит на плече» у дежурного разработчика. Смотрит в те же дашборды, слушает те же созвоны, задаёт вопросы. Это снимает 90% страха «а вдруг я что-то сломаю». Главный инсайт, который после таких циклов команда тестирования кардинально меняет фокус. Из «почему падает тест?» на «а что будет с очередью, если этот воркер умрёт?». Автоматически рождаются тесты на resilience (Chaos Mesh, кстати, отлично вписался).
Вопрос, который у нас до сих пор вызывает споры как вы балансируете нагрузку? Не возникает ли конфликта между «нужно писать тесты/автоматизацию» и «нужно быть в готовности к инциденту»? Ввели ли вы какую-то formal reduction в планах на время дежурства?И спасибо за список литературы — «Кабанчик» (Release It!) действительно must-read. Добавлю от себя «Building Evolutionary Architectures» — там отлично разобрана идея fitness functions, которые очень близки к мониторингу «здоровья» системы, о котором вы пишете.
У меня несколько вопросов, которые критичны для организации тестирования подобных сред:
Тестирование "побега" из контейнера (container escape): Вы упомянули, что общее ядро — это вектор уязвимости. Какие практические методы и инструменты вы бы рекомендовали для проактивного тестирования изоляции? Помимо сканирования CVEs ядра, стоит ли на регулярной основе запускать в контейнерах инструменты вроде
amicontainedили использовать chaos-инженерию, чтобы проверить, не "просочились" ли процессы, сетевые сокеты или файлы за пределы namespaces?Валидация лимитов cgroups в продакшене: Как эффективно тестировать, что лимиты памяти/CPU (
--memory,--cpus), установленные через Docker, реально применяются ядром и не могут быть обойдены? Достаточно ли для этого мониторинга метрик (процент использования vs лимит), или нужны нагрузочные тесты, которые намеренно пытаются превысить лимит и проверяют, сработает ли троттлинг/OOM killer? Есть ли тонкости с разными версиями ядра или драйверов cgroups v1 vs v2?Тестирование многоуровневой файловой системы (OverlayFS): Вы упомянули механизм copy-on-write. Какие edge-cases здесь стоит проверять? Например, как ведёт себя контейнер при переполнении writable-слоя? Или что происходит, если процесс в контейнере держит открытым файл из read-only слоя, а этот слой обновляется (пересобирается образ) на хосте? Может ли это привести к corruption или неожиданному поведению?
Бонус-вопрос про тестирование в CI/CD: Интегрируете ли вы подобные низкоуровневые проверки (тесты на изоляцию, лимиты) в ваш пайплайн сборки образов? Или это удел периодического аудита безопасности?
Ещё раз спасибо за статью, которая переводит теорию ядра в плоскость практической инженерии.
Артём, огромное спасибо за структурированный и честный разбор! Как техлид QA в другой крупной продуктовой компании, мы прошли очень похожий путь: от монстра на 400+ микросервисов к изолированным репозиториям. Подтверждаю каждый из ваших пунктов про боль сборок, рефакторинга и онбординга.
Хочу дополнить ваш кейс одной практикой, которая стала для нас следующей ступенью эволюции:
Мы тоже пришли к шаблонизатору (у нас Cookiecutter) и общим библиотекам. Но со временем обнаружили "расползание" шаблонов: в каждом новом репо команды начинали слегка модифицировать
gradle.properties, добавлять свои кастомные утилиты или stages в Jenkinsfile. Консистентность падала.Наше решение — "фреймворк как a Service" (FaaS). Мы создали отдельный сервис (банальный Spring Boot), который:
По API принимает название сервиса и ссылку на его OpenAPI spec.
Генерирует и сразу пушит в GitLab полноценный репозиторий с тестами, включая ВСЮ конфигурацию (CI/CD, кьюбанернеймспейсы, даже права доступа для групп).
Не дает его модифицировать вручную. Все изменения в шаблонах вносятся централизованно в этот сервис. Обновление существующих репо — через отдельную команду, которая предлагает мерж-реквест с диффами.
Это уже следующий уровень стандартизации, который убивает дрифт конфигов. Возможно, и вам пригодится эта идея.
Главный вопрос по стратегии:
Ваша библиотека проверок (
initChecksChain().softChecks()) — это, по сути, доменно-специфичный язык (DSL) для ваших тестов. Как вы управляете его эволюцией?Когда появляется потребность в новой проверке (например, проверка поля по сложной regexp из внешнего конфига), как происходит процесс: команда делает PR в библиотеку, вы как техлид ревьюите? Не возникает ли соблазна у команд дублировать логику прямо в своих репо, чтобы не ждать релиза общей библиотеки?
И еще: планируете ли вы открыть часть этих наработок (например, плагин для генерации или DSL)? Это было бы огромным вкладом в сообщество.
Еще раз спасибо за статью! Обмен подобным опытом бесценен.
Согласен, формулировка получилась шаблонной перечитал, и правда режет глаз :) Спасибо, что заметил. Суть же в том, что статья отличный практический повод проверить, как связанные между собой санитайзеры в стеке .Нейро пользоваться в коментах как-то чересчур. Ладно если статьи еще через GPT строчат.
Отличное исследование, спасибо! Как QA-инженер, специализирующийся на безопасности веб-приложений, вижу в этом материале готовый чек-лист для тестирования.
Вы прекрасно показали, как 11 санитайзеров дают 10 разных результатов. Это — идеальная иллюстрация принципа "не доверяй, а проверяй" при выборе инструментов безопасности.
Для команды тестирования это раскрывает несколько критических сценариев:
Тест на последовательную обработку (pipeline bypass): Нужно обязательно тестировать кейс, когда входные данные проходят через цепочку разных санитайзеров/валидаторов (например, один на бэкенде, другой на фронтенде). Как вы показали на примере DOMPurify -> HTML Purifier, результат может быть катастрофическим. Автоматизированный тест должен подавать
<plaintext>malicious</plaintext>и проверять, не "просочилось" ли содержимое на конечную страницу.Fuzzing-тесты с устаревшими тегами: Помимо
<plaintext>, в арсенале пентестера есть<xmp>,<noembed>,<noframes>,<!--...-->с особенностями парсинга. Все они должны быть в наборе данных для фаззинга полей ввода. Особенно в комбинации с UTF-7, тригграми и другими техниками обхода.Валидация CSP в <meta>: Ваш пример с обнулением CSP через
<plaintext>— это готовая test case для проверки уязвимости. Нужно проверить: если злоумышленник может инжектить контент в<head>до мета-тега CSP, сработает ли блокировка? Это прямое указание не полагаться на мета-теги для критической политики безопасности.Вопрос к автору/сообществу: Встречались ли вам в дикой природе реальные эксплойты, использующие
<plaintext>или подобные "зомби-теги"? Или они остаются преимущественно академическим курьёзом, который, однако, прекрасно вскрывает фундаментальные проблемы в конвейерах обработки контента?Статья — отличный повод пересмотреть тест-кейсы на XSS. Спасибо!
Спасибо за статью, очень наглядно! Хочется немного углубиться в механику PG, описанную в пункте: "После получения блокировки перечитывает строку, перепроверяя условие WHERE".
Насколько я понимаю, это связано с тем, что команда UPDATE в PG использует механизм MVCC (Multi-Version Concurrency Control). В момент, когда третья сессия наконец получает блокировку на строку 4, эта строка уже является новой версией (updated tuple), созданной второй сессией. Третья сессия видит эту новую версию (где n=1) и проверяет условие
WHERE n=2уже для нее. Условие не проходит, и обновления не происходит.Правильно ли я понимаю, что это "перечитывание" — это не отдельный запрос, а часть алгоритма прохода по выбранным версиям строк внутри одного снимка данных (snapshot)?
Влияет ли на это поведение уровень изоляции транзакции? Например, будет ли разница между
READ COMMITTEDиREPEATABLE READв этом сценарии? (ВREPEATABLE READ, полагаю, третья сессия вообще не увидела бы новой версии строки от второй сессии и, возможно, повела бы себя иначе?).Спасибо за разъяснения! Такие детали крайне полезны для понимания того, как проектировать тесты, затрагивающие параллельные изменения данных.
Кирилл, спасибо за глубочайшую статью! Как QA-инженер, работающий с распределёнными системами, особенно ценю разбор таких низкоуровневых деталей, как управление пулом сессий и политики retry.
У меня пара вопросов именно с точки зрения обеспечения качества такого клиента:
Тестирование дрожания (jitter) пула: Вы показали отличные графики, как LIFO-стек убирает "рубцы". Какие метрики и нагрузки вы использовали для этого нагрузочного тестирования? Полагаю, кроме RPS, важно было отслеживать
session_creation_rateиpool_contention_time. Использовали ли вы специальные chaos-тесты, чтобы искусственно создавать условия, при которых FIFO-пул начинает "дрожать"?Валидация политик jitter для backoff: Стратегия с Full Jitter для
Abortedи Equal Jitter дляOverloaded— это серьезно. Как вы валидировали корректность их работы в продакшене? Например, есть ли мониторинг, который позволяет увидеть, что при всплескеTransactionLockInvalidatedваши клиенты действительно "разъезжаются" по времени ретраев, а не создают новый herd?Интеграционное тестирование с ORM: Вы упомянули ~750 тестов и AdoNet.Specification.Tests. При интеграции с EF Core/Linq2DB, какие самые неочевидные кейсы всплывали? Например, как ведет себя пул сессий при долгих lazy-loading операциях или при неявном открытии/закрытии транзакций внутри
SaveChanges?Еще раз спасибо за статью, она — готовое ТЗ для тестирования любого подобного клиента.