Как стать автором
Обновить
51
0
Вячеслав @polarnik

Performance Engineer

Отправить сообщение

Сколько памяти нужно серверу сказать сложно. У меня базы данных, размером около 15 ГБайт:

15973724349     ./engine
15298464381     ./engine/data
54345865        ./engine/wal
4096            ./engine/replicationq

При этом сервису выделено 25 ГБайт HEAP, а на самом сервере 32 ГБайт ОЗУ

Базы данных достаточно нагруженные, потребление ресурсов такое:

  • Res: 9 ГБайт (всего выделено 25 ГБайт Heap, а памяти на сервере 30 ГБайт физически)

  • Virt: 40 ГБайт

  • Shr: 633 МБайт (для контейнера задан --shm-size 2g)

Под нагрузкой все 8 ядер процессора используются. Технически такую базу данных можно перести на сервер, где будет только 16 ГБайт ОЗУ и

GOMEMLIMIT=11GiB

А также можно сократить и настройку shm до 1g -- тоже хватит

Но ядра CPU нужны

Всем привет!

Прошло время, я обновился до InfluxDB v2

Стал использовать станцию
Instance type
m5a.2xlarge -- 32 ГБайт ОЗУ
https://instances.vantage.sh/aws/ec2/m5a.2xlarge

Запускаю сервис вот так:

docker run --shm-size 2g \
  --user=influxdb --restart=on-failure --restart unless-stopped \
  -d -p 8086:8086 \
  --log-driver=syslog \
  --name influx \
  --env-file env.list \
  -v /home/influxdb:/var/lib/influxdb2 \
influxdb:2.7.6-alpine

Где файл env.list это файл, в котором задано использование только 25 ГБайт ОЗУ из 32-х и другие более строгие лимиты по памяти:

GOMEMLIMIT=25GiB
GOGC=10


INFLUXD_REPORTING_DISABLED=true
INFLUXD_STORAGE_CACHE_SNAPSHOT_WRITE_COLD_DURATION=10m
INFLUXD_STORAGE_COMPACT_FULL_WRITE_COLD_DURATION=48h
INFLUXD_STORAGE_COMPACT_THROUGHPUT_BURST=80388608
INFLUXD_STORAGE_MAX_CONCURRENT_COMPACTIONS=1
INFLUXD_STORAGE_SERIES_FILE_MAX_CONCURRENT_SNAPSHOT_COMPACTIONS=1

INFLUXD_QUERY_MEMORY_BYTES=304857600
INFLUXD_QUERY_INITIAL_MEMORY_BYTES=10485760
INFLUXD_QUERY_CONCURRENCY=5
INFLUXD_STORAGE_CACHE_MAX_MEMORY_SIZE=1073741824
INFLUXD_STORAGE_CACHE_SNAPSHOT_MEMORY_SIZE=26214400
INFLUXD_STORAGE_WAL_MAX_WRITE_DELAY=10m
INFLUXD_STORAGE_WRITE_TIMEOUT=10s
INFLUXD_STORAGE_WAL_MAX_CONCURRENT_WRITES=6
INFLUXD_STORAGE_SERIES_ID_SET_CACHE_SIZE=0

INFLUXD_QUERY_QUEUE_SIZE=100
INFLUXD_FLUX_LOG_ENABLED=false

Кроме того в файле /etc/security/limits.conf были увеличены лимиты на файлы

root    soft    nofile  504800
root    hard    nofile  504800
*       soft    nofile  504800
*       hard    nofile  504800

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

echo session required pam_limits.so >> cat /etc/pam.d/common-session-noninteractive
echo session required pam_limits.so >> cat /etc/pam.d/common-session

И увеличены лимиты для системы:

sysctl -w vm.max_map_count=262144
sysctl -p

sysctl -w fs.file-max=800000
sysctl -p

Такая сборка позволяет работать как с InfluxDB v2 так и в режими совместимости с InfluxDB v1. Чтобы создать к backet-ам возможность подключения как к v1 базам данных, нужно выполнить в консоли команды

export INFLUX_HOST=http://influx:8086
export INFLUX_TOKEN=uA3kTOken==
export INFLUX_ORG_ID=d710abc76347d169

brew install influxdb-cli

influx v1 dbrp create \
--bucket-id 5d69c811b871b876 --db ClientMetrics --rp auto

influx v1 auth create \
--read-bucket 5d69c811b871b876 --write-bucket 5d69c811b871b876 \
--username ClientMetrics

Тут INFLUX_HOST, INFLUX_TOKEN, INFLUX_ORG_ID - параметры подключения к серверу

5d69c811b871b876 -- это bucket, а ClientMetrics -- то как будет называться база данных

Таким образом, для ускорения используются:

  • больше памяти для сервера

  • настройки GO для работы с памятью более аккуратно

  • настройки influxdb, чтобы не выделять очень много памяти на запросы

  • настройки сервера, чтобы influxdb мог работать с тысячами файлов без ошибок

И еще есть nginx, который кеширует ответы для метода query и включает поддержку CORS для influxdb, так как в самом InfluxDB v2 нет поддержки CORS вообще.

nginx.conf:

Скрытый текст
worker_processes  4;

error_log  /var/log/nginx/error.log warn;

events {
        worker_connections  1024;
}

http {
    proxy_cache_path /data/nginx/cache keys_zone=mycache:20m max_size=1g inactive=60m;
    limit_conn_zone $server_name zone=perserver:10m;

    upstream backend {
        server host.docker.internal:8086;
        keepalive 5000;
    }
    upstream victoria_metrics_1 {
        server victoria_metrics_1:8428;
        keepalive 5000;
    }

    server {
        listen 8096;
        client_max_body_size 20m;
        location /query {
            proxy_cache mycache;
            proxy_cache_key "$host$request_uri";
            proxy_cache_min_uses 1;
            proxy_cache_methods GET;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404      1m;

            proxy_cache_background_update on;
            proxy_cache_revalidate on;
            proxy_cache_lock on;
            add_header X-Cache-Status $upstream_cache_status;
            proxy_ignore_headers Cache-Control;
            proxy_hide_header 'Access-Control-Allow-Origin';
            if ($http_origin = ''){
                set $http_origin "*";
            }

            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' $http_origin;
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'Pragma,Accept,Authorization,Content-Type,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain charset=UTF-8';
                add_header 'Content-Length' 0;
                add_header 'Access-Control-Allow-Credentials' 'true';
                return 204;
            }
            if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' $http_origin;
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Credentials' 'true';
                add_header 'Access-Control-Allow-Headers' 'Pragma,Accept,Authorization,Content-Type,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                proxy_pass http://backend;
            }
            if ($request_method = 'POST') {
                add_header 'Access-Control-Allow-Origin' $http_origin;
                add_header 'Access-Control-Allow-Credentials' 'true';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'Pragma,Accept,Authorization,Content-Type,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                proxy_pass http://backend;
            }
        }

        location = /write1 {
            internal;
            proxy_pass http://victoria_metrics_1$request_uri;
        }

        location /write {
            #mirror /write1;
            proxy_pass http://backend;
        }

        location / {
            proxy_pass http://backend;
        }
    }
}

  • Потеря контекста. Инженеры по производительности могут не иметь достаточного понимания логики работы приложений и сервисов, которые они тестируют. Это может привести к неправильному планированию, моделированию и анализу тестов производительности.

Что тут имеется в виду? Почему инженерам команды продукта не хватало времени на понимание продукта, если они занимались performance-ом в команде, что им мешало узнать продукт ближе?

Спасибо за статью, Виктор! Каждый раз к ней обращаюсь, когда настраиваю профилирование

upd: написал разработчикам о проблеме и варианте решения

В этом году конференция очень нагрузочная. Докладов по нагрузке 8. А еще круглый стол по карьере в нагрузке. И не только.

Пусть и habr будет быстрее!

