Skip to content

[benchmark][TypeScript] SMS workflow reminder retry count tracking - #5

Open
hsander wants to merge 1 commit into
benchmark/cal_dot_com/pr-14943-basefrom
benchmark/cal_dot_com/pr-14943
Open

hsander wants to merge 1 commit into
benchmark/cal_dot_com/pr-14943-basefrom
benchmark/cal_dot_com/pr-14943

Conversation

@hsander

@hsander hsander commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Benchmark fixture for cal_dot_com PR #14943.\n\nGolden comments: benchmark/golden_comments/cal_dot_com.json — PR 14943.

scheduledDate: {
lte: dayjs().toISOString(),
},
OR: [

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Ветвь OR в deleteMany не ограничена method: WorkflowMethods.SMS — удаляются напоминания любых типов

Вторая ветка OR-условия { retryCount: { gt: 1 } } в deleteMany не содержит фильтра по method: WorkflowMethods.SMS. Поскольку ветви OR независимы, эта ветка совпадает с любым workflowReminder (email, push и т.д.), у которого retryCount > 1, независимо от метода и даты. Это приводит к тихой потере данных не-SMS напоминаний при каждом вызове эндпоинта.

scheduledDate: {
lte: dayjs().toISOString(),
},
OR: [

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Удаление по retryCount игнорирует scheduledDate — удаляются запланированные на будущее напоминания

Ветвь { retryCount: { gt: 1 } } удаляет напоминания только по количеству повторов без ограничения по scheduledDate. Напоминание, запланированное на будущую дату, но достигшее порога повторов, будет удалено до наступления срока. В сочетании с отсутствием фильтра по method это расширяет радиус поражения.

} catch (error) {
await prisma.workflowReminder.update({
where: {
id: reminder.id,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Обновление в catch-блоке может выбросить исключение и скрыть исходную ошибку

В catch-блоке выполняется await prisma.workflowReminder.update(...) без собственного try-catch. Если обновление не удастся (запись удалена, проблема с БД), выброшенная ошибка заменит исходную ошибку планирования, а последующий console.log с исходной ошибкой никогда не выполнится. Клиент получит необработанную 500 без полезной диагностики.

})) as (PartialWorkflowReminder & { retryCount: number })[];

if (!unscheduledReminders.length) {
res.json({ ok: true });

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Ошибка обновления в else-ветке перехватывается внешним catch и вызывает повторное обновление тем же значением

Если prisma.workflowReminder.update в else-ветке выбрасывает исключение, внешний catch перехватывает его и пытается выполнить то же обновление с тем же reminder.retryCount + 1. Двойного инкремента не происходит (базовое значение то же), но тратится лишний запрос к БД, а реальная причина сбоя в else-ветке маскируется. Catch-блок не различает ошибку планирования и ошибку обновления.

},
});
} else {
await prisma.workflowReminder.update({

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Дублирование логики обновления retryCount в else и catch-ветках

Блок prisma.workflowReminder.update({ where: { id: reminder.id }, data: { retryCount: reminder.retryCount + 1 } }) дословно повторяется в else-ветке и в catch-блоке. Дублирование повышает риск рассинхронизации при изменении логики и увеличивает нагрузку на поддержку.

@hsander

hsander commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

🔍 Автоматическое ревью кода

Краткий итог

Синтез ревью: 9 уникальных групп замечаний по 2 исходн. выполнениям; 6 для публикации (критических: 1, высоких: 1, средних: 2, низких: 2, мелких: 0).

Замечания

🔴 Критично

  • [src/typescript/packages/features/ee/workflows/api/scheduleSMSReminders.ts:4] Ветвь OR в deleteMany не ограничена method: WorkflowMethods.SMS — удаляются напоминания любых типов — Вторая ветка OR-условия { retryCount: { gt: 1 } } в deleteMany не содержит фильтра по method: WorkflowMethods.SMS. Поскольку ветви OR независимы, эта ветка совпадает с любым workflowReminder (email, push и т.д.), у которого retryCount > 1, независимо от метода и даты. Это приводит к тихой потере данных не-SMS напоминаний при каждом вызове эндпоинта.

🟠 Важно

  • [src/typescript/packages/features/ee/workflows/api/scheduleSMSReminders.ts:4] Удаление по retryCount игнорирует scheduledDate — удаляются запланированные на будущее напоминания — Ветвь { retryCount: { gt: 1 } } удаляет напоминания только по количеству повторов без ограничения по scheduledDate. Напоминание, запланированное на будущую дату, но достигшее порога повторов, будет удалено до наступления срока. В сочетании с отсутствием фильтра по method это расширяет радиус поражения.

💡 Дополнительно

  • [src/typescript/packages/features/ee/workflows/api/scheduleSMSReminders.ts:48] Обновление в catch-блоке может выбросить исключение и скрыть исходную ошибку — В catch-блоке выполняется await prisma.workflowReminder.update(...) без собственного try-catch. Если обновление не удастся (запись удалена, проблема с БД), выброшенная ошибка заменит исходную ошибку планирования, а последующий console.log с исходной ошибкой никогда не выполнится. Клиент получит необработанную 500 без полезной диагностики.
  • [src/typescript/packages/features/ee/workflows/api/scheduleSMSReminders.ts:30] Ошибка обновления в else-ветке перехватывается внешним catch и вызывает повторное обновление тем же значением — Если prisma.workflowReminder.update в else-ветке выбрасывает исключение, внешний catch перехватывает его и пытается выполнить то же обновление с тем же reminder.retryCount + 1. Двойного инкремента не происходит (базовое значение то же), но тратится лишний запрос к БД, а реальная причина сбоя в else-ветке маскируется. Catch-блок не различает ошибку планирования и ошибку обновления.
  • [src/typescript/packages/features/ee/workflows/api/scheduleSMSReminders.ts:35] Дублирование логики обновления retryCount в else и catch-ветках — Блок prisma.workflowReminder.update({ where: { id: reminder.id }, data: { retryCount: reminder.retryCount + 1 } }) дословно повторяется в else-ветке и в catch-блоке. Дублирование повышает риск рассинхронизации при изменении логики и увеличивает нагрузку на поддержку.
  • [src/typescript/packages/features/ee/workflows/api/scheduleSMSReminders.ts:4] Семантика порога повторов off-by-one относительно предполагаемого намерения — Очистка удаляет напоминания с retryCount > 1 (т.е. >= 2), а инкремент retryCount устанавливает 1 при первой неудаче и 2 при второй. Поскольку deleteMany выполняется в самом начале функции, напоминание, достигшее retryCount=2 в предыдущем запуске, удаляется до повторной попытки в текущем. Эффективный максимум попыток — 2 предыдущих сбоя. Возможно, это задумано, но не задокументировано.

Автоматическое ревью от Review Engine v2.0

scheduledDate: {
lte: dayjs().toISOString(),
},
OR: [

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Семантика порога повторов off-by-one относительно предполагаемого намерения

Очистка удаляет напоминания с retryCount > 1 (т.е. >= 2), а инкремент retryCount устанавливает 1 при первой неудаче и 2 при второй. Поскольку deleteMany выполняется в самом начале функции, напоминание, достигшее retryCount=2 в предыдущем запуске, удаляется до повторной попытки в текущем. Эффективный максимум попыток — 2 предыдущих сбоя. Возможно, это задумано, но не задокументировано.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant