9.2 KiB
Производительность и дополнительные возможности
Целевой уровень из задания — задержка не выше 0,5 с при 1000 запросах в секунду. Замер ниже снят на правилах, без нейросетевой ступени: именно так работает поставка, пока каталог models/ пуст.
Нагрузочный тест
Инструмент — k6, сценарий k6-load-test.js.
Условия:
- один узел в Docker, порт 8080;
- система
crm(маскирование, без демаскирования; типы — ФИО, телефон, email и части адреса); - пять коротких текстов, часть из них с персональными данными, один — без них;
- профиль виртуальных пользователей: 30 с до 500, затем по 30 с на 1000, 1500 и 2000, затем спад до нуля;
- между запросами одного пользователя пауза 0,1 с;
- пороги сценария: доля ошибок ниже 1 %, p95 длительности HTTP ниже 200 мс.
Запуск при уже поднятом сервисе:
k6 run k6-load-test.js
Другой адрес: k6 run -e BASE_URL=http://localhost:8080 k6-load-test.js.
Зафиксированный прогон этого сценария на одном узле:
| Метрика | Значение |
|---|---|
| Пропускная способность | ~9 500 запросов/с |
| Задержка p50 | 1,03 мс |
| Задержка p95 | 13,87 мс |
| Ошибки | 0,00 % |
0,5 с при 1000 запросах/с перекрыто с запасом: p95 на этом профиле около 14 мс, поток около 9 500 запросов/с.
Отдельный замер внутри процесса (PerformanceBenchmarkTest) гоняет тот же набор текстов по правилам после прогрева JIT. В тесте закреплены пороги: p99 маскирования одного обращения ниже 5 мс и пропускная способность выше 1000 обращений/с на доступных ядрах. Это порог регрессии, а не замена цифр k6.
Потолок одновременных запросов не фиксирован. AdaptiveConcurrencyLimiter держит его между pdguard.min-concurrent (8) и pdguard.max-concurrent (2000) и подстраивает по фактической задержке с целью 200 мс. На старте PipelineWarmup прогоняет горячий путь несколько тысяч раз на отдельном коротком хранилище, чтобы первые боевые запросы не попали на непрогретый код: без этого p95 в первые десятки секунд примерно на порядок хуже.
Вторая ступень (NER) в замер не входила. Модель зовётся только на непокрытые кандидаты и стоит десятки миллисекунд на вызов; на текстах, которые уже закрыты правилами, она не вызывается. Доля таких обращений видна в метрике pdguard_ner_requests_total.
На дашборде Grafana (http://localhost:3000/d/pd-guard) во время прогона смотрят обращения в секунду, p95 маскирования, долю ответов быстрее 0,5 с, отказы по перегрузке и текущий предел конкурентности.
Дополнительные возможности
Сверх маскирования заданного перечня типов реализовано следующее.
Четыре режима подмены. Звёздочки с сохранением краёв и разделителей (MASK), сплошное закрытие (STRICT), обратимые токены (TOKEN), правдоподобные вымышленные значения (SYNTHETIC). Синтетический номер карты проходит проверку Луна. Один и тот же фрагмент в тексте всегда получает одну и ту же замену.
Политика на систему без пересборки. Включение, демаскирование, режим, белый список типов, типы-спутники и общий секрет задаются в config/systems.json и применяются на лету. Хранилище соответствий разделено по системам.
Контекстное маскирование. PIN, CVV, дата без якоря, место рождения, страна, реквизиты организации, доход и упоминание биометрии маскируются только рядом с самостоятельным персональным данным. Список спутников настраивается у каждой системы. Два спутника друг друга не подтверждают.
Защита от ложных срабатываний. Общеизвестные имена и правители снимаются, если рядом нет других персональных данных; внешний список config/well-known.txt дополняет встроенный и перечитывается сам. Имя в названии организации и на вывеске не маскируется. Адрес отделения банка и улица в рассказе о городе не считаются адресом клиента. Словари имён, стран и населённых пунктов учитывают склонения и уменьшительные формы.
Контрольные суммы и свободная запись чисел. Карта, ИНН, СНИЛС, ОГРН и ОГРНИП подтверждаются контрольной суммой. Те же номера находятся, если между цифрами стоят пробелы, точки, дефисы или скобки.
Вторая ступень распознавания. Три необязательные ONNX-модели: имена (WikiNEuRal), составляющие адреса (ruBERT), юридические реквизиты. Модели в репозиторий не входят, скачиваются tools/fetch-ner-model.sh. Сбой ступени её отключает и оставляет правила. Регион и район адреса размечаются только этой ступенью.
Безопасный отказ. При внутренней ошибке наружу уходит фиксированная строка [обработка недоступна], а не исходный текст. В журнал пишутся идентификатор, длина и счётчики типов; значения персональных данных не пишутся.
Хранилище. Исходный текст шифруется AES-GCM. Запись живёт ограниченное время и вытесняется по объёму. Демаскирование возможно по payload_id и по отпечатку маски. Повтор того же исходного текста с тем же идентификатором возвращает прежнюю маску. Общий слой Redis включается настройкой pdguard.store.backend=redis и при серии сбоев на время перестаёт опрашиваться, не роняя запрос.
Демонстрация модели. POST /proxy показывает замаскированный запрос, ответ модели и восстановленный текст. Без адреса модели работает заглушка; ошибка модели тоже сводится к заглушке.
Наблюдаемость. Пробы /health и /actuator/health, метрики Micrometer и Prometheus, готовые Prometheus и Grafana. Гистограмма pdguard.process размечена корзинами до 10 с, на дашборде видна доля ответов быстрее 0,5 с. Веб-страница http://localhost:8080/ гоняет три режима маскирования без отдельного клиента.
Прогрев и предел нагрузки. Прогрев JIT на старте и адаптивный лимитер конкурентности с ответом 429, чтобы задержка не упиралась в таймаут вызывающей стороны.