Общие сведения
Надёжность и безопасность
Начало работы
Обзор системы
Проекты
Концепции
Компоненты
Инструкции
Ресурсы
Таймшиты
Финансы
Клиенты
Биллинг
Затраты
Отчёты и аналитика
FAQ
Типы отчётов
Использование отчётов
Группировка данных источника
Группировка данных в отчёте
Типы виджетов
Общие отчёты и шаблоны
Настройка отчёта
Экспорт отчётов
Пользовательские настройки отчёта
Вычисляемые поля
Выражения вычисляемых полей
Особые колонки отчётов с временными рядами
Использование панелей мониторинга
Публикация панелей
Фильтры источников данных
Настройка и администрирование
Типовой порядок настройки системы
Жизненные циклы и воркфлоу
On-premises
API
История изменений
Термины и определения

Методика нагрузочного тестирования Timetta с использованием JMeter

Обновлено: 01.12.2025

1. Цель

Методика предназначена для проверки производительности и устойчивости системы Timetta при работе с целевым профилем нагрузки, характерным для облачной версии приложения. Тестирование направлено на:

  • проверку отклика сервисов под нагрузкой;
  • оценку потребности в соединениях с базой данных и брокерами сообщений;
  • выявление узких мест и возможности масштабирования системы.

2. Вводные данные и профиль использования

2.1 Основные параметры системы

Параметр Значение
Максимальное число зарегистрированных пользователей 20 000
Среда развертывания Kubernetes-кластер с выделенными нодами (Node Selector / Node Affinity)
PostgreSQL Отказоустойчивый кластер Postgres Pro на отдельной ВМ
Redis Кластер на отдельной ВМ
RabbitMQ Кластер на отдельной ВМ
Сервисы Настроены с отказоустойчивостью и автоматическим failover

2.2 Профиль нагрузки

  • Коэффициент ежедневной активности (DAU/MAU): 40–50% → ~10 000 активных пользователей в день.
  • Пиковая часовая активность: 30% дневных пользователей в самый загруженный час (10:00–11:00) → ~3 330 пользователей.
  • Коэффициент одновременности: 10–15% → верхний предел одновременных пользователей ~495.

2.3 Оценка соединений с PostgreSQL

  • Пиковый показатель TPS: ~2 800 операций/сек.
  • Среднее время выполнения запроса: 250 мс, плюс 250 мс на ожидание освобождения соединения.
  • Запас на потерянные соединения: +30%
  • Запас на служебные соединения: +30
  • Итого одновременных соединений: ~1 850

3. Подготовка к тестированию

  1. Установить Apache JMeter (версии 5.6+ рекомендуется).
  2. Создать отдельный тестовый план, включая Thread Group с числом потоков, соответствующим расчетной одновременной нагрузке (~500 пользователей).
  3. Настроить HTTP Request Defaults для базового URL приложения.
  4. Настроить HTTP Header Manager с авторизацией по ApiToken.
  5. Добавить Listeners для сбора метрик (Graph Results, Summary Report, Backend Listener для Prometheus/Grafana).

4. Сценарии запросов

В тестовом плане рекомендуется создать отдельные HTTP Request Sampler для каждого из ключевых запросов. Ниже приведены целевые эндпоинты и предполагаемое количество обращений в рамках тестового цикла:

Endpoint Метод Примерное количество вызовов
/odata/TimeAllocations POST 348
/odata/Projects(id)/UpdateResourcePlan POST 31
/odata/GetClientProfile(clientType='web') GET 187
/odata/GetEntityTypes GET 187
/odata/GetNavigationItems GET 187
/odata/Notifications? $apply=filter(read eq false)/aggregate(id with countdistinct as count) GET 222
/odata/Notifications? $top=50& $orderby=created desc GET 198
/odata/GetSession? $expand=configuration/metamodel/entities GET 196
/odata/GetIndicators GET 193
/odata/StartUndoRedo POST 44
/odata/TimeSheetLines POST 108
/odata/AbortUndoRedo POST 34
/odata/ProjectTasks(id) PATCH 6
/odata/ExtendedTimeSheets? $apply=filter((timeSheet/state/code eq 'Draft' and timeSheet/dueDate lt 01.12.2025))/aggregate($count as count) GET 55
/odata/Projects(id)/UpdateResourcePlan POST 5

Примечание: числа вызовов ориентировочные, используются для моделирования нагрузки в JMeter Thread Group.

5. Настройка нагрузки в JMeter

  1. Thread Group (Группа потоков):

    • Количество потоков: 495 (одновременных пользователей).
    • Ramp-Up Period: 300–600 сек для плавного выхода на пик.
    • Loop Count: 1–10 (в зависимости от продолжительности теста).
  2. HTTP Request Defaults:

    • Протокол: HTTPS
    • Сервер: <base_url>
    • Port: 443
  3. HTTP Header Manager:

    • Authorization: ApiToken <token>
    • Content-Type: application/json (для POST/PATCH запросов)
  4. Timers:

    • Использовать Constant Timer / Gaussian Random Timer для имитации реального пользовательского поведения.
  5. Listeners:

    • Summary Report для анализа TPS, среднего времени отклика и ошибок.
    • Graph Results для визуализации нагрузки.
    • Backend Listener для передачи метрик в систему мониторинга (Prometheus/Grafana).

