• Grafana, InfluxDB, два тега и одна сумма. Или как посчитать сумму подгрупп?
    +1

    Хороший вопрос, спасибо.


    Да, $granularity — список констант, с интервалом времени.


    Сделан для ускорения отображения большого количества графиков, чтобы показать меньше точек. Чтобы можно было задать группировку по минутам, и получить на графике 100 точек вместо 1900 точек (при ширине монитора 1980 точек).


    Также для отладки неудачных запросов. И для возможности предсказания ожидаемого количества точек => для наличия критериев соответствия результата запроса настройкам.


    Причина — не самое производительное оборудование для станций мониторинга.


    И для обратной ситуации, чтобы иногда показать больше точек, чем поддерживает монитор. Показать на отдельном графике три месяца, но с группировкой по минутам. Это используется, чтобы найти на графике отдельный кратковременный запуск теста. Или метрики по отдельному процессу, который фиксировался однажды в момент тестирования, ссылки на временной интервал не сохранилось, и теперь надо его визуально найти. Представьте, что вы выполняли когда-то в этом году тестирование длительностью 30 минут, и вот сейчас отображаете последние 6 месяцев на графике. В этом случае для разрешения 1900 точек получается, что в каждой точке будет 6×30×24×60÷1900=136,42 минут, примерно с такой группировкой выполнится запрос при настройке auto. А если все метрики собирались всего 30 минут, и на графике отображается одна точка, то точку не видно. Некоторые графики для отображения нуждаются в двух точках, и при группировке превышающей весь набор сбора метрик вообще график не получить. Например, функции NON_NEGATIVE_DERIVATIVE(), DERIVATIVE(), NON_NEGATIVE_DIFFERENCE(), DIFFERENCE(),… в таком случае не вернут числовой результат им нужно две точки минимум.


    Причина почему из 6 месяцев сбора метрик бывает нужно найти только интервал в 30 минут — нерегулярные запуски тестов, нерегулярный сбор метрик из-за переключений между проектами. Стенд может собраться, тесты будут выполнены (30 минут). А следующий запуск тестов, через недели три, а может через месяц.


    Кстати эту проблему уже решил, на связке Grafana + InfluxDB можно сделать ссылки на конкретные участки временного ряда, и кликом по гиперссылке получить нужный интервал времени. Это интересная и полезная задача. Расскажу скоро и о ней.


    Из-за таких причин приучил себя везде использовать настраиваемый интервал времени. Иметь такую возможность.


    Если отдавать разработанную доску во внешний мир, то можно в $granularity оставить одно значение = auto, а саму переменную скрыть. Тогда у пользователей всегда будет одно значение, основанное на количестве пикселей в графике. Но пока идёт разработка, параметр $granularity полезен с разными значениями.


    The $__interval Variable


    Цитата
    The $__interval is calculated using the time range and the width of the graph (the number of pixels).

    Approximate Calculation: (from - to) / resolution

    For example, when the time range is 1 hour and the graph is full screen, then the interval might be calculated to 2m — points are grouped in 2 minute intervals. If the time range is 6 months and the graph is full screen, then the interval might be 1d (1 day) — points are grouped by day.
  • Grafana, InfluxDB, два тега и одна сумма. Или как посчитать сумму подгрупп?
    0

    Вариант 3 (не рабочий)
    Посмотрел ещё на постпроцессоры telegraf, среди них не нашел такого, который бы ещё на этапе записи метрик в InfluxDB объединил бы hostname и ip_address в одно поле.


    Вот все постпроцессоры:
    https://github.com/influxdata/telegraf#processor-plugins


    Вариант 4 (рабочий)
    Но есть вариант решения, через переменные окружения и небольшое изменение в файле конфигурации telegraf.conf. Решение в обход, ещё один пример ненормального программирования с InfluxDB.


    Можно:


    • периодически обновлять переменную окружения hostname_with_ip: export hostname_with_ip="hostname(hostname -I | awk '{print $1}')"
    • в файле telegraf.conf настроить сохранение переменной окружения hostname_with_ip в тег hostname_with_ip:
    • фильтровать метрики по готовому тегу

    Можно сделать так:


    # Global tags can be specified here in key="value" format.
    [global_tags]
      # dc = "us-east-1" # will tag all metrics with dc=us-east-1
      # rack = "1a"
      ## Environment variables can be used as tags, and throughout the config file
      # user = "$USER"
      hostname_with_ip = "$hostname_with_ip"
    
    [[inputs.exec]]
      commands = ["sh /opt/telegraf_update_hostname_with_ip.sh"]
      timeout = "60s"
      data_format = "influx"

    Можно выполнение export hostname_with_ip="hostname(hostname -I | awk '{print $1}')" поместить куда-нибудь в /etc/profile.d/telegraf_update_hostname_with_ip.sh
    тогда не надо будет делать секцию [[inputs.exec]]


    И просто фильтруем по готовой переменной.

  • Grafana, InfluxDB, два тега и одна сумма. Или как посчитать сумму подгрупп?
    0

    В комментарии выше всё же описал Вариант 1. Там предположил, что подобная конструкция будет работать. Позже прочел ваш amarao комментарий, про сложение, и понял, что Вариант 1 не получится реализовать. Строки не комбинируются в InfluxDB 1.7, в InfluxDB 2.0, возможно комбинируются.

  • Grafana, InfluxDB, два тега и одна сумма. Или как посчитать сумму подгрупп?
    0

    Да, подзапросы есть. Да, за счёт подзапросов можно преобразовать теги в поля. Но у hostname и ip тип — String, а операция + не работает для строк в InfluxDB.

  • Grafana, InfluxDB, два тега и одна сумма. Или как посчитать сумму подгрупп?
    0

    Понял — задача со звёздочкой.
    Вариант 1
    Вывести в выпадающем списке комбинацию hostname (ip), и по выбранным значениям успешно фильтровать метрики по полям hostname или ip.


    Можно попробовать решить с помощью магии регулярных выражений. Сформировав задачу так: как сформировать регулярное выражение с опциональной частью?


    Чтобы выражение вида


    performanceAgent (10.11.100.111)*

    применилось к списку серверов:


    performanceAgent
    databaseServer
    applicationServer
    moniringServer

    И оставался только один результат: performanceAgent


    Да, такое можно попробовать сделать. Ещё не пробовал.


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


    Документация на такой способ есть в самом интерфейсе Grafana:



    Specify an URL (relative or absolute)
    Use special variables to specify cell values:
    ${__cell} refers to current cell value
    ${__cell_n} refers to Nth column value in current row. Column indexes are started from 0. For instance,
    ${__cell_1} refers to second column's value.
    ${__cell:raw} syntax. By default values are URI encoded. If the value is a complete URL you can disable all encoding using


    Пример такой фильтрации подготовил в докладе для Heisenbug, конференцию пока перенесли. Если достаточно одного слайда, то он такой:

    Конкретно для случая server, ip пример можно подготовить, но позже.

  • Grafana, InfluxDB, два тега и одна сумма. Или как посчитать сумму подгрупп?
    0
    Пока не сталкивался. Но если задача — вывод hostname и ip в наименовании серии, то можно сделать группировку метрик по двум тегам сразу:

    USE telegraf;
    SELECT mean("usage")
    FROM "cpu"
    WHERE
        hostname =~ /^${hostname:regex}$/ AND
        $timeFilter
    GROUP BY
        "hostname", "ip", time($granularity)

    А в поле ALIAS BY написать:
    $tag_hostname ($tag_ip) mean cpu


    Это как в самом первом запросе статьи в разделе «Как всё начиналось», только теги называться будут не Type и name, а hostname и ip.

    Чтобы вывести в ALIAS BY переменную, можно использовать $tag_{Имя тега} (только такого тега, по которому есть группировка) или $col — имя поля. И ради вывода значения тега в подписи к серии, просто добавляем группировку значений по этому тегу: hostname, а если по два тега нужно вывести, то группируем по двум тегам: hostname и ip.

    Верно понял задачу? Или нужно выводить ip в другом месте: в выпадающем списке, таблице?
  • Load Testing Meetup в Райффайзенбанке
    +1
    Слайды и видео трёх докладов Load Testing MeetUp:

    1. Программирование с Grafana и InfluxDB. Смирнов Вячеслав (polarnik), Райффайзенбанк.
    Видео.
    2. Автоматизация НТ. Дёшево и больно. Юрков Кирилл (login40k), Билайн.
    Видео.
    3. Case Study дефектов производительности: DeadLock, Race Condition, Memory Leak. Филимонов Сергей, EPAM
    Видео.

    Если у вас есть тема для выступления, то напишите мне (polarnik) или заполните форму заявки на доклад.
    И присоединяйтесь к сообществу QA — Load & Performance
  • Нагрузочное тестирование веб-проекта — без купюр
    0
    Не замерял никогда ещё.
    Изучил вопрос немного.

    Получается, что Java по умолчанию хранит кеш успешных адресов с момент старта до момента завершения. А кеш неуспешных хранит 10 сек.
    $JAVA_HOME/jre/lib/security/java.security
    #
    # The Java-level namelookup cache policy for successful lookups:
    #
    # any negative value: caching forever
    # any positive value: the number of seconds to cache an address for
    # zero: do not cache
    #
    # default value is forever (FOREVER). For security reasons, this
    # caching is made forever when a security manager is set. When a security
    # manager is not set, the default behavior in this implementation
    # is to cache for 30 seconds.
    #
    # NOTE: setting this to anything other than the default value can have
    # serious security implications. Do not set it unless
    # you are sure you are not exposed to DNS spoofing attack.
    #
    #networkaddress.cache.ttl=-1

    # The Java-level namelookup cache policy for failed lookups:
    #
    # any negative value: cache forever
    # any positive value: the number of seconds to cache negative lookup results
    # zero: do not cache
    #
    # In some Microsoft Windows networking environments that employ
    # the WINS name service in addition to DNS, name service lookups
    # that fail may take a noticeably long time to return (approx. 5 seconds).
    # For this reason the default caching policy is to maintain these
    # results for 10 seconds.
    #
    #

    networkaddress.cache.negative.ttl=10



    В файле system.properties эти настройки не переопределены.
    apache-jmeter-5.1/bin/system.properties
    # Java networking-related properties
    #
    # For details of Oracle Java network properties, see for example:
    # http://download.oracle.com/javase/1.5.0/docs/guide/net/properties.html
    #
    #java.net.preferIPv4Stack=false
    #java.net.preferIPv6Addresses=false
    #networkaddress.cache.ttl=-1
    #networkaddress.cache.negative.ttl=10



    Получается, что DNS Cache Manager не нужен для ускорения. А может он понадобиться наоборот, чтобы сбрасывать кеш.



    А доля DNS Lookup, если сервер верно настроен, будет небольшой.
    Но был случай, он помог. Теперь не могу объяснить почему. Спасибо за вопрос.
  • Нагрузочное тестирование веб-проекта — без купюр
    0
    Цитата из статьи
    Коню ясно, что нельзя при нагрузочном тестировании дергать только одну детальную страницу каталога, поэтому полезно считывать и ротировать их список из CSV-файла:
    image

    image



    Так как domain_name принимает значение от 1 до 250. А domain_l1_name ещё несколько значений, пусть 2. То всего имён серверов будет 250 * 2 = 500.
    В статье нагрузка подаётся на 500 серверов. Они принимают нагрузку. Судя по тексту статьи
    Кстати да, мы не осветили этот момент. Из банальных причин обычно всплывает отсутствие балансировки между nginx — apache — mysql воркерами.

    нагрузку принимают nginx-узлы.

    Отвечая на вопрос
    уточните, пож-та, какой сервер Вы имели ввиду, если указаны несколько в многозвенной системе: proxy, web, app, db (возможно ещё balancer, а в отказоустойчивой схеме и т.д.)?

    имел в виду nginx, предполагая, что нагрузка подаётся на него. Он физически может быть один (один сервер), но обслуживать 250 или 500 доменных имён или даже 1000. Правила перенаправления позволяют написать любую конфигурацию.

    Конфигурация необычная. Поэтому и задал свой вопрос автору. Автор ответил, что серверов намного меньше, чем 250.

    и какая суть разницы фигачить соответствующей нагрузочной интенсивности поток запросов на 1 (или 1 сбалансированный отказоустойчивый контур многозвенки) или 250+?

    Разница в мелких деталях. Так подавая нагрузку на 250, 500, 1000 доменных имён. И не включив кеширование DNS ответов на JMeter. Мы будем активнее опрашивать DNS-сервер, на котором будет немалая таблица адресов. Он будет испытывать нагрузку и может стать узким местом. На что обратил внимание автора.
    В случае 1 доменного имени ситуация упрощается, нагрузка на DNS меньшая.

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

    Ускоряем Apache.JMeter:

    Тут детали почему так происходит, и как можно настроить нагрузочную станцию.
  • Нагрузочное тестирование веб-проекта — без купюр
    +1
    vladimirsitnikov привет. Прорецензируй пожалуйста комментарий. Верно ведь указал сферу применения таймера Precise Throughput Timer и его параметры?
  • Нагрузочное тестирование веб-проекта — без купюр
    +1
    А всё-таки, на сколько серверов подаётся нагрузка? На 250+?
  • Нагрузочное тестирование веб-проекта — без купюр
    +2
    Понял подход. Можно гибче сделать. С одной катушкой и Throughput Controller-ами для блоков.

    Тогда чтобы поднять нагрузку достаточно просто увеличить количество потоков в одной катушке и ничего не придётся пересчитывать в Excel. Это количество потоков удобно передавать в скрипт из вне. Взяв в качестве значения не константу 20, а property
    ${__P(NUM_THREADS, 20)}, которое уже задавать в параметрах запуска скрипта:


    А чтобы не считать матожидания и, главное, не пересчитывать их в Excel при модификации скрипта. Можно просто положиться, что запрос выполняется не более 5 сек. И это значение можно сразу выставить в

    HTTP Request Defaults: Response Timeout (4000) и Connect Timeout (1000)

    И зная, что каждый запрос не более 5 сек. Рассчитать с округлением длительность всего сценария по верхней планке: пусть 50 запросов, значит 50*5=250 сек, а если с запасом, то 300 сек.

    Чтобы не валить сервер, когда время отклика превысит 5 сек на запрос. Стоит поменять в катушке параметр Action to be taken after a Sampler error со значения Continue на значение Start Next Thread Loop.

    После чего можно выставить шаг нагрузки. В 300 сек (6 минут). Используя Constant Througthput Timer:
    • Target Throughput (1/6): 0.1667
    • Calculate Throughput based on: this thread only

    Посчитаем сколько запросов будет за сценарий:
    • Авторизация — 10 запросов — вероятность 1.0
    • Главная — 30 запросов — вероятность 1.0
    • Календарь — 20 запросов — вероятность 0.2
    • Блог — 70 запросов — вероятность 0.1
    • Файлы — 15 запросов — вероятность 0.05


    Получилось, примерно:

    10 * 1.0 + 30 * 1.0 + 20 * 0.2 + 70 * 0.1 + 15 * 0.05 = 51,75

    А количество потоков в катушке рассчитать так, чтобы получился требуемый 1 млн запросов в 24 часа:
    • 1 000 000 / 51,75 запросов / 24 часа / 60 минут == 13,419216318 запусков сценария в минуту нужно сделать для интенсивности 1 млн в день
    • 0.1667 запусков сценария в минуту делается сейчас одним потоком
    • значит 13,419216318 / 0.1667 == 80,499198068 ~ 81 поток нужен


    Примерный размер пула потоков мы нашли.

    Так как значение 81 не очень большое, не 10к. То можно доверить финальный расчёт интенсивности умным таймерам. Добавив в пул не менее 81 поток. Например для постоянной нагрузки хорош точный таймер: Precise Throughput Timer.

    Настроек у него немало. Но нам в общем понадобятся такие:
    • Target throughput (in samples per 'throughput period'): 1000000
    • Throughput period (seconds): 86400
    • Test duration (seconds): 86400

    А в катушке нужно будет по прежнему задать не менее 81 поток, но плюс в том, что можно задать и больше. Например, 500. Таймер умный, и использует столько, сколько надо, чтобы получилась интенсивность 1 000 000 в сутки. А когда пул взят с запасом. То можно долгое время не пересчитывать нагрузку в Excel. Просто дорабатывать скрипт, добавлять в него запросы.

    Про паузы. Precise Throughput Timer сам сделает паузы между запросами. Может сделать их случайными. И добавлять паузы искусственно не придётся.

    В теории это так работает. Но, конечно, надо всё проверять.
  • Нагрузочное тестирование веб-проекта — без купюр
    +2
    Спасибо за статью. Очень понравился раздел «Интерпретация результатов нагрузочного тестирования», где отмечена важность анализа логов.

    Технический вопрос про bitrix24. В скрипте нагрузка подаётся минимум на 250 серверов
    load[1...250].bitrix24.[строки из csv-файла]
    По факту, думаю, речь идёт про 500, 1000 серверов.
    Это большая ферма.

    Скриншот, где показан профиль нагрузки в 20 потоков и скриншот, где показан один график утилизации CPU, DISK,… тут показаны для примера? А реальная нагрузка подаётся с такой же большой фермы генераторов нагрузки (100 штук)?

    Ещё. При таком большом количестве доменных имён есть смысл использовать DNS Cache Manager. Если интенсивнсть большая. Ведь по умолчанию JMeter не кеширует DNS-ответы, а клиенты (браузеры) кешируют. И на практике, это однажды ограничило нагрузку. Что стало сюрпризом. Ведь редко кто мониторит и DNS-сервер во время нагрузки.
  • Нагрузочное тестирование веб-проекта — без купюр
    +1
    Согласен, искусственные паузы между запросами и стремление к реальным пользователям за счет таких пауз увеличивают шаг нагрузки. Что и потребуют больше потоков при наличии требования к заданной интенсивности. Что часто приводит к ситуации 10к тредов.

    Но у автора 20 тредов. Закрытая модель нагрузки. Фактически, интенсивность он напрямую не контролирует. А раз так, то можно добавлять любые паузы.

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

    image


    Минус отсутствия контроля за интенсивностью является то, что первоначальное требование
    За сутки сделан 1 млн. хитов

    проверить уже невозможно. Будет сделано столько хитов, сколько выдадут 20 пользователей, которые работают в течение 86400 сек (24 часа). И тут может получиться, как 10 млн, так и 100 тысяч. Зависит от времени отклика системы и длительности пауз.
  • Нагрузочное тестирование веб-проекта — без купюр
    +2
    Отличный awk-скрипт. Тоже их люблю.

    В дополнение к разделу «Планирование распределения нагрузки» добавлю технических деталей. Для того, чтобы сымитировать «реальных» пользователей хорошо учесть, что они выполняют действия с разной вероятностью.

    Например, в статье есть действия:
    1. Авторизация
    2. Главная
    3. Задачи
    4. Календарь
    5. Файлы
    6. Фотографии
    7. ...


    Для стандартного формата access.log ngxin:
    стандартный формат
    log_format combined '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent"';


    где поле '"$request" обрамлено двойными кавычками, поэтому если в качестве разделителя использовать двойную кавычку, то значение поля $request будет после первой и до второй двойной кавычки. То есть $request будет $2 элементом после разделения строки.

    Пример:
    127.0.0.1 - - [16/Jun/2019:21:30:16 +0300] "GET /icons/ubuntu-logo.png HTTP/1.1" 404 152 "http://localhost/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0"



    Можно использовать такой скрипт, чтобы посмотреть распределение операций:

    (cat /var/log/nginx/access.log && cat /var/log/nginx/access.log.1 && zcat -qa /var/log/nginx/access.log.*.gz ) | awk -F '"' '{a[$2]++}END{n=asort(a,b);aMax=b[n];for(i in a){print a[i] " --- " (a[i] / aMax) " --- "  i}}' | sort -nr


    Статистика, нормированная по самой частой операции. Как правило, самая частая операция — главная и авторизация.

    И по полученной статистике вида
    11 --- 1 --- GET /icons/ubuntu-logo.png HTTP/1.1
    11 --- 1 --- GET / HTTP/1.1
    1 --- 0.0909091 --- GET /favicon.ico HTTP/1.1


    Настроить, используя компонент Throughput Controller, вероятности для блоков. И получится, что «Авторизация» и «Главная» имеют вероятность 1.0, а «Фотографии» и «Блог» имеют вероятность 0.0909091.
  • Нагрузочное тестирование игры с парой сотен тысяч виртуальных пользователей
    +2
    Спасибо, Герман. Интересные приёмы используешь, возьму себе:

    • вставка "%" для обозначения Throughput Controller;
    • циклы по переменным в Groovy-стиле:
      vars.entrySet()
      	.collect { it.key }
      	.grep { it.startsWith('can_') }
      	.each vars.&remove
    • передача функций через vars:
      vars.putObject('updateGrid', this.&updateGrid)
    • скрытый JSR223 через Flow Control Action + JSR223 PreProcessor;
    • метрики для определения качества autovacuum.


    Не знаю, чем JSR223 Assertion лучше JSR223 PostProcessor. Проверить надо. Может и нет разницы.

    У меня бы терпения не хватило столько JSR223 отлаживать. Их же постоянно править приходится. Поэтому все их выношу во внешние файлы. И пишу код в IDEA, так удобнее отслеживать версии, быстрее делать рефакторинг, искать код.

    А вот этот код меня смущает:
    // Portal Timer
    def local = ${THREAD_COUNT};
    def total = ${THREAD_COUNT_TOTAL};
    ...

    Сейчас специально замерю, но думаю, что вот такой вариант будет быстрее, за счёт неизменности groovy-кода, и отсутствия даже возможности лишней перекомпиляции:
    // Portal Timer
    def local = vars.get('THREAD_COUNT');
    def total = vars.get('THREAD_COUNT_TOTAL');
    ...


    В Timing PostProcessor код не является потокобезопасным, так как props — это просто HashMap, нет блокировок, между моментами get и put данные изменятся несколько раз:
    int samples = 5;
    
    double oldAvg = props.get('portalRespAvg');
    double newAvg = ((oldAvg * (samples-1)) + prev.getTime()) / samples
    
    props.put('portalRespAvg', newAvg);

    Но тут не знаю, что предложить взамен. Не писал таймеры.
  • Оценка сроков на разработку и тестирование задачи (не нужна)
    +1
    Спасибо за ответ. Ощущение, что в статье что-то не рассказано, уменьшилось.

    Здорово, что тесты не мигают. Важна история, что тестов много и на их поддержку уходит много времени. Как понимаю, это основная работа, значит команда — команда автоматизаторов. Редкая.

    Также важны люди. Фактором успеха может быть конкретная команда из 6-ти человек, которая достаточно квалифицирована, слажена, стабильна, дружна. И в такой команде любой метод работы, особенно тот, где полное доверие и нет микроменеджмента даст просто прекрасный результат.

    И если две такие команды встретятся и начнут обсуждать, что важнее в их работе. Одна будет говорить: «У нас нет сроков», — а вторая отвечать: «А у нас есть сроки, и всё просто замечательно». И обе правду говорят :). Значит критерий оценки не самый точный.

    Работаю в команде, про которую ещё статей не написано. Команды гибкие. Ближе всего к команде из трёх человек. Мы просто делаем работу, делаем хорошо. Иногда мы обсуждаем сроки и приоритеты. Но это в те моменты, когда работы свалилось по 10+ задач на человека. А когда горизонт планирования 1-2 задачи на человека, а все остальные пока не важны, то обсуждения приоритетов нет.
  • Оценка сроков на разработку и тестирование задачи (не нужна)
    0
    Как правило, ответом на вопрос «успеем ли мы к заданному сроку» является аналитика и MVP, качественная инфраструктура разработки и размер технического долга, а именно сложность проведения рефакторинга и наличие автоматических тестов на регрессию.

    Сколько тысяч автоматических тестов на поддержке сейчас? И сколько человек из 150 заняты этой задачей? Мигают ли они, сколько там технического долга?

    Это вопросы с продолжением. Продолжение в том, что инфраструктура и автотесты не даются бесплатно. Если они работают всегда, без долгов, возможно, кто-то из команды с работы не выходит
  • IBM MQ и JMeter: Первый контакт
    +1
    Столкнулся с проблемой во всех реализациях работы с IBM MQ из JMeter (5.0):

    Дублирующиеся client ID при работе JMS Point-to-Point и других компонентов JMeter
    Основная ошибка в checkDuplicateClientID из com.ibm.mq.allclient-9.0.0.5.jar:
    ERROR o.a.j.p.j.s.JMSSampler: JMSCC0111: IBM MQ classes for JMS attempted to set a pre-existing client ID on a Connection or JMSContext.
    com.ibm.msg.client.jms.DetailedInvalidClientIDException: JMSCC0111: IBM MQ classes for JMS attempted to set a pre-existing client ID on a Connection or JMSContext.
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_191]
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_191]
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_191]
      at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_191]
      at com.ibm.msg.client.commonservices.j2se.NLSServices.createException(NLSServices.java:319) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.commonservices.nls.NLSServices.createException(NLSServices.java:226) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.jms.internal.JmsErrorUtils.createException(JmsErrorUtils.java:126) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.jms.internal.JmsConnectionImpl.checkDuplicateClientID(JmsConnectionImpl.java:304) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.jms.internal.JmsConnectionImpl.<init>(JmsConnectionImpl.java:252) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.jms.internal.JmsQueueConnectionImpl.<init>(JmsQueueConnectionImpl.java:67) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl._createConnection(JmsConnectionFactoryImpl.java:276) ~[com.ibm.mq.allclient]
      at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl.createConnection(JmsConnectionFactoryImpl.java:236) ~[com.ibm.mq.allclient]
      at com.ibm.mq.jms.MQConnectionFactory.createCommonConnection(MQConnectionFactory.java:6016) ~[com.ibm.mq.allclient]
      at com.ibm.mq.jms.MQQueueConnectionFactory.createQueueConnection(MQQueueConnectionFactory.java:111) ~[com.ibm.mq.allclient]
      at org.apache.jmeter.protocol.jms.sampler.JMSSampler.threadStarted(JMSSampler.java:664) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread$ThreadListenerTraverser.addNode(JMeterThread.java:762) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at org.apache.jorphan.collections.HashTree.traverseInto(HashTree.java:994) [jorphan-5.0.jar:5.0 r1840935]
      at org.apache.jorphan.collections.HashTree.traverseInto(HashTree.java:995) [jorphan-5.0.jar:5.0 r1840935]
      at org.apache.jorphan.collections.HashTree.traverseInto(HashTree.java:995) [jorphan-5.0.jar:5.0 r1840935]
      at org.apache.jorphan.collections.HashTree.traverse(HashTree.java:977) [jorphan-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.threadStarted(JMeterThread.java:730) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.initRun(JMeterThread.java:718) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:249) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
    


    Наведённая ошибка уже при работе с очередями:
    ERROR o.a.j.p.j.s.JMSSampler: Error browsing queue queue://ManagerName/QName with selector  and configured timeout 20
    java.lang.NullPointerException: null
      at org.apache.jmeter.protocol.jms.sampler.JMSSampler.browseQueueForConsumption(JMSSampler.java:331) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.protocol.jms.sampler.JMSSampler.handleRead(JMSSampler.java:271) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.protocol.jms.sampler.JMSSampler.sample(JMSSampler.java:204) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:622) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:546) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:486) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:253) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
      at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
    

    При любых таймаутах и настройках.



    Происходит например в строке
    github.com/apache/jmeter/blob/v5_0/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/JMSSampler.java#L331
    consumer = session.createReceiver(queue, jmsSelector);


    Причина — если для фабрики соединений указать значение для Общие/Идентификатор клиента отличное от пустого. То в результате получается такая ошибка.

    Исправление — не заполнять поле. В текущей статье, про него не написано, и это правильно.
  • Обход ReCaptcha в Selenium тестах
    0
    Здравствуйте, Михаил. Хорошая тема.
    В тестировании производительности использую более простые на мой взгляд способы.

    Шаг 1. I'd like to run automated tests with reCAPTCHA. What should I do?
    Site key: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
    Secret key: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
    Эти настройки указываются в конфигурационном файле сервера приложений.

    Шаг 2. Настройка Fiddler и работы приложений через прокси-сервер. Так как сервер приложений всё равно будет отправлять запросы в Google, а Google для такой отладочной связки Site key + Secret key будет всегда говорить — ok. Можно автоматизировать процесс, сделать так чтобы сервер приложений работал с Google через прокси-сервер. А в прокси-сервере (использую Fidder) настроить автоответ на запрос.

    2.1. Сохранить оригинальный ответ от Google ReCapcha.
    2.2. Используя auto response в Fiddler сделать так, чтобы на все запросы к Google ReCapcha был фиксированный положительный ответ.

    В ответе будет сказано, что результат проверки — ok. Но будет сказано, что время ответа было в прошлом. Если сервер приложений валидирует не только ответ true/false, но и время. То автоответ не сработает. Но, обычно, сервер приложений время не валидирует, что позволяет сделать замкнутую систему.

    Таким образом, получаю полностью автономную систему. Которая не обращается в Интернет. И на которую можно подавать высокую нагрузку. Проверял на интенсивности до 50 сценариев в сек.
  • ASH Viewer: перезагрузка
    0
    Спасибо за проект! Использую версию с sf.net: sourceforge.net/projects/ashv и очень доволен. Коллеги тоже.

    Были сложности при подключении — jdbc драйвер был старым. А новый не загружался из lib. Коллега разобрался, как и что заменить. С тех пор всё работает.

    Буду знать, что есть новая версия
  • JMeter: забудьте про BeanShell Sampler
    0
    Ещё заметил особенность загрузки SharedHashMap в JMeter 5.0. Возможно, это дефект загрузчика компонент. Если к классе SharedHashMap только статические методы, то методы класса нельзя вызывать из JSR-223 Sampler. И чтобы класс считался классом, добавил в него нестатический метод test(). Что будет делать этот метод не важно, важно, чтобы он был.

    Код можно написать так:
    import java.util.concurrent.ConcurrentHashMap;
    
    public class SharedHashMap
    {
    	private static final ConcurrentHashMap instance = new ConcurrentHashMap();
    
    	public static ConcurrentHashMap GetInstance()
    	{
    		return instance;
    	}
    
    	public String test() {
                    return "SharedHashMap";
            }
    }


    Не исследовал пока почему так происходит. Поисследую
  • JMeter: забудьте про BeanShell Sampler
    0

    Отличная статья, Владимир. Много лет уже использую знания о том, что Groovy производительнее, чем некешируемый BeanShell.


    А об удобстве SharedHashMap для обмена данными между потоками узнал от друга aklexel. Сам упустил этот момент, когда статью читал. Оставлю тут ссылки, чтобы другим пригодились, и сам не забыл.


    О практике применения связанной работы потоков в Apache.JMeter и передачи параметров между ними можно отдельную статью написать:


    • через объект ConcurrentHashMap: put + get
    • через связку объектов LinkedBlockingQueue: put + take или put + pool и ConcurrentHashMap — хеш-таблица с очередями
    • возможно учитывая параметры ConcurentHashMap для повышения эффективности ( Как работает ConcurrentHashMap )
    • или используя другие комбинации: Обзор java.util.concurrent.*
    • про подготовку параметров в SharedHashMap на этапе работы setUp Thread Group + JSR223 Sampler
    • про проверку на наличие оставшихся в SharedHashMap данных на момент завершения теста в tearDown Thread Group + JSR223 Sampler, где используя объект SampleResult можно сгенерировать дочерние объекты с ошибками — невостребованными тестовыми данными (начиная с JMeter 5.0 подзапросы тоже попадают в csv-лог и отчёты, как самостоятельные сущности, поэтому не пропадут)
  • IBM MQ и JMeter: Первый контакт
    +2
    При подключении библиотек использую комбо-набор из:
    • com.ibm.mq.allclient.jar — 8,2 МБайт
    • fscontext.jar — 22,8 кБайт
    • jms.jar — 58,3 кБайт
    • providerutil.jar — 77,1 кБайт

    Архив allclient содержит в себе многие отдельные компоненты. Проверил — это минимальный набор, необходимый для работы с IBM MQ из Apache.JMeter. Он короче набора, указанного в статье за счёт большого allclient.jar.

    Пришел к нему из ответа Jmeter JMS point to point to connect IBM MQ error ( java.lang.IllegalStateException: QueueConnectionFactory expected, but got com.ibm.mq.jms со stackoverflow.

    Библиотеки из ответа на stackoverflow ...
    I believe you don't need that many jars, you should download the relevant 8.x.x.x-WS-MQ-Install-Java-All.jar package from the Fix Central and come up with libraries like:

    com.ibm.mq.allclient.jar
    com.ibm.mq.traceControl.jar
    fscontext.jar
    jms.jar
    JSON4J.jar
    providerutil.jar


    Провалидировал. На сколько помню, com.ibm.mq.traceControl.jar есть в allclient.jar или просто не всегда нужная библиотека — активируется только в режиме трассировки, который редко используется для JMeter, ведь в этом режиме весь трафик между JMeter и IBM MQ пишется в файл, и тест становится медленным. А библиотека JSON4J.jar не нужна — не нашел её в последних сборках IBM MQ, и без неё работает тест.

    Удобно, что в статье и библиотеки приведены. Одно пожелание — чтобы быстрее находили, не прятать их. Пусть список длинный, но будет виден.
  • IBM MQ и JMeter: Первый контакт
    +1
    Тоже оценил, что в JMS Publisher + JMS Subscriber есть разные форматы, есть возможность передать Object, который сериализуется в XML самостоятельно, прочитать файл — удобно. Думаю изучить эту связку.
    Ещё раз спасибо
  • IBM MQ и JMeter: Первый контакт
    +2
    Предполагая, что JMS Publiser и JMS Subscriber предназначены только для топиков, а не для очередей. Что предположил после просмотра скриншота в документации на Publisher:
    JMS Publiser: dinamicTopics/myStaticTopic1
    dinamicTopics/myStaticTopic1
    указано:
    dinamicTopics/myStaticTopic1


    И не читая саму документацию :). Не рассматривал связку Publisher+Subscriber для очередей.
    А просто использовал для отправки и получения сообщений из IBM MQ компонент JMS Point-to-Point, который с очередями работает — проверено.

    Есть ли отличие между
    • JMS Publisher + JMS Subscriber
    • JMS Point-to-Point (request_only) + JMS Point-to-Point (read)

    в удобстве использования?

    Статья отличная, спасибо!
  • [лонгрид] 20 лет программистской карьеры в большом маленьком городе
    0

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


    С другой стороны, в Ижевске умеют считать деньги, оптимизировать интервал работы транспорта. И там в троллейбусах и трамваях свободно — они помедленнее ходят, а в автобусах и маршрутках плотно — они ходят реже. Интервал подобран под поток пассажиров. Поток меньше — расписание реже.
    Фактор этот важен — частота работы транспорта в Москве выше, чем в небольших городах. Интервал работы метро и автобусов центральных маршрутов небольшой — минуты (2 минуты для метро). И пропустить автобус, уехав на следующем свободном, в Москве стоит 5 минут, а не 15.


    А чего не много в Ижевске, так это ночных маршрутов. Водил там машину во многом из-за того, что в 22:00 транспортное сообщение с моим домом прекращалось. Зашёл после работы в гости, и потом остаёшься или вызываешь такси. В Москве оно прекращается где-то в 01:30. Вход в метро закрывается в 01:00, переходы чуть позже, автобусы ещё позже. Но это важно не каждому.

  • Управление мощностями: в поисках идеального баланса
    0
    Нашел ответ на свой вопрос в более ранней публикации: Работа с потоком логов в реальном времени с помощью Heka. Опыт Яндекс.Денег. Heka отправляет данные в ElasticSearch в реальном времени
  • Управление мощностями: в поисках идеального баланса
    +1
    И более простой вопрос. Про Concurrency Thread Group, который приведён на картинке. Таймеры используются вместе с этой катушкой? Throughput Shaping Timer можно использовать при наличии в сценарии транзакций, группирующих запросы?

    Не использовал такую связку ранее, так как не мог понять, как считать для неё параметры. Инструкция по Throughput Shaping Timer огромна. И смущает, что надо задать RPS. В текущей статье описывается пример, когда нет транзакций, только запросы. Поэтому счётчик RPS будет точным. А у меня в работе много транзакций, и предполагаю транзакции дадут лишние (но на деле не выполняющиеся) запросы, завысив RPS. Поэтому и решил, что мне Throughput Shaping Timer не подойдёт.

    И поэтому использую надёжную простую связку Utlimate Thread Group + Test Action + Constant Throughput Timer / Precise Throughput Timer. Опираясь на шаг для транзакций.

    Или тут открытая модель нагрузки. Без таймеров, только количество потоков через Concurrency Thread Group. И все расчёты будут точны, если опираться на медианное значение времени отклика на проде?
  • Управление мощностями: в поисках идеального баланса
    +1
    Спасибо за статью.
    С помощью Grafana мы проанализировали пользовательскую интенсивность в приложении за несколько месяцев. За единицу измерения решили взять суммарное процессорное время выполнения запросов, то есть учитывались количество запросов и время их выполнения.

    Удивительно простая метрика. Это разовый ручной анализ или такая метрика собирается постоянно?

    Если постоянно, то правильно ли я понял, что раз Grafana, а не Kibana даёт метрику, то вероятно базой метрик тут выступает не ElasticSearch, а что-то ещё, возможно, Graphite / InfluxDB. А раз так, и выше упомянута Heka, то Heka анализирует логи балансировщика нагрузки, складывает их в Graphite / InfluxDB, а далее Grafana визуализирует метрику (Количество * Длительность)?
    Почему спрашиваю. Потому что в рассказах коллег из других компаний они используют для анализа времени обработки запросов например такую связку: HP Real User Monitoring + ClickHouse + Grafana. И говорят, что такая связка работает на предельных оборотах. Какие технологии у Вас справляются с анализом пользовательской активности?
  • Производительность торговой платформы на простом примере
    0
    Интересная статья, спасибо. Аналогии может и хороши, но их недостаточно, мне — человеку не погруженному в тему RDMA, DPDK,… чтобы понять их суть и пользу. Можно ли попросить дополнить статью ссылками на полезные материалы по указанным технологиям. Вероятно, ссылки от погруженного в тему человека будет полезнее, чем ссылки от поисковой системы.

    Я нашел такие ссылки:

    Первая статья хороша, интересные рекомендации — Optimizing Computer Applications for Latency: Part 1: Configuring the Hardware.

    Возможно, ссылки были в оригинале. Но удалились по каким-то причинам.

    И какая технология по опыту даёт наибольшее ускорение для торговых платформ? Если верить аналогиям, то профилирование кода с помощью Intel VTune TM Amplifier и Intel Processor Trace technology с последующим его ускорением (замена обычных работников на Усейнов Болтов) даёт наибольший выигрыш с 5-ти до 2-х минут. Как оно на практике?
  • [лонгрид] 20 лет программистской карьеры в большом маленьком городе
    +4
    Хорошая у вас память, по годам помните. Когда вспоминаю Ижевск, то вижу большие тихие кабинеты НПО. В Москве такой тишины не хватает, тут плотнее.

    А про автомобиль (это из заключительной скрытой части) такое мнение, обратное — в Ижевске машина мне была нужна. Для удовольствия, просто потому, что её приятно водить. В Москве машина уже не столь нужна, расходов больше. Метро и другой общественный транспорт удобнее.
  • Экспорт дерева тестов из JMeter в текст
    0
    Привет, rahna.
    Могу посоветовать попробовать YAML-синтаксис. Он не будет уступать текстовому представлению по понятности. И кроме того, тесты в таком представлении можно будет сразу же запускать, используя taurus.

    gettaurus.org/docs/JMX2YAML
  • Cucumber в облаке: использование BDD-сценариев для нагрузочного тестирования продукта
    0

    Здравствуйте. Интересная статья. Коллеги на одном из проектов пробовали сделать такое. Cucumber для нагрузки. Доступность облаков будила фантазию. Но не сделали — это не для всех задач подходит.


    На другом проекте также использовали функциональные тесты, уже успешно и регулярно. Но не для нагрузки в широком смысле понимания, а как индикатор появления узких мест после внесения небольшой изменений в проект. Думаю, как и у Антона Косякина. Подход такой:


    • запуск тестов + сбор метрик производительности (длительность выполнения, память, процессор, количество error/warn/info/debug в логе)
    • изменение системы
    • запуск тех же тестов + сбор метрик производительности
    • сравнение метрик от теста 1 и 2 на одном графике — и так от сборки к сборке

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


    Из минусов — нет точного понимания, какие будут показатели RPS (запросов в сек), TPS (операций в сек). Ведь функциональный тест может зависнуть, и если вчера за минуту выполнялось 60 тестов (1 TPS), то сегодня выполняется только 1 тест в минуту (1/60 TPS) — то есть нагрузка на систему подаётся в 10 раз меньшая, и результаты запусков не сравнимы. Предположу, что в данном подходе такое может случиться. Это открытая модель нагрузки. В JMeter/Gatling/Yandex.Tank контроль TPS/RPS — решаемая задача, с понятными ограничениями.


    Также, достаточно дорого поддерживать много генераторов нагрузки, если она подаётся браузером — браузер требует очень много оперативной памяти, JMeter требует сильно меньше, а Gatling, так как гораздо меньше потоков, требует ещё меньше памяти. Для реализации нагрузки 100 операций в минуту через браузер (много)/http-api-robot (только аутентификация), может понадобится 20 станций. С ростом потребностей (1000 операций в минуту), доля http-api-robot будет расти, а доля браузера снижаться. А потом (10 000 операций в минуту) и вовсе решите перейти на модель, когда всю нагрузку подаёт 4 станции с Gatling/JMeter, и ещё на одной параллельно выполняется функциональный тест в BDD-формате — для оценки отзывчивости интерфейса под высокой нагрузкой.

  • Образ современного тестировщика. Что нужно знать и уметь
    0
    Спасибо, за статью. Заголовки могут служить каркасом оценки кандидатов: «Кандидат коммуникабелен, основ тестирования не знает, обладает знаниями языка Java, претендует на лидирующие позиции». Приведённые факторы встречаются в разном сочетании.

    В команде нужны разные люди — весёлый, терпеливая, умный, в платье, с бородой,… и гитарист. Это шутка, в которой лишь доля шутки. А для сложных систем нужны команды.
  • О чем молчат Лиды: начало карьеры разработчика. Принципы или как стать Middl’ом
    0
    Какой подход порекомендуете при выборе целей?

    Пример подхода — если есть на входе список из 7-ми целей, выбираю три наиболее интересные и важные основываясь на интуиции. Цели соответствуют SMART и их три.

    На что обратить внимание при формировании и выборе целей?
  • О чем молчат Лиды: начало карьеры разработчика. Принципы или как стать Middl’ом
    0
    в компании существуют лимиты на повышения

    Такое видел. Лимиты заранее не известны даже руководителям. И может случиться так, что завершилась оценка, отправлено обоснование повышений команды руководителю, он согласует, уже оглашает команде даты и суммы. А потом выясняется, что о-па и упс, лимиты есть.
    Бюджет надо заранее выяснять, просчитывать.

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

    Спасибо за акцент на степени влияния на команду и бизнес. Думаю стоит оценить этот фактор, не выделял его явно
  • О чем молчат Лиды: начало карьеры разработчика. Принципы или как стать Middl’ом
    0
    Вопрос про вопрос «что должен знать мидл?». Один из комментариев раскрывает один из способов повышения — смена проекта или компании. А если говорить про вариант работы на том же проекте, то такой вопрос задаёт себе каждый лид.

    Кто-то использует матрицы компетенций, различные профили специальностей, наборы курсов и систему их прохождения с получением баллов. Ещё есть оценка 360, просто разговоры по душам. И это только начало.

    Понятно, что система повышения должна быть прозрачной и справедливой. И что это большой труд.

    Как лучше такую систему сделать?
  • Нагрузочное тестирование на фреймворке Gatling
    0
    Здравствуй, Сергей. Спасибо за статью. Отдельное спасибо за "${}" и Just magic с анонимной функцией. И за примеры.

    Написал сценарий с профилем: поднять нагрузку до 20 сценариев в сек, и подержать ее такой час.
    rumpUsersPerSec(0) to (20) during (5 minutes),
    constantUsersPerSec(20) during (1 hour)

    Проверил, на версии 2.3.1: constantUsersPerSec держит среднюю частоту выполнения пользовательского сценария равной указанному значению rate — обычная ровная ступенька. То что надо.

    Поправь описание, в статье написано, про рост нагрузки на rate каждую секунду.
  • Применение методологии OWASP Mobile TOP 10 для тестирования Android приложений
    +2
    Спасибо за статью. За наглядное представление работы инструментов в связке друг с другом. Прочёл с интересом.

    В статье нет рисунков 6, 7. Было бы здорово добавить.

    Статья раскрывает способы проверки на наличие уязвимостей. Сценарий. Её можно методикой назвать. А OWASP top 10 mobile — один из вариантов классификации. Без подобных методик top 10 не столь полезен. Спасибо за труд.