Skip to content

Latest commit

 

History

History
194 lines (106 loc) · 12.9 KB

File metadata and controls

194 lines (106 loc) · 12.9 KB

Testizer email funnels · Analytics guide

Этот документ описывает, какие метрики доступны в текущей версии скрипта и как ими пользоваться.

1. Структура таблицы funnel_entries

Таблица funnel_entries используется для базовой аналитики воронок.

1.1. Гарантия идемпотентности

В таблице есть уникальный индекс по (email, funnel_type, test_id), который обеспечивает идемпотентность на уровне базы данных. Это ограничение гарантирует, что:

  • Один и тот же пользователь не может быть добавлен в одну и ту же воронку несколько раз для одного теста.
  • Дублирующиеся записи предотвращаются даже при параллельном выполнении сервиса синхронизации.
  • Сервис можно безопасно запускать несколько раз без создания дублирующихся записей.

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

Эта гарантия идемпотентности устраняет гонки (TOCTOU - Time-Of-Check-Time-Of-Use), которые могли возникнуть при проверке существующих записей перед вставкой. Ограничение на уровне базы данных предоставляет надежный, атомарный механизм для предотвращения дубликатов.

Важные поля:

  • email

    Email пользователя.

  • funnel_type

    Тип воронки:

    • language для языковых тестов;

    • non_language для неязыковых тестов.

  • user_id, test_id

    Связь с тестом и пользователем в базе MODX.

  • entered_at

    Дата и время попадания пользователя в воронку.

  • certificate_purchased

    Флаг:

    • 0 сертификат не куплен;

    • 1 сертификат куплен.

  • certificate_purchased_at

    Дата и время покупки сертификата. Заполняется при синхронизации с таблицами MODX.

1.2. Проверка дублирующихся записей воронок

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

python -m scripts.find_funnel_duplicates

Скрипт запрашивает базу данных на предмет групп записей, которые имеют одинаковую комбинацию (email, funnel_type, test_id), и отображает их в читаемом табличном формате. Вывод включает:

  • Дублирующуюся комбинацию email, типа воронки и ID теста
  • Количество дублирующихся записей для каждой комбинации
  • Минимальный и максимальный ID записей
  • Самые ранние и поздние временные метки записей

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

Скрипт строго предназначен только для чтения и не изменяет базу данных каким-либо образом. Его безопасно запускать на продакшн-базах данных в диагностических целях.

2. Структура таблицы brevo_sync_outbox

Таблица brevo_sync_outbox хранит ожидающие и обработанные задачи для синхронизации с Brevo. Эта таблица реализует паттерн outbox, разделяя записи в базу данных и внешние вызовы API для обеспечения надежной обработки операций Brevo.

Каждая строка в outbox связана с записью воронки через поле funnel_entry_id, которое ссылается на funnel_entries.id. Таблица отслеживает статус операции, попытки повтора и информацию об ошибках для поддержки надежной доставки вызовов API Brevo.

2.1. Статусы задач

Задачи в outbox могут иметь следующие статусы:

  • pending: Задача ожидает обработки или запланирована для повторной попытки. Задачи со статусом status='pending' и next_attempt_at IS NULL OR next_attempt_at <= NOW() доступны для обработки.

  • success: Задача успешно обработана. Задача больше не обрабатывается воркером.

  • failed: Задача окончательно провалилась после превышения максимального количества попыток или при возникновении фатальной ошибки. Задача больше не обрабатывается воркером.

2.2. Планирование повторных попыток

Outbox реализует явное планирование повторных попыток с использованием retry_count и next_attempt_at:

  • retry_count: Отслеживает количество попыток обработки задачи. Этот счетчик увеличивается каждый раз, когда задача завершается ошибкой.

  • next_attempt_at: Поле типа DATETIME, которое указывает, когда задача должна быть повторно обработана. Это поле используется для планирования повторных попыток с экспоненциальной задержкой:

    • Когда задача завершается ошибкой с временной ошибкой и retry_count все еще ниже максимума (по умолчанию: 5), статус задачи остается pending, а next_attempt_at устанавливается в NOW() + INTERVAL (retry_count * 5) MINUTE.
    • Когда задача завершается фатальной ошибкой или превышает максимальное количество попыток, next_attempt_at устанавливается в NULL, а статус задачи устанавливается в failed.
  • Максимальное количество попыток: По умолчанию задачи повторяются до 5 раз. После превышения максимального количества попыток задача помечается как failed и больше не обрабатывается воркером.

Воркер обрабатывает только задачи, где status='pending' и (next_attempt_at IS NULL OR next_attempt_at <= NOW()), что гарантирует соблюдение запланированных повторных попыток и предотвращает обработку задач до запланированного времени.

3. Отчет по конверсии

Для просмотра конверсии по воронкам используется скрипт:

python -m app.report_conversions

Пример вывода:


Funnel conversion report

------------------------

language: entries=10, purchased=3, conversion=30.00%

non_language: entries=5, purchased=1, conversion=20.00%

Где:

  • entries количество пользователей, которые попали в воронку;
  • purchased количество пользователей, которые купили сертификат после входа в воронку;
  • conversion отношение purchased / entries в процентах.

2.1. Фильтрация по датам

Можно указать период:

python -m app.report_conversions --from-date 2024-01-01 --to-date 2025-01-01
  • --from-date включительно;
  • --to-date исключительно.

Если указать только --from-date, будут учтены записи с этой даты и до текущего момента.

Если параметры не указаны, берутся все записи из funnel_entries.

4. Интерпретация результатов

Примеры вопросов, на которые можно ответить:

  • Сколько людей попало в языковую воронку за последний месяц?
  • Какова конверсия из воронки в покупку сертификата по языковым тестам?
  • Как конверсия языковых тестов отличается от неязыковых?

Для более детальной аналитики можно использовать SQL-запросы к funnel_entries, комбинируя условия по email, user_id, test_id и времени.

5. Расширение аналитики через UTM-метки

Сейчас скрипт не изменяет содержимое писем, а только отправляет контакты в Brevo. Для расширенной аналитики через UTM-метки можно использовать следующий подход:

  1. В шаблонах писем Brevo добавить UTM-метки в ссылки, например:

    • для языковой воронки:

      ?utm_source=testizer&utm_medium=email&utm_campaign=language_funnel

    • для неязыковой:

      ?utm_source=testizer&utm_medium=email&utm_campaign=non_language_funnel

  2. В системах аналитики (например, Google Analytics или аналогах) анализировать переходы по этим UTM.

  3. При необходимости можно связать внешнюю аналитику с данными funnel_entries по email и временным периодам.

При таком подходе структура базы и скрипта не меняется, а расширенная аналитика настраивается через шаблоны писем и внешние отчеты.

6. Возможные направления развития

Если в будущем понадобится более детализированная аналитика, можно:

  • добавить в funnel_entries поле source, например email_language_v1, email_non_language_v1;
  • записывать туда версию воронки или кампании;
  • строить отчеты по комбинации funnel_type + source.

Текущая архитектура уже готова к таким расширениям, так как:

  • вход в воронку фиксируется централизованно в funnel_entries;
  • покупка сертификата привязывается к тем же записям;
  • отчет app.report_conversions можно расширять без изменения основного бизнес-кода.