6. Метрики и критерии успешного теста

  • Среднее время отклика: < 500 мс для большинства GET-запросов, < 1 сек для POST/PATCH.
  • Процент ошибок: < 1% (HTTP 5xx / timeout).
  • TPS: соответствует ожидаемой нагрузке (до 2 800 TPS в пике).
  • Использование соединений с PostgreSQL: не превышает рассчитанные 1 850 одновременных соединений.
  • Нагрузка на Redis и RabbitMQ: стабильная без ошибок подключения и переполнения очередей.

7. Рекомендации

  1. Перед нагрузочным тестированием убедиться, что все сервисы запущены в отказоустойчивом режиме и настроен failover.
  2. Проводить тестирование в тестовой среде, идентичной продакшену по ресурсам.
  3. Повторять тестирование с разными сценариями и интенсивностью для выявления узких мест.
  4. После теста анализировать логи сервисов, баз данных и брокеров сообщений.
  5. В случае превышения допустимой нагрузки корректировать масштабирование нод, пул соединений и ресурсы кластеров.

Вот обновлённый пункт, с учётом твоих условий: 4 ноды Kubernetes и 1 реплика PostgreSQL, без HA для остальных компонентов, только для нагрузочного тестирования.

8. Требования к оборудованию для нагрузочного тестирования

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

Роль Кол-во vCPU (Intel Ice Lake, 100%) RAM SSD (IOPS) Макс. bandwidth (чтение / запись)
Нода кластера K8s 4 8 32 GB 300 GB, ≥10k IOPS 150 МБ/с
PostgreSQL Primary VM 1 12 64 GB 500 GB, ≥15k IOPS 200 МБ/с
PostgreSQL Replica VM 1 12 64 GB 500 GB, ≥15k IOPS 200 МБ/с
Redis VM 1 4 8 GB 50–100 GB SSD 150 МБ/с
RabbitMQ VM 1 4 8 GB 200 GB, ≥10k IOPS 150 МБ/с
Клиент JMeter (нагрузка) 1 8 16 GB 100 GB 150 МБ/с

9. Конфигурация тест-плана


<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Timetta Load Test Plan" enabled="true">
      <stringProp name="TestPlan.comments">Нагрузочный тест для Timetta с профилем нагрузки облачной версии</stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments">
        <collectionProp name="Arguments.arguments">
          <elementProp name="base_url" elementType="Argument">
            <stringProp name="Argument.name">base_url</stringProp>
            <stringProp name="Argument.value">https://your-timetta-domain.com</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="api_token" elementType="Argument">
            <stringProp name="Argument.name">api_token</stringProp>
            <stringProp name="Argument.value">YOUR_API_TOKEN</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Timetta Users Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">495</stringProp>
        <stringProp name="ThreadGroup.ramp_time">300</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
      </ThreadGroup>
      <hashTree>
        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${base_url}</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path"></stringProp>
          <stringProp name="HTTPSampler.concurrentPool">6</stringProp>
        </ConfigTestElement>
        <hashTree/>
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="Authorization" elementType="Header">
              <stringProp name="Header.name">Authorization</stringProp>
              <stringProp name="Header.value">ApiToken ${api_token}</stringProp>
            </elementProp>
            <elementProp name="Content-Type" elementType="Header">
              <stringProp name="Header.name">Content-Type</stringProp>
              <stringProp name="Header.value">application/json</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
        <hashTree/>
        <!-- Пример HTTP Request Sampler -->
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/odata/TimeAllocations POST" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain"></stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol"></stringProp>
          <stringProp name="HTTPSampler.path">/odata/TimeAllocations</stringProp>
          <stringProp name="HTTPSampler.method">POST</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
        </HTTPSamplerProxy>
        <hashTree/>
        <!-- Timer для имитации реального поведения пользователя -->
        <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Think Time Timer" enabled="true">
          <stringProp name="ConstantTimer.delay">500</stringProp>
        </ConstantTimer>
        <hashTree/>
        <!-- Listener -->
        <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
          <boolProp name="ResultCollector.error_logging">false</boolProp>
          <objProp>
            <name>saveConfig</name>
            <value class="SampleSaveConfiguration">
              <time>true</time>
              <latency>true</latency>
              <timestamp>true</timestamp>
              <success>true</success>
              <label>true</label>
              <code>true</code>
              <message>true</message>
              <threadName>true</threadName>
              <dataType>true</dataType>
              <encoding>false</encoding>
              <assertions>true</assertions>
              <subresults>true</subresults>
              <responseData>false</responseData>
              <samplerData>false</samplerData>
              <xml>true</xml>
              <fieldNames>true</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
              <responseDataOnError>false</responseDataOnError>
              <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
              <assertionsResultsToSave>0</assertionsResultsToSave>
              <bytes>true</bytes>
              <sentBytes>true</sentBytes>
              <url>true</url>
              <threadCounts>true</threadCounts>
              <idleTime>true</idleTime>
              <connectTime>true</connectTime>
            </value>
          </objProp>
        </ResultCollector>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

Предыдущая
 Сайзинг
Следующая
FAQ 

Содержание

1. Цель 2. Вводные данные и профиль использования 2.1 Основные параметры системы 2.2 Профиль нагрузки 2.3 Оценка соединений с PostgreSQL 3. Подготовка к тестированию 4. Сценарии запросов 5. Настройка нагрузки в JMeter 6. Метрики и критерии успешного теста 7. Рекомендации 8. Требования к оборудованию для нагрузочного тестирования 9. Конфигурация тест-плана
Ничего не найдено

Перейти на русскую версию?