Когда смотрел на раздел Нагрузка https://habr.com/ru/company/jugru/blog/690438/#load, то заметил, что изображения грузятся долго. И подумал, как так, это же habr, тут же отдельный быстрый и надежный хостинг фото?

Фото Анатолия размыто
Фото Анатолия размыто
И оно размыто более 1 секунды (это снимок из результатов тестов sitespeed.io)
И оно размыто более 1 секунды (это снимок из результатов тестов sitespeed.io)

Разобрался, при загрузке изображения на сайт предоставляется ссылка:
https://habrastorage.org/getpro/habr/upload_files/5fb/14d/3b8/5fb14d3b82545757b366e83cdf6efda6.jpeg (ответ отдается за 200-300 мсек)
и она используется в публикации. Ответ на такой запрос отдается всегда без кеширования и содержит редирект на другой сайт
https://hsto.org/getpro/habr/upload_files/5fb/14d/3b8/5fb14d3b82545757b366e83cdf6efda6.jpeg (ответ за 600-700 мсек для первого ответа, и 2 мсек для ответа из кеша).
А этот ответ уже успешно кешируется

И поэтому минимальное время загрузки изображения 200-300 + 2 мсек при повторном открытии. Или 200-300 + 600-700 при первом открытии. А могло быть лишь 2 мсек почти всегда!

Убедиться в этом можно, используя инструменты разработчика вашего браузера:

1344 мсек на habrastorage.org, потом редирект и еще 966 мсек на hsto.org
1344 мсек на habrastorage.org, потом редирект и еще 966 мсек на hsto.org

К сожалению такие проблемки есть много где, и на проекте, где сам работаю - знаю о них, все будет ускорено. Искать их несложно и интересно.

Надеюсь, что заинтересовал темой. До встречи!

Кирилл, спасибо! Тут любой вопрос дорогой. Вот вы делали, в том числе эксперименты, командой 2-4 человека в течение 4 месяцев. Это 12 человеко-месяцев, мифических. Мне хочется сделать за спринт или за пару дней ))

И поэтому я думаю вообще не писать тесты пока, массово не писать. Вот есть статистика, пусть в Elastic, есть понятные способы ее обработки и мониторинга. И уже можно завести дефекты на ряд очевидных случаев.

Для неочевидных случаев, где нужно профилировать на тестовом стенде, в качестве исключения можно написать тесты. Сначала самые дешевые и простые. Потом посложнее, с параметрами, если простых не хватит.

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

Вы собираете профиль нагрузки с продуктива.

Что используется в качестве основы? Как идет работа с параметрами запросов? Есть ли что-то аналогичные property based testing, но для нагрузки в вашем подходе?

Например, (GET) /{categoryID}/positions — есть такой метод для меню доставки товаров, и вы решили его нагрузить

Если его передать в запрос вот прямо вот так:

(GET) /30/positions, то это будет запрос без фильтров и с параметрами по умолчанию

(GET) /30/positions?type=ALL&limit=100
Возвращает сначала 10 000 элементов и оставляет из них 100, у такого запроса пусть узкое место в том, что там фильтрация Limit 100 внутри сделана не средствами SQL-запроса, а средствами фильтрации коллекции ответов в памяти бекенда на Java/Kotlin

(GET) /30/positions?type=ALL&limit=100&search=Pizza
Возвращает сначала 5 элементов пиццы и оставляет из них 5 — этот запрос пусть работает всегда быстрее, чем предыдущие два варианта, потому что тут результатов мало из-за более строгой фильтрации

В тесте будут использоваться полные URL или может только первый вариант без параметров? Деперсонализация базы данных позволит использовать оригинальные ID-шки в тестовых запросах

Может быть, нужно провести полноценное обучение? Научить всех быть супер-спецами. Они будут делать красное нагрузочное тестирование, и все запоет-засияет. Обучили какой-то состав, ребята стали супер-спецами… и ушли в другую компанию, потому что там больше платят, например.

Вот тут я напрягся

Сейчас мы находимся на этапе планового внедрения его в каждую команду Самоката и проводим микрообучения. 

Вот тут отпустило

То есть обучение нужно, но пререквизитом идет стандартизация, шаблонизация, некая база?

Можно собрать k6 с дополнительным модулем xk6-browser (задача непростая) или скачать собранный. Использовать функцию setup, чтобы один раз открыть страницу и получить ее ресурсы используя Routehttps://try.playwright.tech/?e=intercept-requests.

  // Log and continue all network requests
  page.route('**', (route, request) => {
  if(request.method() == 'GET' &&
    request.url().toString().indexOf('testserver:5000/')>0) {
      console.log(request.url()); // тут копирование в список
    }
    route.continue();
  });

Положить ссылки на ресурсы в список или в map, где ключ будет базовой ссылкой, а значениями — список ссылок на связанные ресурсы. Написать два теста. Один, который загружает только базовую ссылку, пусть в 95% случаев и второй, которой имитирует загрузку с пустым кешем браузера, пусть в 5% случаев. Загрузка с пустым кешем — это загрузка всех ссылок

Это сложный способ описал. Простой может кто-то еще подскажет

Статья хорошая. Для целей отслеживания деградаций я бы порекомендовал заменить подход с нагрузкой в пользователях/потоках без пауз: startVUs: 100, target: 1000 на профиль с фиксированной нагрузкой. Так можно будет оценивать время ответа.

Если этого не сделать, то вот приложение обрабатывало запросы от 100 потоков пусть за 100 мсек и мы получали 1000 rps. Вот оно стало оптимальнее, и готово обрабатывать запросы за 50 мсек, но в него приходит нагрузка в 2000 rps и мы получаем все равно тормоза и те же 100 мсек - не видим разницы.

В подходе с пулом подключений основная метрика не http_req_duration а

http_reqs......................: 10908 1081.901504/s

Если стало ниже, чем вчера - стало хуже.

А вот если подавать постоянную нагрузку, пусть в 150 rps, например, как на продуктиве или близко, вот так

https://k6.io/docs/using-k6/scenarios/executors/constant-arrival-rate

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

Привет! А как выполняется работа с транзакциями? Понимаю, что можно явно написать в тексте SQL-запроса:

  • Commit

  • Rollback

Есть ли настройки для Autocommit, какие они по умолчанию? В коде нашел задание настройки только для Batch-операции, и она false: https://github.com/Tinkoff/gatling-jdbc-plugin/blob/v0.5.0/src/main/scala/ru/tinkoff/load/jdbc/db/JDBCClient.scala#L49

Виктор, привет! Спасибо за твою статью про JFR, ссылался на нее в выступлении.

Да, ты прав. С включением профилирования налету, а в частности, с включением Offline-профилирования есть, в дополнение, такие наблюдения.

Запускать offline-профилирование (массовое на группу сервисов) надо с задержкой не менее 1 минуты от старта JVM. Иначе JVM в Kubernets, может вообще не стартовать. Профилировать с 0-й секунды после старта JVM не получается. У меня ни разу не получилось запустить сервис с профилированием прямо со старта.

У сервиса есть readness и leavness-пробы. Пусть 10 минут. А профилирование, запущенное до ввода сервиса в балансировку, может замедлить старт сервиса и сервис стартанет только через 15 минут. А через 10 минут его перезагрузит Kubernetes, так как истечет таймаут на запуск. И чтобы такого не происходило надо или пробы увеличивать. Или оставлять в настройках профилирования самый минимум - профилирование кода семплированием потоков. Может быть сбор исключений. Может быть какую-то еще дополнительную статистику. Не больше. И запускать профилирование с таймером 15 минут от момента старта сервиса. Чтобы сервис сначала нормально стартанул. Включился в балансировку. А уже потом запустилось профилирование.

Спасибо. Я и правда называл VisualVM, как JVisualVM. Позже подготовлю для Антона список правок. Например, заметил, что когда говорил про нативную инструментацию и gcc, то получилось по статье, как про JCC.

Антон, спасибо! Здорово!

Если понадобится скрипты, например, для SJK скопировать со слайдов, то слайды тут: https://polarnik.github.io/JVM-profiling-in-Kubernetes/

Чтобы в SVG-изображениях не использовался шрифт Times New Roman, а использовался Roboto, надо скачать и поставить локально шрифт https://fonts.google.com/specimen/Roboto (ссылка в правом верхнем углу страницы - Download family по ссылке выше)

Если появятся вопросы, буду рад. Задавайте тут. Или в telegram: https://t.me/qa_load

Привет! Тестовый агент с SoapUI запускался на локальной станции, на ноутбуке?

Результат в RPS мог получиться скромным из-за запуска теста через интернет. По ряду причин:

  • исчерпание TCP-подключений, проблема TCP Time Wait в Windows также актуальна, как и в Linux

  • лимиты сетевого провайдера, иногда это троттлинг (защита от DoS со стороны абонента) иногда просто ограничение пропускной способности

  • лимиты Azure (защита от DDoS/DoS на стороне площадки)

Скорее всего первая причина

Стоит проверить рекомендации из статьи базы знаний Microsoft:

https://docs.microsoft.com/ru-ru/windows/client-management/troubleshoot-tcpip-port-exhaust

и по возможности повторить замер. Или провести замер, подавая нагрузку со станции внутри кластера.

Также некоторые инструменты не закрывают TCP-подключение к серверу, поэтому они иногда заявляют, что обгоняют по производительности инструменты которые закрывают и открывают снова.

Инструменты, которые закрывают подключения после каждого теста:

  • Apache.JMeter, но если в конфигурационный файл jmeter.properties внести настройку httpclient.reset_state_on_thread_group_iteration=false, то тоже не будет закрывать

  • Gatling, но если в настройки http-протокола добавить shareConnections , то тоже не будут, пример.

Инструменты, которые не закрывают подключения:

  • k6

  • ab

Как поступает Soap UI не знаю, и не знаю если ли у него настройки для изменения поведения.

Резюмируя: изменение инструмента или его настроек также можно повысить RPS

Что выступает агентом мониторинга, который кладет данные в Clickhouse?

Используется отправка JSON через Kafka или Graphite-интерфейс или HTTP-протокол ClickHouse?

Или это какая-то интеграция с Prometheus, Victoria, InfluxDB перекладывает метрики в ClickHouse?

Можно предположить, что вся команда отлично знает базы данных. Так как поддерживает механизм подчистки. Мой опыт такой, что поддерживать такие скрипты можно, но для отдельных микросервисов. А для всех сервисов - сложно

Другое предположение, что основные сценарии только на чтение и ничего не создают. Это интересная мысль. Пробовали в команде такое сделать однажды, но для функциональных автоматических тестов, которые ничего не создают, а проверяют, что стенд работает.

Коллеги из Перфоманс Лаб сделали хороший вариант статьи про ускорение JMeter на английском языке:


И симуляция реального пользователя для большой нагрузки (100-1000 запросов в сек) вредна и не нужна. Проблемы автора, думаю, от этой идеи фикс. Но так можно делать для малой нагрузки.


Иногда идея фикс принимает форму: нам нужна нагрузка от 100 000 пользователей, значит нам нужно 100 000 потоков в JMeter.


Иногда такую: пользователь зайдя на сайт с вероятностью 80% ищет телефон, с вероятностью 70% красный, он может уточнить свой поиск, если наше телефон,… В результате люди пишут сложный функциональный тест на JMeter/Gatling, вместо того, чтобы заниматься нагрузкой.


А на деле все проще. От нагрузки требуется создать лишь интенсивность. По возможности повторить время жизни сессии — но это можно делать отдельно, не в JMeter/Gatling/… уже, а в параллельном автотесте, который работает в 1 поток.


Раз автор хочет все сразу, то у него нагрузка малая — до 10 запросов в сек.

1
23 ...

Информация

В рейтинге
Не участвует
Откуда
Армения
Зарегистрирован
Активность

Специализация

Инженер по производительности
Ведущий
PostgreSQL
Java
Высоконагруженные системы
Kubernetes