TGStat
TGStat
Qidiruv uchun matnni kiriting
Ilg‘or kanal qidiruvi
  • flag Uzbek
    Sayt tili
    flag Russian flag English flag Uzbek
  • Saytga kirish
  • Katalog
    Kanal va guruhlar katalogi Hududiy to‘plamlar Tematik to‘plamlar Платные каналы Kanallar qidiruvi
    Kanal/guruh qo‘shish
  • Reytinglar
    Kanallar reytingi Guruhlar reytingi Postlar reytingi
    Brendlar va shaxslar reytingi
  • Analitika
  • Postlarda qidiruv
  • Telegram'ni kuzatish
  • Targ‘ibot
    Yandex Business orqali reklama TGStat Agency orqali kanallarda reklama TGStat.ru saytida reklama
.NET sh blog

12 Dec 2024, 23:27

Telegram'da ochish Ulashish Shikoyat qilish

Про Result pattern

В C# основной способ обработки ошибок - Exceptions. Под него заточено много инструментария.
Есть удобный stacktrace, конструкции обработки ошибок try catch, exception filters и прочее.
И самый главный плюс - выброшенное исключение невозможно проигнорировать - кто-то его обязан обработать.

Но есть и минусы:

1. Производительность

Исключения жрут много ресурсов. Основная причина - необходимость в stack unwinding, в поисках того кто выше по стеку сможет обработать исключение чтобы войти в catch блок.

Еще из-за stack unwinding мы теряем данные в AsyncLocal, а следовательно всё что реализовано на его основе, например LoggingScope создаваемый с помощью ILogger.BeginScope будут тоже потеряны.

Исключения в асинхронном коде еще кратно дороже из-за множества стейт машин которые то и дело ловят и перевыбрасывают исключения, пока не дойдут до самого верха стека. А если еще сделать асинхронные middlware (чем пронизан современный .NET) , то там всё еще печальней.

К счастью, майкрософт работает над улучшением производительности и в .NET 9 они уже оптимизировали обработку исключений от 2 до 4 раз.

Возможно, еще когда .NET откажется от async/await на уровне языка в пользу реализации её в рантайме (эксперимент async2),
то поведение AsyncLocal починится и перформанс обработки ошибок заметно станет лучше и будет все хорошо.

2. Исключения - неявный контракт.

Ваш метод может выбросить любое исключение, но оно никак не будет отражено в контрактах (сигнатуре) метода.
Максимум что можно сделать, это добавить в документацию метода.
///
/// When was invalid.
///
Но можно ли доверять этому? Метод может вызывать что-нибудь из BCL, который допустим выбросит InvalidOperationException.
Это что, теперь всевозможные виды исключений перечислять в каждом методе? Никогда не знаешь что будет выброшено.

Альтернативой этому подходу является Result pattern, который является основным способом обработки ошибок в таких языках как Rust и Go, и по факту является монадой т.е. одной из основ функциональных ЯП.

Для наглядности я отрефакторил приложение BookLibrary: перевёл юзкейсы и агрегаты на него. Также сохранил всю функциональность кодов ошибок, форматы ответов API в ProblemDetails, логирование и сбор метрик.
А чтобы бенчмарк был актуален - обновил до .NET 9

Стало определенно лучше с точки зрения контракта.
Я теперь точно знаю по сигнатуре метода может ли он вернуть ошибку. В тоже время это меня теперь обязывает обрабатывать результаты вызовов каждого такого метода, но об этом можно легко забыть и занести баг :)
Здесь очень нужна языковая поддержка и статические анализаторы.
В целом сама концепция хороша тем, что заставляет заранее продумать различные ошибочные сценарии.

Теперь что касается перформанса, здесь всё еще интересней:

Я решил сделать нагрузочное тестирование на API, которое возвращает книгу по его идентификатору.
Тестов будет 2:
Лучший кейс - 100% запросов вернут книгу
Худший кейс - 100% запросов не найдут книгу в бд

Логирование в консоль отключено, чтобы не влияло на результаты.
Проект запущен через консоль, сбилжено в релизной конфигурации. Postgres 16ый в докере.

Получились такие результаты.

Result pattern (Худший кейс):

'https://t.me/sh_dotnet/97?comment=279' rel='nofollow'>100 VUs p95 13ms RPS 13703
'https://t.me/sh_dotnet/97?comment=280' rel='nofollow'>150 VUs p95 19.23ms RPS 13644
'https://t.me/sh_dotnet/97?comment=281' rel='nofollow'>200 VUs p95 24.13ms RPS 13323

Result pattern (Лучший кейс):

'https://t.me/sh_dotnet/97?comment=282' rel='nofollow'>100 VUs p95 12.99ms RPS 13667

Exception (Худший кейс):

'https://t.me/sh_dotnet/97?comment=283' rel='nofollow'>100 VUs p95 171.77ms RPS 817
'https://t.me/sh_dotnet/97?comment=284' rel='nofollow'>150 VUs p95 232.87ms RPS 969
'https://t.me/sh_dotnet/97?comment=285' rel='nofollow'>200 VUs p95 353.16ms RPS 880

Exception (Лучший кейс):

'https://t.me/sh_dotnet/97?comment=286' rel='nofollow'>100 VUs p95 14.41ms RPS 13241

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

Если Ваше приложение высоконагруженное и чувствительное к latency и работает в очень нестабильных условиях (много сетевых вызовов) - Result pattern ваш выбор :)
Причем необязательно всё приложение переводить на Result. Достаточно там, где нестабильность максимальная.

В комментарии закинул скриншоты нагрузочных тестов.

518 0 4 16 16
Katalog
Kanal va guruhlar katalogi Kanallar to‘plamlari Kanallar qidiruvi Kanal/guruh qo‘shish
Reytinglar
Telegram-kanallar reytingi Telegram-guruhlar reytingi Postlar reytingi Brendlar va shaxslar reytingi
API
Statistika API'si Postlar qidiruvi API'si API Callback
Kanallarimiz
@TGStat @TGStat_Chat @telepulse @TGStatAPI
O‘qish
Академия TGStat Telegram tadqiqoti 2019 Telegram tadqiqoti 2021 Telegram tadqiqoti 2023
Kontaktlar
Справочный центр Qo‘llab-quvvatlash Email Vakansiyalar
Har xil narsalar
Foydalanuvchi shartnomasi Maxfiylik siyosati Ommaviy oferta
Botlarimiz
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot