День 2735. #ЗаметкиНаПолях #Cancellation
Токен Отмены: Ошибки, Которые Незаметно Приводят к Проблемам. Начало
Пользователь закрывает вкладку во время выполнения запроса. Клиент отключается. На панели управления этот запрос должен отображаться как отменённый в течение миллисекунд. Вместо этого он продолжает выполняться ещё секунды — обращается к БД, API, записывает результат, который никто никогда не прочтёт. При тысячах прерванных запросов в день вы платите за вычислительные ресурсы, которые никому не служат. Это чаще всего токен отмены, который корректно создаётся в начале конвейера обработки запросов, а затем незаметно перестаёт передаваться дальше. ASP.NET Core предоставляет возможность отмены практически бесплатно. Токен интегрирован в конвейер, и фреймворк отменяет его в момент отключения клиента. Все ошибки происходят в коде, который забывает о существовании токена.
Каждый HTTP-запрос в ASP.NET Core содержит токен, который вы можете запросить напрямую:
Редко требуется явно использовать HttpContext.RequestAborted — обработчики в минимальных API и методы-действия в MVC могут просто принимать параметр CancellationToken, и фреймворк автоматически привязывает RequestAborted к нему:
Вот и весь контракт: токен передаётся вам, а ваша задача — передать его каждому ожидаемому вызову ниже. Описанные ниже ошибки — это варианты нарушения этой цепочки.
Ошибка 1: «Проглатывание» токена на уровне сервиса
Наиболее распространённое нарушение - сигнатура метода сервиса игнорирует параметр:
Здесь ничего не вызывает ошибок. На код-ревью всё выглядит нормально, если только вы специально это не проверяете. Но с этого момента запрос становится неотменяемым — клиент может исчезнуть, a запрос к БД и HTTP-вызов всё равно завершатся. Верный вариант:
Правило: если метод ожидает чего-либо, он обязан принимать CancellationToken. Анализатор, вроде CA2016 (часть встроенных анализаторов .NET), отметит большинство таких случаев — включите его как предупреждение, а не просто как рекомендацию.
Ошибка №2: Task.Run без токена или с неправильным токеном
Task.Run имеет две перегрузки, и легко вызвать ту, которая делает вашу работу неотменяемой:
Передача
Для циклов, интенсивно использующих процессор, вызывайте
Продолжение следует…
Источник: https://thecodeman.net/posts/cancellation-tokens-in-aspnet-core-mistakes
Токен Отмены: Ошибки, Которые Незаметно Приводят к Проблемам. Начало
Пользователь закрывает вкладку во время выполнения запроса. Клиент отключается. На панели управления этот запрос должен отображаться как отменённый в течение миллисекунд. Вместо этого он продолжает выполняться ещё секунды — обращается к БД, API, записывает результат, который никто никогда не прочтёт. При тысячах прерванных запросов в день вы платите за вычислительные ресурсы, которые никому не служат. Это чаще всего токен отмены, который корректно создаётся в начале конвейера обработки запросов, а затем незаметно перестаёт передаваться дальше. ASP.NET Core предоставляет возможность отмены практически бесплатно. Токен интегрирован в конвейер, и фреймворк отменяет его в момент отключения клиента. Все ошибки происходят в коде, который забывает о существовании токена.
Каждый HTTP-запрос в ASP.NET Core содержит токен, который вы можете запросить напрямую:
app.MapGet("/reports/{id}", async (int id, HttpContext http, ReportService service) =>
{
// Этот токен отменяется при отключении клиента
// или на старте процедуры завершения работы сервера
CancellationToken ct = http.RequestAborted;
var report = await service.BuildReportAsync(id, ct);
return Results.Ok(report);
});Редко требуется явно использовать HttpContext.RequestAborted — обработчики в минимальных API и методы-действия в MVC могут просто принимать параметр CancellationToken, и фреймворк автоматически привязывает RequestAborted к нему:
app.MapGet("/reports/{id}", async (int id, ReportService service, CancellationToken ct) =>
{
var report = await service.BuildReportAsync(id, ct);
return Results.Ok(report);
});Вот и весь контракт: токен передаётся вам, а ваша задача — передать его каждому ожидаемому вызову ниже. Описанные ниже ошибки — это варианты нарушения этой цепочки.
Ошибка 1: «Проглатывание» токена на уровне сервиса
Наиболее распространённое нарушение - сигнатура метода сервиса игнорирует параметр:
public class ReportService
{
// …
public async Task<Report> BuildReportAsync(int id)
{
var order = await _db.Orders.FindAsync(id);
var pricing = await _prcClient.GetAsync($"/price/{id}");
return Map(order, pricing);
}
}
Здесь ничего не вызывает ошибок. На код-ревью всё выглядит нормально, если только вы специально это не проверяете. Но с этого момента запрос становится неотменяемым — клиент может исчезнуть, a запрос к БД и HTTP-вызов всё равно завершатся. Верный вариант:
public async Task<Report> BuildReportAsync(
int id, CancellationToken ct)
{
var order = await _db.Orders.FindAsync(new object[] { id }, ct);
var pricing = await _prcClient.GetAsync($"/price/{id}", ct);
return Map(order, pricing);
}
Правило: если метод ожидает чего-либо, он обязан принимать CancellationToken. Анализатор, вроде CA2016 (часть встроенных анализаторов .NET), отметит большинство таких случаев — включите его как предупреждение, а не просто как рекомендацию.
Ошибка №2: Task.Run без токена или с неправильным токеном
Task.Run имеет две перегрузки, и легко вызвать ту, которая делает вашу работу неотменяемой:
public async Task<byte[]> GenerateExportAsync(
int reportId, CancellationToken ct)
{
return await Task.Run(() => BuildReport(reportId), ct);
}
Передача
ct в Task.Run останавливает запуск задачи только в том случае, если она уже отменена, но ничего не делает, если делегат уже запущен, т.к. BuildReport не может отслеживать отмену. Решение – передавать токен в исполняемый метод, а не только в Task.Run:public async Task<byte[]> GenerateExportAsync(
int reportId, CancellationToken ct)
{
return await Task.Run(() => BuildReport(reportId, ct), ct);
}
private byte[] BuildReport(
int id, CancellationToken ct)
{
for (int page = 0; page < TotalPages(id); page++)
{
// периодически проверяем отмену
ct.ThrowIfCancellationRequested();
RenderPage(id, page);
}
…
}
Для циклов, интенсивно использующих процессор, вызывайте
ct.ThrowIfCancellationRequested() между итерациями, а не только один раз в начале. Цикл, который проверяет токен один раз, а затем работает десять секунд, не может быть корректно отменён.Продолжение следует…
Источник: https://thecodeman.net/posts/cancellation-tokens-in-aspnet-core-mistakes
👍20
День 2736. #ЗаметкиНаПолях #Cancellation
Токен Отмены: Ошибки, Которые Незаметно Приводят к Проблемам. Продолжение
Начало
Ошибка №3: Игнорирование OperationCanceledException
Отмена в .NET работает путём генерации исключений. Когда срабатывает токен, следующая ожидаемая операция генерирует исключение OperationCanceledException (или его подкласс TaskCanceledException). Блок catch, перехватывающий все исключения, перехватит и это — и теперь ваши логи полны «сбоев», которые на самом деле являются просто закрытием вкладок пользователями:
Решение – ожидать отмену:
ASP.NET Core умеет обрабатывать необработанное OperationCanceledException, связанное с RequestAborted — оно завершает запрос без записи кода 500.
Ошибка №4: Предположение, что фоновый сервис получает токен запроса
Эта ошибка сбивает с толку, потому что выглядит как противоположная проблема. У фонового сервиса есть собственный токен отмены, и он не имеет отношения к HTTP-запросу. Он срабатывает только при завершении работы хоста:
Если вы запускаете фоновые процессы внутри обработчика запросов и передаёте в него RequestAborted, вы создаёте ошибку: фоновые процессы прекращаются в тот же момент, когда отправляется HTTP-ответ (потому что RequestAborted также срабатывает в этих случаях при некоторых конфигурациях хоста) или когда клиент отключается. Фоновым процессам, которые должны продолжаться после завершения запроса, нужен собственный токен — обычно IHostApplicationLifetime.ApplicationStopping, а не токен запроса:
Важное различие: RequestAborted означает «тот, кто вызывал этот запрос, отключился». ApplicationStopping означает «процесс завершается». Использование одного значения вместо другого либо приводит к преждевременной отмене работы, либо к лишней работе, которая должна была быть отменена вместе с запросом.
Окончание следует…
Источник: https://thecodeman.net/posts/cancellation-tokens-in-aspnet-core-mistakes
Токен Отмены: Ошибки, Которые Незаметно Приводят к Проблемам. Продолжение
Начало
Ошибка №3: Игнорирование OperationCanceledException
Отмена в .NET работает путём генерации исключений. Когда срабатывает токен, следующая ожидаемая операция генерирует исключение OperationCanceledException (или его подкласс TaskCanceledException). Блок catch, перехватывающий все исключения, перехватит и это — и теперь ваши логи полны «сбоев», которые на самом деле являются просто закрытием вкладок пользователями:
try
{
await service.BuildReportAsync(id, ct);
}
catch (Exception ex)
{
_logger.LogError(ex, "Генерация отчёта не удалась");
throw;
}
Решение – ожидать отмену:
try
{
await service.BuildReportAsync(id, ct);
}
catch (OperationCanceledException)
when (ct.IsCancellationRequested)
{
_logger.LogDebug("Генерация отчёта №{Id} отменена клиентом", id);
throw; // позволяем ASP.NET Core перехватить это и вернуть правильный ответ
}
catch (Exception ex)
{
_logger.LogError(ex, "Генерация отчёта не удалась");
throw;
}
ASP.NET Core умеет обрабатывать необработанное OperationCanceledException, связанное с RequestAborted — оно завершает запрос без записи кода 500.
Ошибка №4: Предположение, что фоновый сервис получает токен запроса
Эта ошибка сбивает с толку, потому что выглядит как противоположная проблема. У фонового сервиса есть собственный токен отмены, и он не имеет отношения к HTTP-запросу. Он срабатывает только при завершении работы хоста:
public class OutboxProcessor : BackgroundService
{
private readonly IServiceProvider _services;
public OutboxProcessor(IServiceProvider services)
=> _services = services;
protected override async Task ExecuteAsync(
CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
using var scope = _services.CreateScope();
var db = scope.ServiceProvider
.GetRequiredService<AppDbContext>();
// stopToken здесь означает «приложение закрывается»,
// а не «эта часть работы отменена кем-то»
await ProcessAsync(db, stopToken);
await Task.Delay(TimeSpan.FromSeconds(5), stopToken);
}
}
}
Если вы запускаете фоновые процессы внутри обработчика запросов и передаёте в него RequestAborted, вы создаёте ошибку: фоновые процессы прекращаются в тот же момент, когда отправляется HTTP-ответ (потому что RequestAborted также срабатывает в этих случаях при некоторых конфигурациях хоста) или когда клиент отключается. Фоновым процессам, которые должны продолжаться после завершения запроса, нужен собственный токен — обычно IHostApplicationLifetime.ApplicationStopping, а не токен запроса:
app.MapPost("/reports/{id}/export", async (
int id,
ReportService svc,
IHostApplicationLifetime lt) =>
{
// НЕ RequestAborted – сервис должен продолжить
// работу, даже когда ответ клиенту отправлен
_ = svc.GenerateAsync(id, lt.ApplicationStopping);
return Results.Accepted();
});Важное различие: RequestAborted означает «тот, кто вызывал этот запрос, отключился». ApplicationStopping означает «процесс завершается». Использование одного значения вместо другого либо приводит к преждевременной отмене работы, либо к лишней работе, которая должна была быть отменена вместе с запросом.
Окончание следует…
Источник: https://thecodeman.net/posts/cancellation-tokens-in-aspnet-core-mistakes
👍10
День 2737. #ЗаметкиНаПолях #Cancellation
Токен Отмены: Ошибки, Которые Незаметно Приводят к Проблемам. Окончание
Начало
Продолжение
Ошибка № 5: Понимание необходимости связывания токенов
Иногда одной операции необходимо учитывать сразу два источника отмены — токен запроса и тайм-аут, который вы добавляете. Если забыть их объединить, один из двух источников молча ничего не сделает:
Без связывания, отдельный токен таймаута игнорирует разрывы соединения с клиентом, а отдельный токен запроса игнорирует таймаут. CreateLinkedTokenSource — решение, когда отмена должна происходить из нескольких мест.
Правила
Не каждому методу нужен параметр CancellationToken. Синхронное вычисление ничего не ожидает, поэтому отменять нечего. Вот некоторые правила:
1. Всё, что ожидает ввода-вывода (БД, HTTP, файл, очередь) — принимает токен, передаёт его дальше, без исключений.
2. Циклы, сильно нагружающие CPU, длящиеся более нескольких миллисекунд — периодически проверяйте ThrowIfCancellationRequested() внутри цикла, а не только при входе.
3. Работа «fire-and-forget» — никогда не используйте RequestAborted. Используйте ApplicationStopping или лучше, передайте запрос в соответствующую фоновую очередь вместо
4. Логирование отмены — LogDebug или ниже, никогда не LogError. Это не ошибка.
FAQ
1. В чем разница между CancellationToken.None и не передачей токена?
На практике это приводит к одному результату — операцию нельзя отменить. Преднамеренная передача CancellationToken.None должна быть редкой и осознанной, например, в фоновый сервис, работающий вне контекста запроса, который действительно должен завершить работу независимо от обстоятельств.
2. Использовать HttpContext.RequestAborted напрямую или параметр CancellationToken?
Параметр. Привязка модели ASP.NET Core автоматически заполняет параметр значением RequestAborted, и это выглядит чище, чем обращение к HttpContext. Используйте HttpContext.RequestAborted в коде, не имеющем прямого доступа к параметру, например, для фильтра или промежуточного ПО.
3. Почему мой BackgroundService останавливается посреди выполнения пакета при повторном развертывании?
Потому что токен остановки срабатывает при ApplicationStopping, а таймаут завершения работы хоста по умолчанию короткий. Если пакет должен завершиться корректно, либо уменьшите размер пакета так, чтобы одна единица работы поместилась в окно завершения, либо увеличьте HostOptions.ShutdownTimeout — но не полагайтесь на неограниченный таймаут завершения, поскольку хост (или ваш оркестратор) в итоге принудительно завершит процесс независимо от этого.
4. Перехват исключения OperationCanceledException без повторного выбрасывания что-либо ломает?
Зависит от места. Внутри обработчика запроса, если проигнорировать его без повторного выбрасывания, ASP.NET Core никогда не получит сигнал об отмене запроса, поэтому может попытаться отправить ответ на уже разорванное соединение, что вызовет собственное исключение на более высоком уровне. Повторно выбросьте исключение и позвольте фреймворку обработать жизненный цикл ответа; перехватывайте его локально только для добавления отладочного лога или очистки ресурса.
Источник: https://thecodeman.net/posts/cancellation-tokens-in-aspnet-core-mistakes
Токен Отмены: Ошибки, Которые Незаметно Приводят к Проблемам. Окончание
Начало
Продолжение
Ошибка № 5: Понимание необходимости связывания токенов
Иногда одной операции необходимо учитывать сразу два источника отмены — токен запроса и тайм-аут, который вы добавляете. Если забыть их объединить, один из двух источников молча ничего не сделает:
public async Task<Pricing> GetPricingAsync(
int id, CancellationToken requestCt)
{
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCt, timeoutCts.Token);
// linkedCts.Token отменяется при первом из событий:
// клиент отключился или после 3х секунд
var resp = await
_prcClient.GetAsync($"/price/{id}", linkedCts.Token);
return await resp.Content
.ReadFromJsonAsync<Pricing>(linkedCts.Token);
}
Без связывания, отдельный токен таймаута игнорирует разрывы соединения с клиентом, а отдельный токен запроса игнорирует таймаут. CreateLinkedTokenSource — решение, когда отмена должна происходить из нескольких мест.
Правила
Не каждому методу нужен параметр CancellationToken. Синхронное вычисление ничего не ожидает, поэтому отменять нечего. Вот некоторые правила:
1. Всё, что ожидает ввода-вывода (БД, HTTP, файл, очередь) — принимает токен, передаёт его дальше, без исключений.
2. Циклы, сильно нагружающие CPU, длящиеся более нескольких миллисекунд — периодически проверяйте ThrowIfCancellationRequested() внутри цикла, а не только при входе.
3. Работа «fire-and-forget» — никогда не используйте RequestAborted. Используйте ApplicationStopping или лучше, передайте запрос в соответствующую фоновую очередь вместо
_ = SomeAsync(…).4. Логирование отмены — LogDebug или ниже, никогда не LogError. Это не ошибка.
FAQ
1. В чем разница между CancellationToken.None и не передачей токена?
На практике это приводит к одному результату — операцию нельзя отменить. Преднамеренная передача CancellationToken.None должна быть редкой и осознанной, например, в фоновый сервис, работающий вне контекста запроса, который действительно должен завершить работу независимо от обстоятельств.
2. Использовать HttpContext.RequestAborted напрямую или параметр CancellationToken?
Параметр. Привязка модели ASP.NET Core автоматически заполняет параметр значением RequestAborted, и это выглядит чище, чем обращение к HttpContext. Используйте HttpContext.RequestAborted в коде, не имеющем прямого доступа к параметру, например, для фильтра или промежуточного ПО.
3. Почему мой BackgroundService останавливается посреди выполнения пакета при повторном развертывании?
Потому что токен остановки срабатывает при ApplicationStopping, а таймаут завершения работы хоста по умолчанию короткий. Если пакет должен завершиться корректно, либо уменьшите размер пакета так, чтобы одна единица работы поместилась в окно завершения, либо увеличьте HostOptions.ShutdownTimeout — но не полагайтесь на неограниченный таймаут завершения, поскольку хост (или ваш оркестратор) в итоге принудительно завершит процесс независимо от этого.
4. Перехват исключения OperationCanceledException без повторного выбрасывания что-либо ломает?
Зависит от места. Внутри обработчика запроса, если проигнорировать его без повторного выбрасывания, ASP.NET Core никогда не получит сигнал об отмене запроса, поэтому может попытаться отправить ответ на уже разорванное соединение, что вызовет собственное исключение на более высоком уровне. Повторно выбросьте исключение и позвольте фреймворку обработать жизненный цикл ответа; перехватывайте его локально только для добавления отладочного лога или очистки ресурса.
Источник: https://thecodeman.net/posts/cancellation-tokens-in-aspnet-core-mistakes
👍6
День 2738. #ЗаметкиНаПолях
Отладка Дампов в Visual Studio
Дамп памяти — это файл, содержащий копию памяти конкретного процесса. Файлы дампов в Windows обычно имеют расширение .dmp. Это полезно, когда у вас есть ошибка, которая воспроизводятся только в рабочей среде. И да, это, как правило, крайняя мера! Обычно ошибки обнаруживаются путём воспроизведения проблемы локально, а затем вы можете просто запустить код в отладчике. Но если вам не удаётся найти/воспроизвести ошибку за разумное время, создание дампа — отличный способ получить дополнительную информацию.
Создание дампа
Лучший инструмент для этого — ProcDump: можно создавать дампы по запросу или по триггеру мониторинга (сбой процесса, высокая загрузка ЦП/памяти и т.д.). Однако ProcDump требует доступа к машине, поэтому он отлично подходит только для локальных или виртуальных развёртываний. Если вы работаете с веб-приложением Azure, вы можете создавать дампы памяти и там.
Загрузка дампа
Вы можете просто перетащить файл dbg в Visual Studio. Но, прежде чем это сделать, вот пара советов по настройке VS для наилучшего результата:
1. Just my code
Снимите флажок Just my code (Только мой код). Удобнее видеть полную картину при отладке файла дампа.
2. Символы
Включите загрузку символов (например, файлов .pdb). VS использует символы для расшифровки скомпилированного кода, особенно если он был скомпилирован в режиме Release. Символы часто необходимы для понимания стека. Лучший вариант — указать VS загружать все символы при загрузке файла дампа. Обычно настройку лучше выключать, т.к. загрузка этих символов занимает много времени. Но для при отладке дампа она пригодится.
Также нужно указать, откуда загружать файлы символов. Два основных источника — это Microsoft и NuGet (symbols.nuget.org). Microsoft предоставляет символы для своей ОС (как минимум); сервер символов NuGet — это общее место для символов библиотек с открытым кодом.
Наконец, хорошо бы настроить локальный кэш символов. Это локальная папка, которая будет хранить все эти файлы pdb.
3. Исходные файлы
Включите поддержку сервера исходных файлов. Это способ для Visual Studio загружать точную версию исходных файлов, фактически использованных для компиляции программы. В настоящее время серверы исходных файлов в основном используются для неуправляемого кода, хотя некоторые устаревшие библиотеки .NET могут их использовать.
Также лучше включить диспетчер учётных данных Git для Source Link. Source Link — это современная замена серверам исходного кода, по крайней мере, в контексте .NET. Это позволит получать исходные файлы из вашего репозитория исходного кода.
Теперь VS готова загрузить файл дампа; просто перетащите его прямо в VS. Затем сходите за чашкой кофе; загрузка всех этих символов в первый раз потребует некоторое время! По мере заполнения кэша файлы дампов будут загружаться быстрее; первая загрузка обычно самая медленная.
VS спросит, какой тип отладчика вы хотите запустить; лучше выбрать Mixed (смешанный) — если вы не знаете, в чём проблема, лучше иметь возможность видеть всё. Затем вы перейдёте в режим, похожий на отладку. Конечно, вы не сможете снять паузу или пошагово отладить, но сможете покопаться в настройках. Как правило, окна отладки «Параллельные стеки», «Потоки», «Стек вызовов» и «Модули» являются хорошими отправными точками для попытки разобраться в происходящем.
Источник: https://blog.stephencleary.com/2025/12/debug-dumps-in-visual-studio.html
Отладка Дампов в Visual Studio
Дамп памяти — это файл, содержащий копию памяти конкретного процесса. Файлы дампов в Windows обычно имеют расширение .dmp. Это полезно, когда у вас есть ошибка, которая воспроизводятся только в рабочей среде. И да, это, как правило, крайняя мера! Обычно ошибки обнаруживаются путём воспроизведения проблемы локально, а затем вы можете просто запустить код в отладчике. Но если вам не удаётся найти/воспроизвести ошибку за разумное время, создание дампа — отличный способ получить дополнительную информацию.
Создание дампа
Лучший инструмент для этого — ProcDump: можно создавать дампы по запросу или по триггеру мониторинга (сбой процесса, высокая загрузка ЦП/памяти и т.д.). Однако ProcDump требует доступа к машине, поэтому он отлично подходит только для локальных или виртуальных развёртываний. Если вы работаете с веб-приложением Azure, вы можете создавать дампы памяти и там.
Загрузка дампа
Вы можете просто перетащить файл dbg в Visual Studio. Но, прежде чем это сделать, вот пара советов по настройке VS для наилучшего результата:
1. Just my code
Снимите флажок Just my code (Только мой код). Удобнее видеть полную картину при отладке файла дампа.
2. Символы
Включите загрузку символов (например, файлов .pdb). VS использует символы для расшифровки скомпилированного кода, особенно если он был скомпилирован в режиме Release. Символы часто необходимы для понимания стека. Лучший вариант — указать VS загружать все символы при загрузке файла дампа. Обычно настройку лучше выключать, т.к. загрузка этих символов занимает много времени. Но для при отладке дампа она пригодится.
Также нужно указать, откуда загружать файлы символов. Два основных источника — это Microsoft и NuGet (symbols.nuget.org). Microsoft предоставляет символы для своей ОС (как минимум); сервер символов NuGet — это общее место для символов библиотек с открытым кодом.
Наконец, хорошо бы настроить локальный кэш символов. Это локальная папка, которая будет хранить все эти файлы pdb.
3. Исходные файлы
Включите поддержку сервера исходных файлов. Это способ для Visual Studio загружать точную версию исходных файлов, фактически использованных для компиляции программы. В настоящее время серверы исходных файлов в основном используются для неуправляемого кода, хотя некоторые устаревшие библиотеки .NET могут их использовать.
Также лучше включить диспетчер учётных данных Git для Source Link. Source Link — это современная замена серверам исходного кода, по крайней мере, в контексте .NET. Это позволит получать исходные файлы из вашего репозитория исходного кода.
Теперь VS готова загрузить файл дампа; просто перетащите его прямо в VS. Затем сходите за чашкой кофе; загрузка всех этих символов в первый раз потребует некоторое время! По мере заполнения кэша файлы дампов будут загружаться быстрее; первая загрузка обычно самая медленная.
VS спросит, какой тип отладчика вы хотите запустить; лучше выбрать Mixed (смешанный) — если вы не знаете, в чём проблема, лучше иметь возможность видеть всё. Затем вы перейдёте в режим, похожий на отладку. Конечно, вы не сможете снять паузу или пошагово отладить, но сможете покопаться в настройках. Как правило, окна отладки «Параллельные стеки», «Потоки», «Стек вызовов» и «Модули» являются хорошими отправными точками для попытки разобраться в происходящем.
Источник: https://blog.stephencleary.com/2025/12/debug-dumps-in-visual-studio.html
👍8
День 2739. #Карьера
21 Урок После 14 Лет в Google. Начало
Автор оригинала: Addy Osmani
Когда я пришел в Google около 14 лет назад, я думал, что работа заключается в написании отличного кода. Отчасти я был прав. Но чем дольше я там работал, тем больше понимал, что преуспевают не обязательно лучшие программисты, а те, кто научился ориентироваться во всём, что связано с кодом: в людях, в политике, в согласованности действий, в неопределённости. Эти советы о закономерностях, которые постоянно проявляются, проект за проектом, команда за командой.
1. Лучшие инженеры одержимы решением проблем пользователей.
Заманчиво влюбиться в технологию и искать места для её применения. Все это делали. Но инженеры, создающие наибольшую ценность, работают наоборот: они одержимы пониманием проблем пользователей и позволяют решениям возникать из этого понимания.
Это означает тратить время на обработку заявок в службу поддержки, на общение с пользователями, на наблюдение за их трудностями, на вопросы «почему», пока не доберёшься до сути. Инженер, который действительно понимает проблему, часто обнаруживает, что элегантное решение проще, чем кто-либо ожидал. Инженер, который начинает с готового решения, склонен к усложнению в поисках оправдания.
2. Быть правым легко. Настоящая работа — совместное движение к правде.
Вы можете выиграть каждый технический спор и потерять проект. Я наблюдал, как блестящие инженеры накапливали молчаливое негодование, всегда будучи самым умным человеком в комнате. Цена проявляется позже в виде «загадочных проблем с исполнением» и «странного сопротивления».
Навык не в том, чтобы быть правым, а в том, чтобы в дискуссии находить решение проблемы, оставляя пространство для других и оставаться скептически настроенным к собственной уверенности.
3. Ориентация на действие. Выпуск продукта. Вы можете отредактировать плохую страницу, но не можете отредактировать пустую.
Стремление к совершенству парализует. Я наблюдал, как инженеры неделями обсуждали идеальную архитектуру для чего-то, чего они никогда не создавали. Идеальное решение редко рождается из одних лишь размышлений — оно рождается из контакта с реальностью. ИИ во многом может помочь в этом.
Сначала сделайте как-нибудь, затем правильно, затем улучшайте. Покажите пользователям некрасивый прототип. Напишите неряшливый первый черновик проектной документации. Выпустите MVP, который немного вас смущает. Вы узнаете больше из одной недели реальной обратной связи, чем из месяца теоретических дебатов.
4. Опыт – это ясность. Умничание — это лишние затраты.
Инстинкт писать умный код почти универсален среди инженеров. Это воспринимается как доказательство компетентности. Но разработка ПО — это работа с дедлайнами и другими программистами. В такой среде ясность — это не споры о стиле, а снижение операционных рисков.
Ваш код — это руководство для незнакомцев, которые будут поддерживать его в 2 часа ночи во время сбоя. Оптимизируйте код для их понимания, а не для своего эго. Самые уважаемые сеньоры научились всегда жертвовать остроумием ради ясности.
5. Новизна — это кредит, который вы возвращаете сбоями, наймом персонала и когнитивными издержками.
Относитесь к выбору технологий как к организации с небольшим бюджетом на «инновационные токены». Тратьте один каждый раз, когда внедряете что-то существенно нестандартное. Вы не можете позволить себе много.
Это не значит «никогда не внедряйте новинки». Это значит «внедрять инновации только там, где вам платят именно за это». Всё остальное должно быть скучным, потому что у скучного есть известные причины неудач.
«Лучший инструмент для работы» часто оказывается «наименее худшим инструментом для большинства задач», потому что содержание зоопарка становится затратным.
6. За вас говорит не ваш код, а люди.
В начале своей карьеры я считал, что отличная работа говорит сама за себя. Нет. Код молча лежит в репозитории. Ваш менеджер упоминает вас на совещании, или нет. Коллега рекомендует для проекта вас или кого-то ещё.
В крупных организациях решения принимаются на совещаниях, на которые вас не приглашают, на основе отзывов, которые писали не вы, людьми, у которых есть пять минут и двенадцать приоритетов. Если никто не может рассказать за вас о вашем вкладе, ваш вклад по сути незначителен.
Речь не только о саморекламе, но и о том, чтобы сделать цепочку создания ценности понятной для всех, включая вас самих.
7. Лучший код — тот, который вам никогда не приходилось писать.
В инженерной культуре мы ценим созидание. Никто не получает повышения за удаление кода, хотя это улучшает систему больше, чем добавление. Каждая строка кода, которую вы не написали, — это строка, которую вам никогда не придётся отлаживать, поддерживать или объяснять.
Прежде чем начать разработку, задайте себе вопрос: «Что произойдет, если мы просто… не будем этого делать?» Иногда ответ — «ничего плохого», вот и решение.
Проблема не в том, что инженеры не умеют писать код или использовать для этого ИИ. Проблема в том, что мы настолько хорошо пишем код, что забываем спросить себя, стоит ли это делать.
Продолжение следует…
Источник: https://addyosmani.com/blog/21-lessons/
21 Урок После 14 Лет в Google. Начало
Автор оригинала: Addy Osmani
Когда я пришел в Google около 14 лет назад, я думал, что работа заключается в написании отличного кода. Отчасти я был прав. Но чем дольше я там работал, тем больше понимал, что преуспевают не обязательно лучшие программисты, а те, кто научился ориентироваться во всём, что связано с кодом: в людях, в политике, в согласованности действий, в неопределённости. Эти советы о закономерностях, которые постоянно проявляются, проект за проектом, команда за командой.
1. Лучшие инженеры одержимы решением проблем пользователей.
Заманчиво влюбиться в технологию и искать места для её применения. Все это делали. Но инженеры, создающие наибольшую ценность, работают наоборот: они одержимы пониманием проблем пользователей и позволяют решениям возникать из этого понимания.
Это означает тратить время на обработку заявок в службу поддержки, на общение с пользователями, на наблюдение за их трудностями, на вопросы «почему», пока не доберёшься до сути. Инженер, который действительно понимает проблему, часто обнаруживает, что элегантное решение проще, чем кто-либо ожидал. Инженер, который начинает с готового решения, склонен к усложнению в поисках оправдания.
2. Быть правым легко. Настоящая работа — совместное движение к правде.
Вы можете выиграть каждый технический спор и потерять проект. Я наблюдал, как блестящие инженеры накапливали молчаливое негодование, всегда будучи самым умным человеком в комнате. Цена проявляется позже в виде «загадочных проблем с исполнением» и «странного сопротивления».
Навык не в том, чтобы быть правым, а в том, чтобы в дискуссии находить решение проблемы, оставляя пространство для других и оставаться скептически настроенным к собственной уверенности.
3. Ориентация на действие. Выпуск продукта. Вы можете отредактировать плохую страницу, но не можете отредактировать пустую.
Стремление к совершенству парализует. Я наблюдал, как инженеры неделями обсуждали идеальную архитектуру для чего-то, чего они никогда не создавали. Идеальное решение редко рождается из одних лишь размышлений — оно рождается из контакта с реальностью. ИИ во многом может помочь в этом.
Сначала сделайте как-нибудь, затем правильно, затем улучшайте. Покажите пользователям некрасивый прототип. Напишите неряшливый первый черновик проектной документации. Выпустите MVP, который немного вас смущает. Вы узнаете больше из одной недели реальной обратной связи, чем из месяца теоретических дебатов.
4. Опыт – это ясность. Умничание — это лишние затраты.
Инстинкт писать умный код почти универсален среди инженеров. Это воспринимается как доказательство компетентности. Но разработка ПО — это работа с дедлайнами и другими программистами. В такой среде ясность — это не споры о стиле, а снижение операционных рисков.
Ваш код — это руководство для незнакомцев, которые будут поддерживать его в 2 часа ночи во время сбоя. Оптимизируйте код для их понимания, а не для своего эго. Самые уважаемые сеньоры научились всегда жертвовать остроумием ради ясности.
5. Новизна — это кредит, который вы возвращаете сбоями, наймом персонала и когнитивными издержками.
Относитесь к выбору технологий как к организации с небольшим бюджетом на «инновационные токены». Тратьте один каждый раз, когда внедряете что-то существенно нестандартное. Вы не можете позволить себе много.
Это не значит «никогда не внедряйте новинки». Это значит «внедрять инновации только там, где вам платят именно за это». Всё остальное должно быть скучным, потому что у скучного есть известные причины неудач.
«Лучший инструмент для работы» часто оказывается «наименее худшим инструментом для большинства задач», потому что содержание зоопарка становится затратным.
6. За вас говорит не ваш код, а люди.
В начале своей карьеры я считал, что отличная работа говорит сама за себя. Нет. Код молча лежит в репозитории. Ваш менеджер упоминает вас на совещании, или нет. Коллега рекомендует для проекта вас или кого-то ещё.
В крупных организациях решения принимаются на совещаниях, на которые вас не приглашают, на основе отзывов, которые писали не вы, людьми, у которых есть пять минут и двенадцать приоритетов. Если никто не может рассказать за вас о вашем вкладе, ваш вклад по сути незначителен.
Речь не только о саморекламе, но и о том, чтобы сделать цепочку создания ценности понятной для всех, включая вас самих.
7. Лучший код — тот, который вам никогда не приходилось писать.
В инженерной культуре мы ценим созидание. Никто не получает повышения за удаление кода, хотя это улучшает систему больше, чем добавление. Каждая строка кода, которую вы не написали, — это строка, которую вам никогда не придётся отлаживать, поддерживать или объяснять.
Прежде чем начать разработку, задайте себе вопрос: «Что произойдет, если мы просто… не будем этого делать?» Иногда ответ — «ничего плохого», вот и решение.
Проблема не в том, что инженеры не умеют писать код или использовать для этого ИИ. Проблема в том, что мы настолько хорошо пишем код, что забываем спросить себя, стоит ли это делать.
Продолжение следует…
Источник: https://addyosmani.com/blog/21-lessons/
👍6👎2
День 2740. #Карьера
21 Урок После 14 Лет в Google. Продолжение
Начало
8. В больших масштабах даже у ваших ошибок есть пользователи.
При достаточном количестве пользователей каждое наблюдаемое поведение становится зависимостью — независимо от того, что вы обещали. Кто-то парсит ваш API, автоматизирует ваши «особенности», кэширует ваши баги.
Нельзя рассматривать работу по обеспечению совместимости как «поддержку», а новые функции как «настоящую работу». Совместимость — это продукт.
Планируйте устаревание как миграции с учётом времени, инструментов и эмпатии. Большая часть «проектирования API» на самом деле является «прекращением поддержки устаревших функций».
9. Большинство «медленных» команд на самом деле являются проблемой координации.
Когда проект затягивается, инстинктивно винят исполнение: люди работают недостаточно усердно, технология неправильная, инженеров недостаточно. Обычно ничто из этого не является настоящей проблемой.
В крупных компаниях затраты на координацию растут в геометрической прогрессии по мере увеличения количества команд. Большая часть замедления на самом деле является результатом нарушения согласованности — люди создают неправильные вещи или правильные вещи несовместимыми способами.
10. Сосредоточьтесь на том, что вы можете контролировать. Игнорируйте то, что не можете контролировать.
В крупной компании бесчисленное множество переменных находятся вне вашего контроля — организационные изменения, решения руководства, сдвиги рынка, изменения в продукте. Зацикливание на этом порождает тревогу без возможности влиять на ситуацию.
Вы не можете контролировать, произойдёт ли реорганизация компании. Вы можете контролировать качество своей работы, то, как вы реагируете, и то, чему вы учитесь. Столкнувшись с неопределённостью, разбейте проблемы на части и определите конкретные действия, доступные вам.
Это не пассивное принятие, а стратегическая фокусировка. Энергия, потраченная на то, что вы не можете изменить, — это энергия, украденная у того, что вы можете изменить.
11. Абстракции не устраняют сложность. Они переносят её на тот день, когда вы дежурите.
Каждая абстракция — это ставка на то, что вам не нужно разбираться, что лежит в основе. Иногда эта ставка играет. Но часто получается, что что-то просачивается, и когда это происходит, хорошо бы про это знать.
Сеньоры продолжают изучать «низкоуровневые» вещи не из ностальгии, а чтобы быть готовым к моменту, когда абстракция даёт сбой, и вы остаётесь наедине с системой в 3 часа ночи. Используйте свой стек, но имейте в голове представление об основных его слабых местах.
12. Письменное изложение способствует ясности. Самый быстрый способ лучше чему-то научиться — это попытаться научить этому других.
Когда я объясняю концепцию другим — в документе, докладе, комментарии к коду, даже просто общаясь с ИИ — я обнаруживаю пробелы в собственном понимании. Сам акт превращения чего-либо в понятное для другого человека делает это более понятным для меня.
Если вы думаете, что что-то понимаете, попытайтесь объяснить это простым языком. Вы споткнётесь там, где ваше понимание поверхностно. Обучение — это отладка собственных ментальных моделей.
13. Работа, которая делает возможной работу других, бесценна… и незаметна.
Работа по обеспечению взаимодействия: документация, адаптация новых сотрудников, межкомандная координация, улучшение процессов — жизненно важна. Но если вы делаете это неосознанно, это может затормозить ваш технический прогресс и привести к выгоранию. Не делайте это просто, чтоб «быть полезным». Это должно быть явное, ограниченное, видимое воздействие.
Ограничьте помощь по времени. Чередуйте с другой работой. Превратите её в артефакты: документы, шаблоны, автоматизацию. И сделайте её заметной как воздействие, а не как черту вашего характера. Полезное, но невидимое — опасное сочетание для вашей карьеры.
14. Если вы выигрываете все споры, вы, вероятно, накапливаете скрытое сопротивление.
Я научился с подозрением относиться к собственной уверенности. Когда я слишком легко «побеждаю», обычно что-то не так. Люди бросают бороться с вами не потому, что вы их убедили, а потому, что они перестают пытаться – и они выражают это несогласие на практике, а не на совещаниях.
Настоящее согласование требует времени. Нужно действительно понимать другие точки зрения, учитывать обратную связь и иногда публично менять свое мнение.
Кратковременное чувство собственной правоты стоит гораздо меньше, чем долгосрочная уверенность при создании чего-то вместе с заинтересованными партнёрами.
Окончание следует…
Источник: https://addyosmani.com/blog/21-lessons/
21 Урок После 14 Лет в Google. Продолжение
Начало
8. В больших масштабах даже у ваших ошибок есть пользователи.
При достаточном количестве пользователей каждое наблюдаемое поведение становится зависимостью — независимо от того, что вы обещали. Кто-то парсит ваш API, автоматизирует ваши «особенности», кэширует ваши баги.
Нельзя рассматривать работу по обеспечению совместимости как «поддержку», а новые функции как «настоящую работу». Совместимость — это продукт.
Планируйте устаревание как миграции с учётом времени, инструментов и эмпатии. Большая часть «проектирования API» на самом деле является «прекращением поддержки устаревших функций».
9. Большинство «медленных» команд на самом деле являются проблемой координации.
Когда проект затягивается, инстинктивно винят исполнение: люди работают недостаточно усердно, технология неправильная, инженеров недостаточно. Обычно ничто из этого не является настоящей проблемой.
В крупных компаниях затраты на координацию растут в геометрической прогрессии по мере увеличения количества команд. Большая часть замедления на самом деле является результатом нарушения согласованности — люди создают неправильные вещи или правильные вещи несовместимыми способами.
10. Сосредоточьтесь на том, что вы можете контролировать. Игнорируйте то, что не можете контролировать.
В крупной компании бесчисленное множество переменных находятся вне вашего контроля — организационные изменения, решения руководства, сдвиги рынка, изменения в продукте. Зацикливание на этом порождает тревогу без возможности влиять на ситуацию.
Вы не можете контролировать, произойдёт ли реорганизация компании. Вы можете контролировать качество своей работы, то, как вы реагируете, и то, чему вы учитесь. Столкнувшись с неопределённостью, разбейте проблемы на части и определите конкретные действия, доступные вам.
Это не пассивное принятие, а стратегическая фокусировка. Энергия, потраченная на то, что вы не можете изменить, — это энергия, украденная у того, что вы можете изменить.
11. Абстракции не устраняют сложность. Они переносят её на тот день, когда вы дежурите.
Каждая абстракция — это ставка на то, что вам не нужно разбираться, что лежит в основе. Иногда эта ставка играет. Но часто получается, что что-то просачивается, и когда это происходит, хорошо бы про это знать.
Сеньоры продолжают изучать «низкоуровневые» вещи не из ностальгии, а чтобы быть готовым к моменту, когда абстракция даёт сбой, и вы остаётесь наедине с системой в 3 часа ночи. Используйте свой стек, но имейте в голове представление об основных его слабых местах.
12. Письменное изложение способствует ясности. Самый быстрый способ лучше чему-то научиться — это попытаться научить этому других.
Когда я объясняю концепцию другим — в документе, докладе, комментарии к коду, даже просто общаясь с ИИ — я обнаруживаю пробелы в собственном понимании. Сам акт превращения чего-либо в понятное для другого человека делает это более понятным для меня.
Если вы думаете, что что-то понимаете, попытайтесь объяснить это простым языком. Вы споткнётесь там, где ваше понимание поверхностно. Обучение — это отладка собственных ментальных моделей.
13. Работа, которая делает возможной работу других, бесценна… и незаметна.
Работа по обеспечению взаимодействия: документация, адаптация новых сотрудников, межкомандная координация, улучшение процессов — жизненно важна. Но если вы делаете это неосознанно, это может затормозить ваш технический прогресс и привести к выгоранию. Не делайте это просто, чтоб «быть полезным». Это должно быть явное, ограниченное, видимое воздействие.
Ограничьте помощь по времени. Чередуйте с другой работой. Превратите её в артефакты: документы, шаблоны, автоматизацию. И сделайте её заметной как воздействие, а не как черту вашего характера. Полезное, но невидимое — опасное сочетание для вашей карьеры.
14. Если вы выигрываете все споры, вы, вероятно, накапливаете скрытое сопротивление.
Я научился с подозрением относиться к собственной уверенности. Когда я слишком легко «побеждаю», обычно что-то не так. Люди бросают бороться с вами не потому, что вы их убедили, а потому, что они перестают пытаться – и они выражают это несогласие на практике, а не на совещаниях.
Настоящее согласование требует времени. Нужно действительно понимать другие точки зрения, учитывать обратную связь и иногда публично менять свое мнение.
Кратковременное чувство собственной правоты стоит гораздо меньше, чем долгосрочная уверенность при создании чего-то вместе с заинтересованными партнёрами.
Окончание следует…
Источник: https://addyosmani.com/blog/21-lessons/
👍5👎2
День 2741. #Карьера
21 Урок После 14 Лет в Google. Окончание
Начало
Продолжение
15. Когда показатель становится целью, он перестаёт измерять.
Каждый показатель, который вы предоставляете руководству, в итоге будет использован не по назначению. Не из-за злого умысла, а потому что люди оптимизируют то, что измеряется.
Если вы отслеживаете количество строк кода, вы получите больше строк, скорость - получите завышенные оценки.
Рекомендация для руководителей: отвечайте на каждый запрос метрики парой значений - скорости и качества/риска. Настаивайте на отслеживании тенденций, а не абсолютных значений. Цель — понимание, а не слежка.
16. Признание того, чего вы не знаете, более безопасно, чем притворство, что вы знаете.
Сеньоры, которые говорят: «Я не знаю», - не проявляют слабость — они дают разрешение. Когда лидер признаёт неуверенность, это сигнал, что другим безопасно делать то же самое. Альтернатива — культура, где все делают вид, что понимают, и проблемы остаются скрытыми, пока не рванёт.
В команде, где самый старший сотрудник никогда не признаёт, что чего-то не знает, не задаются вопросы, предположения не подвергаются сомнению, джуны молчат, потому что считают, что все остальные всё понимают.
Проявляйте любознательность, и вы получите команду, которая действительно учится.
17. Ваши связи переживут любые места работы.
Инженеры, которые вкладывали силы в отношения — внутри и вне компании — пожинали плоды на протяжении десятилетий. Они первыми узнавали о возможностях, быстрее налаживали связи, получали рекомендации на должности и становились соучредителями предприятий с людьми, с которыми они годами выстраивали доверительные отношения.
Ваша работа не вечна, а ваша сеть контактов — да. Относитесь к ней с любопытством и открытостью, а как в деловой рутине. Когда придёт время двигаться дальше, именно отношения часто открывают двери.
18. Большинство успехов в повышении производительности достигается за счёт сокращения работы, а не добавления умных фич.
Когда системы начинают тормозить, инстинктивно возникает желание добавить: слои кэширования, параллельную обработку, более интеллектуальные алгоритмы. Иногда это правильно. Но я видел больше улучшений производительности, когда задавался вопросом: «Что мы вычисляем, что нам не нужно?»
Удаление ненужной работы почти всегда оказывает большее влияние, чем ускорение выполнения необходимой работы. Самый быстрый код — это код, который никогда не запускается. Прежде чем оптимизировать, задайте себе вопрос, должна ли эта работа вообще существовать.
19. Процесс существует для уменьшения неопределённости, а не для создания бумажной волокиты.
Лучший процесс упрощает координацию и снижает затраты на устранение сбоев. Худший процесс — это бюрократический театр — он существует не для того, чтобы помогать, а для того, чтобы переложить вину, когда что-то пойдёт не так.
Если вы не можете объяснить, как процесс снижает риски или повышает ясность, это, вероятно, просто лишняя работа. И если люди тратят больше времени на документирование своей работы, чем на её выполнение, что-то пошло очень не так.
20. В итоге время ценнее денег. Действуйте соответственно.
В начале своей карьеры вы обмениваете время на деньги — и это нормально. Но в какой-то момент они меняются местами. Вы начинаете понимать, что время — это невозобновляемый ресурс.
Я наблюдал, как сеньоры выгорали, гонясь за следующим повышением с зарплату на несколько процентов выше. Некоторые из них добились успеха. Большинство же потом задавались вопросом, стоило ли это того, что они потеряли. Это не значит «работать на отвали», но важно знать, чем вы жертвуете, и делать это обдуманно.
21. Нет коротких путей, но есть эффект накопления.
Экспертность приходит благодаря осознанной практике — выход за границы своих текущих навыков, размышления, повторения. Годами. Короткого пути нет.
Хорошая новость: опыт накапливается, когда создаёшь новые вещи, а не просто повторяешь за кем-то. Создавайте переиспользуемые примитивы. Собирайте «набитые шишки» в заметки.
Инженер, который относится к своей карьере как к системе со сложными процентами, а не как к лотерее, как правило, добивается гораздо большего.
Итого
Советов много, но они сводятся к нескольким основным идеям: оставайтесь любопытными, оставайтесь скромными и помните, что работа всегда связана с людьми — с пользователями, для которых вы создаёте продукт, и с коллегами, с которыми вы работаете.
Карьера в инженерии достаточно длинная, чтобы совершить множество ошибок и всё равно добиться успеха. Инженеры, которыми я больше всего восхищаюсь, — не те, кто всё делал правильно, а те, кто учился на своих ошибках, делился своими открытиями и продолжал работать.
Источник: https://addyosmani.com/blog/21-lessons/
21 Урок После 14 Лет в Google. Окончание
Начало
Продолжение
15. Когда показатель становится целью, он перестаёт измерять.
Каждый показатель, который вы предоставляете руководству, в итоге будет использован не по назначению. Не из-за злого умысла, а потому что люди оптимизируют то, что измеряется.
Если вы отслеживаете количество строк кода, вы получите больше строк, скорость - получите завышенные оценки.
Рекомендация для руководителей: отвечайте на каждый запрос метрики парой значений - скорости и качества/риска. Настаивайте на отслеживании тенденций, а не абсолютных значений. Цель — понимание, а не слежка.
16. Признание того, чего вы не знаете, более безопасно, чем притворство, что вы знаете.
Сеньоры, которые говорят: «Я не знаю», - не проявляют слабость — они дают разрешение. Когда лидер признаёт неуверенность, это сигнал, что другим безопасно делать то же самое. Альтернатива — культура, где все делают вид, что понимают, и проблемы остаются скрытыми, пока не рванёт.
В команде, где самый старший сотрудник никогда не признаёт, что чего-то не знает, не задаются вопросы, предположения не подвергаются сомнению, джуны молчат, потому что считают, что все остальные всё понимают.
Проявляйте любознательность, и вы получите команду, которая действительно учится.
17. Ваши связи переживут любые места работы.
Инженеры, которые вкладывали силы в отношения — внутри и вне компании — пожинали плоды на протяжении десятилетий. Они первыми узнавали о возможностях, быстрее налаживали связи, получали рекомендации на должности и становились соучредителями предприятий с людьми, с которыми они годами выстраивали доверительные отношения.
Ваша работа не вечна, а ваша сеть контактов — да. Относитесь к ней с любопытством и открытостью, а как в деловой рутине. Когда придёт время двигаться дальше, именно отношения часто открывают двери.
18. Большинство успехов в повышении производительности достигается за счёт сокращения работы, а не добавления умных фич.
Когда системы начинают тормозить, инстинктивно возникает желание добавить: слои кэширования, параллельную обработку, более интеллектуальные алгоритмы. Иногда это правильно. Но я видел больше улучшений производительности, когда задавался вопросом: «Что мы вычисляем, что нам не нужно?»
Удаление ненужной работы почти всегда оказывает большее влияние, чем ускорение выполнения необходимой работы. Самый быстрый код — это код, который никогда не запускается. Прежде чем оптимизировать, задайте себе вопрос, должна ли эта работа вообще существовать.
19. Процесс существует для уменьшения неопределённости, а не для создания бумажной волокиты.
Лучший процесс упрощает координацию и снижает затраты на устранение сбоев. Худший процесс — это бюрократический театр — он существует не для того, чтобы помогать, а для того, чтобы переложить вину, когда что-то пойдёт не так.
Если вы не можете объяснить, как процесс снижает риски или повышает ясность, это, вероятно, просто лишняя работа. И если люди тратят больше времени на документирование своей работы, чем на её выполнение, что-то пошло очень не так.
20. В итоге время ценнее денег. Действуйте соответственно.
В начале своей карьеры вы обмениваете время на деньги — и это нормально. Но в какой-то момент они меняются местами. Вы начинаете понимать, что время — это невозобновляемый ресурс.
Я наблюдал, как сеньоры выгорали, гонясь за следующим повышением с зарплату на несколько процентов выше. Некоторые из них добились успеха. Большинство же потом задавались вопросом, стоило ли это того, что они потеряли. Это не значит «работать на отвали», но важно знать, чем вы жертвуете, и делать это обдуманно.
21. Нет коротких путей, но есть эффект накопления.
Экспертность приходит благодаря осознанной практике — выход за границы своих текущих навыков, размышления, повторения. Годами. Короткого пути нет.
Хорошая новость: опыт накапливается, когда создаёшь новые вещи, а не просто повторяешь за кем-то. Создавайте переиспользуемые примитивы. Собирайте «набитые шишки» в заметки.
Инженер, который относится к своей карьере как к системе со сложными процентами, а не как к лотерее, как правило, добивается гораздо большего.
Итого
Советов много, но они сводятся к нескольким основным идеям: оставайтесь любопытными, оставайтесь скромными и помните, что работа всегда связана с людьми — с пользователями, для которых вы создаёте продукт, и с коллегами, с которыми вы работаете.
Карьера в инженерии достаточно длинная, чтобы совершить множество ошибок и всё равно добиться успеха. Инженеры, которыми я больше всего восхищаюсь, — не те, кто всё делал правильно, а те, кто учился на своих ошибках, делился своими открытиями и продолжал работать.
Источник: https://addyosmani.com/blog/21-lessons/
👍5👎1
День 2742. #TipsAndTricks
Получаем Название Символа Unicode из Руны
Я уже писал в обзоре новинок .NET 11, что в C# добавлена поддержка рун. Однако, если у вас есть руна и вы хотите получить название этого Unicode-символа (например,
Библиотека ICU (International Components for Unicode) предоставляет функцию
Использование:
Источник: https://www.meziantou.net/get-a-unicode-character-name-from-a-rune-in-dotnet.htm
Получаем Название Символа Unicode из Руны
Я уже писал в обзоре новинок .NET 11, что в C# добавлена поддержка рун. Однако, если у вас есть руна и вы хотите получить название этого Unicode-символа (например,
GRINNING FACE для 😀), то в .NET на сегодняшний день нет встроенного API.Библиотека ICU (International Components for Unicode) предоставляет функцию
u_charName, которая возвращает название Unicode-символа. Обычно, начиная с .NET 5, вам не требуется обращаться к ней напрямую, т.к. .NET нативно её поддерживает. Но поддержка названий Unicode-символов пока не добавлена, поэтому придётся вызвать её напрямую:using System;
using System.Runtime.InteropServices;
using System.Text;
public static class IcuNative
{
private const string IcuLib = "icuuc";
private enum UCharNameChoice
{
U_UNICODE_CHAR_NAME = 0,
}
private enum UErrorCode
{
U_ZERO_ERROR = 0,
}
[DllImport(IcuLib, CallingConvention = CallingConvention.Cdecl,
EntryPoint = "u_charName")]
private static extern int u_charName(
int codepoint,
UCharNameChoice nameChoice,
byte[] buffer,
int bufferLength,
ref UErrorCode errorCode);
public static string GetCharName(Rune rune)
{
var buffer = new byte[128];
var error = UErrorCode.U_ZERO_ERROR;
int length = u_charName(
rune.Value,
UCharNameChoice.U_UNICODE_CHAR_NAME,
buffer,
buffer.Length,
ref error);
if (error != UErrorCode.U_ZERO_ERROR)
throw new InvalidOperationException(
$"Ошибка ICU: {error}");
return Encoding.ASCII
.GetString(buffer, 0, length);
}
}
Использование:
var rune = new Rune(0x1F600); // 😀
Console.WriteLine(IcuNative.GetCharName(rune));
// GRINNING FACE
var rune = new Rune(0x1F600); // 🌍
Console.WriteLine(IcuNative.GetCharName(rune));
// EARTH GLOBE EUROPE-AFRICA
Источник: https://www.meziantou.net/get-a-unicode-character-name-from-a-rune-in-dotnet.htm
👍1
День 2743. #ЗаметкиНаПолях #Microservices
Стоит ли Делить Это на Микросервисы?
Одни команды внедряют микросервисы, другие отказываются от них. Почти во всех случаях отказа решение о разделении принималось раньше, чем находились причины. Приложение может когда-нибудь масштабироваться. Монолит кажется всё более запутанным с каждым спринтом. В докладе на конференции сказали, что независимые развёртывания — это легко. Ничто из этого не является причиной для перехода на распределённую систему. Между тем, реальная цена высока: микросервисы обменивают локальную сложность на распределённую. Вызов метода становится сетевым. Транзакция становится сагой. Трассировка стека - распределённой трассировкой по трём сервисам и очереди. Иногда этот обмен стоит того. Ответьте на вопросы ниже, и решение делить или нет станет очевидным.
1. Действительно ли у разных частей системы разные потребности в масштабировании?
Не потребности, которые могут возникнуть когда-нибудь, а те, которые можно измерить сегодня: одна часть системы обрабатывает в 100 раз больший трафик или требует графического процессора, или потребляет память так, что приходится рассчитывать масштаб всего развёртывания под пиковые нагрузки этой части.
Это реальная причина. Выделение «горячего» пути, позволяющего масштабироваться (и падать) независимо, — один из лучших аргументов в пользу выделения сервиса.
Но сначала проверьте: большинство монолитов в .NET хорошо масштабируются за балансировщиком нагрузки. Если всё приложение комфортно работает на трёх экземплярах, у вас нет проблемы масштабирования, ради которой стоило бы выделять сервисы.
2. Действительно ли команды мешают друг другу?
Несколько команд, одна кодовая база и процесс релиза, где наполовину готовая функция команды А задерживает выпуск исправления командой Б. Развёртывания усложняются, релизы откладываются, постоянные конфликты слияния и т.п. Если это ваша реальность, то независимое развёртывание имеет реальную ценность.
Если у вас команда из 6 человек, то нет. Одна команда не настолько сильно мешает сама себе, чтобы оправдать эксплуатацию распределённой системы. Если у вас меньше двух полных команд, организационная польза от микросервисов равна нулю.
3. Можно ли разграничить данные?
Каждый сервис должен полностью владеть своими данными. Владение базой данных означает, что ни один другой сервис не будет напрямую считывать её таблицы, даже для одного удобного join'а. Если двум потенциальным сервисам постоянно требуются данные друг друга для ответа на базовые запросы, это не два сервиса, а один который вы собираетесь разорвать пополам.
Граница, которая выглядит чистой на организационной диаграмме, может быть безнадёжно запутана на уровне данных. Запутанность не исчезнет, когда вы добавите сеть между половинами. Она усугубится, потому что теперь каждое «соединение» — это вызов API, и сохранение целостности границ данных становится распределённой проблемой.
4. Требует ли что-либо независимого отказа или выпуска?
Некоторые части системы предъявляют требования, которых нет у остальных:
- Платёжный поток, который должен оставаться работоспособным даже при сбое модуля отчётности;
- Компонент, который обязан соответствовать государственным стандартам, и необходимо максимально уменьшить площадь проверяемого кода;
- Интеграция, которая выпускается еженедельно, в то время как ядро — ежеквартально;
и т.п.
Это законные требования к изоляции, и выделение сервиса — это чёткий способ их выразить. Обратите внимание, насколько они конкретны. Обычное желание изолировать сервис в этом списке отсутствует.
5. Можете ли вы позволить себе «налог на платформу»?
Прежде чем первый микросервис начнёт приносить какую-либо пользу, вам необходимы: платформа контейнеров, CI/CD для каждого сервиса, централизованное логирование, распределённая трассировка, брокер сообщений и паттерны надёжности, обеспечивающие безопасное взаимодействие между сервисами («Исходящие», «Идемпотентные потребители», «Повторные попытки»).
Это вступительный взнос, оплачиваемый в инженерных человеко-месяцах, ещё до получения первого преимущества.
Команда, которая не может выделить такие ресурсы, не получит более дешёвую версию в виде микросервисов. Она получит распределённый монолит без всех преимуществ и со всеми его издержками.
Оценка
- 4 или 5 ответов «да»: разделите проект и начните с одного сервиса. Выделите часть с наиболее чёткими границами и запустите её в продакшене на квартал, прежде чем выделять следующую.
- 2 или 3: вам нужны модули, а не сервисы. Модульный монолит даст границы, командное владение кодом и возможность разделения позже без платы за платформу. Границы, которые вы устанавливаете сейчас, — это именно то, что делает последующую миграцию механической, а не героической.
- 0 или 1: сохраните монолит и вложите сэкономленную энергию в его совершенствование.
Команды, которые пожалели о переходе на микросервисы, почти никогда не ошибались в выборе технологии. Они ошиблись полтора года назад на совещании, где было принято решение о разделении, ещё до того, как стали известны причины. Проведите опрос по пяти вопросам перед подобным совещанием у вас.
Источник: https://milanjovanovic.tech/blog/should-you-split-that-into-microservices-ask-these-5-questions-first
Стоит ли Делить Это на Микросервисы?
Одни команды внедряют микросервисы, другие отказываются от них. Почти во всех случаях отказа решение о разделении принималось раньше, чем находились причины. Приложение может когда-нибудь масштабироваться. Монолит кажется всё более запутанным с каждым спринтом. В докладе на конференции сказали, что независимые развёртывания — это легко. Ничто из этого не является причиной для перехода на распределённую систему. Между тем, реальная цена высока: микросервисы обменивают локальную сложность на распределённую. Вызов метода становится сетевым. Транзакция становится сагой. Трассировка стека - распределённой трассировкой по трём сервисам и очереди. Иногда этот обмен стоит того. Ответьте на вопросы ниже, и решение делить или нет станет очевидным.
1. Действительно ли у разных частей системы разные потребности в масштабировании?
Не потребности, которые могут возникнуть когда-нибудь, а те, которые можно измерить сегодня: одна часть системы обрабатывает в 100 раз больший трафик или требует графического процессора, или потребляет память так, что приходится рассчитывать масштаб всего развёртывания под пиковые нагрузки этой части.
Это реальная причина. Выделение «горячего» пути, позволяющего масштабироваться (и падать) независимо, — один из лучших аргументов в пользу выделения сервиса.
Но сначала проверьте: большинство монолитов в .NET хорошо масштабируются за балансировщиком нагрузки. Если всё приложение комфортно работает на трёх экземплярах, у вас нет проблемы масштабирования, ради которой стоило бы выделять сервисы.
2. Действительно ли команды мешают друг другу?
Несколько команд, одна кодовая база и процесс релиза, где наполовину готовая функция команды А задерживает выпуск исправления командой Б. Развёртывания усложняются, релизы откладываются, постоянные конфликты слияния и т.п. Если это ваша реальность, то независимое развёртывание имеет реальную ценность.
Если у вас команда из 6 человек, то нет. Одна команда не настолько сильно мешает сама себе, чтобы оправдать эксплуатацию распределённой системы. Если у вас меньше двух полных команд, организационная польза от микросервисов равна нулю.
3. Можно ли разграничить данные?
Каждый сервис должен полностью владеть своими данными. Владение базой данных означает, что ни один другой сервис не будет напрямую считывать её таблицы, даже для одного удобного join'а. Если двум потенциальным сервисам постоянно требуются данные друг друга для ответа на базовые запросы, это не два сервиса, а один который вы собираетесь разорвать пополам.
Граница, которая выглядит чистой на организационной диаграмме, может быть безнадёжно запутана на уровне данных. Запутанность не исчезнет, когда вы добавите сеть между половинами. Она усугубится, потому что теперь каждое «соединение» — это вызов API, и сохранение целостности границ данных становится распределённой проблемой.
4. Требует ли что-либо независимого отказа или выпуска?
Некоторые части системы предъявляют требования, которых нет у остальных:
- Платёжный поток, который должен оставаться работоспособным даже при сбое модуля отчётности;
- Компонент, который обязан соответствовать государственным стандартам, и необходимо максимально уменьшить площадь проверяемого кода;
- Интеграция, которая выпускается еженедельно, в то время как ядро — ежеквартально;
и т.п.
Это законные требования к изоляции, и выделение сервиса — это чёткий способ их выразить. Обратите внимание, насколько они конкретны. Обычное желание изолировать сервис в этом списке отсутствует.
5. Можете ли вы позволить себе «налог на платформу»?
Прежде чем первый микросервис начнёт приносить какую-либо пользу, вам необходимы: платформа контейнеров, CI/CD для каждого сервиса, централизованное логирование, распределённая трассировка, брокер сообщений и паттерны надёжности, обеспечивающие безопасное взаимодействие между сервисами («Исходящие», «Идемпотентные потребители», «Повторные попытки»).
Это вступительный взнос, оплачиваемый в инженерных человеко-месяцах, ещё до получения первого преимущества.
Команда, которая не может выделить такие ресурсы, не получит более дешёвую версию в виде микросервисов. Она получит распределённый монолит без всех преимуществ и со всеми его издержками.
Оценка
- 4 или 5 ответов «да»: разделите проект и начните с одного сервиса. Выделите часть с наиболее чёткими границами и запустите её в продакшене на квартал, прежде чем выделять следующую.
- 2 или 3: вам нужны модули, а не сервисы. Модульный монолит даст границы, командное владение кодом и возможность разделения позже без платы за платформу. Границы, которые вы устанавливаете сейчас, — это именно то, что делает последующую миграцию механической, а не героической.
- 0 или 1: сохраните монолит и вложите сэкономленную энергию в его совершенствование.
Команды, которые пожалели о переходе на микросервисы, почти никогда не ошибались в выборе технологии. Они ошиблись полтора года назад на совещании, где было принято решение о разделении, ещё до того, как стали известны причины. Проведите опрос по пяти вопросам перед подобным совещанием у вас.
Источник: https://milanjovanovic.tech/blog/should-you-split-that-into-microservices-ask-these-5-questions-first
👍9
День 2744. #ЧтоНовенького #VisualStudio
Заставьте Вашу Модель Думать Больше
Не каждый вопрос требует одинакового уровня обдумывания. Переименование переменной — это не то же самое, что отладка утечки памяти, и для них требуется разный уровень мышления.
Начиная с Visual Studio 18.9 Insiders 2 поддерживаемые модели теперь имеют регулятор Thinking (уровня мыслительных усилий), поэтому вы можете увеличить уровень рассуждений, когда задача действительно сложная, и уменьшить его, когда она простая. Результат настройки - более подходящие ответы и больший контроль над тем, сколько ресурсов вы тратите на их получение.
Параметр Thinking регулирует, сколько рассуждений выполняет модель, прежде чем дать ответ. Он отображается в виде набора именованных уровней, и какие именно уровни вы получите, зависит от модели (см. картинку ниже):
- Низкий — быстрые ответы с минимальными рассуждениями. Отлично подходит для простых вопросов и повседневных подсказок по коду, а также потребляет меньше токенов.
- Средний — сбалансированное мышление и скорость, обычно используется по умолчанию. Отлично подходит для типичных задач программирования.
- Высокий — более глубокое мышление для сложных проблем. Используйте его, когда сталкиваетесь со сложным алгоритмом, архитектурным решением или неуловимой ошибкой.
- Экстра высокий и Максимальный — максимальное мышление, предлагаемое некоторыми моделями, для самых сложных задач, где вы хотите, чтобы модель использовала все доступные возможности.
Какие уровни отображаются, зависит от выбранной вами модели, поэтому отображаемый список адаптирован к тому, что эта модель фактически поддерживает. Некоторые модели вообще не отображают этот элемент управления, и это тоже нормально: они будут показывать прочерк и продолжат работать как обычно.
Настройка выполняется быстро. В окне чата GitHub Copilot раскройте список выбора модели и выберите Manage models (Управление моделями), чтобы открыть расширенное окно управления моделями (на картинке ниже), и отрегулируйте уровень мышления для каждой модели.
В окне управления уровень мыслительной нагрузки находится в отдельном столбце рядом с возможностями каждой модели, размером контекста и стоимостью. Таким образом, то же место, где вы сравнивали модели, теперь используется для настройки их мыслительных процессов.
Более высокие уровни мыслительной нагрузки требуют больше рассуждений, что потребляет больше токенов. И наоборот. Это означает, что вы можете оставить модель на низком уровне для десятков небольших задач, которые заполняют обычную сессию, а затем повысить его до высокого уровня для одной сложной задачи, которая действительно в этом нуждается. Одна и та же модель, один и тот же диалог, каждый раз нужное количество размышлений.
Источник: https://devblogs.microsoft.com/visualstudio/tell-your-model-when-to-think-harder/
Заставьте Вашу Модель Думать Больше
Не каждый вопрос требует одинакового уровня обдумывания. Переименование переменной — это не то же самое, что отладка утечки памяти, и для них требуется разный уровень мышления.
Начиная с Visual Studio 18.9 Insiders 2 поддерживаемые модели теперь имеют регулятор Thinking (уровня мыслительных усилий), поэтому вы можете увеличить уровень рассуждений, когда задача действительно сложная, и уменьшить его, когда она простая. Результат настройки - более подходящие ответы и больший контроль над тем, сколько ресурсов вы тратите на их получение.
Параметр Thinking регулирует, сколько рассуждений выполняет модель, прежде чем дать ответ. Он отображается в виде набора именованных уровней, и какие именно уровни вы получите, зависит от модели (см. картинку ниже):
- Низкий — быстрые ответы с минимальными рассуждениями. Отлично подходит для простых вопросов и повседневных подсказок по коду, а также потребляет меньше токенов.
- Средний — сбалансированное мышление и скорость, обычно используется по умолчанию. Отлично подходит для типичных задач программирования.
- Высокий — более глубокое мышление для сложных проблем. Используйте его, когда сталкиваетесь со сложным алгоритмом, архитектурным решением или неуловимой ошибкой.
- Экстра высокий и Максимальный — максимальное мышление, предлагаемое некоторыми моделями, для самых сложных задач, где вы хотите, чтобы модель использовала все доступные возможности.
Какие уровни отображаются, зависит от выбранной вами модели, поэтому отображаемый список адаптирован к тому, что эта модель фактически поддерживает. Некоторые модели вообще не отображают этот элемент управления, и это тоже нормально: они будут показывать прочерк и продолжат работать как обычно.
Настройка выполняется быстро. В окне чата GitHub Copilot раскройте список выбора модели и выберите Manage models (Управление моделями), чтобы открыть расширенное окно управления моделями (на картинке ниже), и отрегулируйте уровень мышления для каждой модели.
В окне управления уровень мыслительной нагрузки находится в отдельном столбце рядом с возможностями каждой модели, размером контекста и стоимостью. Таким образом, то же место, где вы сравнивали модели, теперь используется для настройки их мыслительных процессов.
Более высокие уровни мыслительной нагрузки требуют больше рассуждений, что потребляет больше токенов. И наоборот. Это означает, что вы можете оставить модель на низком уровне для десятков небольших задач, которые заполняют обычную сессию, а затем повысить его до высокого уровня для одной сложной задачи, которая действительно в этом нуждается. Одна и та же модель, один и тот же диалог, каждый раз нужное количество размышлений.
Источник: https://devblogs.microsoft.com/visualstudio/tell-your-model-when-to-think-harder/
👎2
День 2745. #ЗаметкиНаПолях #Architecture
5 Архитектурных Ошибок, Которые Затрудняют Изменение Системы. Начало
Вы можете следовать любым лучшим практикам, использовать чистую архитектуру, event-sourcing, микросервисы или что-либо ещё популярное. В итоге вы всё равно оказываетесь в той же ситуации. У вас система, которую очень сложно изменить. Когда вы всё-таки вносите изменения, вы боитесь что-нибудь сломать. В конце концов всё чаще возникает мысль: «Лучше бы это переписать».
Кодовая база может даже выглядеть не так уж плохо. Она может быть организованной и относительно простой для понимания. Но почему-то очень трудно что-то изменить. Причина, вероятно, кроется в одной из 5 архитектурных ошибок. В каждом случае вы принимаете дорогостоящее решение, прежде чем по-настоящему поймёте бизнес или проблемы, которые пытаетесь решить. Вопрос не в том, какую архитектуру использовать? Вопрос должен звучать: «Что мы понимаем о проблеме, что оправдывает архитектурное решение, которое мы собираемся принять?»
1. Выбор архитектуры до понимания предметной области
Если в начале обсуждения проекта речь идёт о необходимости микросервисов, событийного моделирования, CQRS, Kafka и Kubernetes, но никто не может объяснить бизнес-процессы или рабочие процессы, вы делаете всё наоборот.
Может кто-нибудь объяснить ограничения? Какие проблемы с согласованностью могут возникнуть? Какие возможные режимы отказов? Какие части системы часто меняются? Где допустимы задержки?
Архитектурные шаблоны и инструменты сопряжены с компромиссами и дополнительной сложностью:
- Нужна независимая развёртываемость? Теперь у вас распределённые операции.
- Масштабировать части системы независимо? Придётся бороться со сбоями в сети.
- Автономия команды? Потребуется межсервисное и межкомандное взаимодействие.
- Изоляция между границами? Получите проблемы согласованности между этими границами.
Всегда есть компромисс. У каждого решения есть своя цена. Основное внимание следует уделить факторам, влияющим на вашу систему, и проблемам, которые вы пытаетесь решить.
Есть ли проблемы с согласованностью? Есть ли в системе часть, где правила быстро меняются и которую следует изолировать? Содержит ли рабочий процесс задержки, которые необходимо учитывать в последующих процессах?
Если вы сначала принимаете технические решения, вы лишь предполагаете, что когда-нибудь столкнётесь с проблемой, которую эти решения должны решить.
2. Создание сервисов сущностей, управляемых CRUD-операциями
При этом система полностью управляется своей моделью данных, а не поведением. Код может выглядеть отлично, быть хорошо организован. Но сущности на самом деле представляют собой просто таблицы или графы объектов, отображающие клиентов, заказы, товары и т.п.
Вот простой заказ:
Что нам говорит эта модель? Что какие-то данные существуют. Она ничего не говорит нам о том, как ведёт себя заказ. Можно ли его отменить? Возможно да, но как? Просто изменить статус? Когда заказ может быть отправлен? Должен ли он находиться в определённом статусе? Можно ли изменить статус после того, как товар зарезервирован? Модель предоставляет нам только данные. Вся бизнес-логика при этом обычно разбросана повсюду: в контроллерах, обработчиках сообщений, хранимых процедурах или даже во фронтэнде.
Сами по себе анемичные модель не плохи. В системе всегда есть части, которые ими по сути являются. Проблема в том, когда всё становится анемичными моделями, а разработка сводится к операциям над обновляемой записью:
В UpdateOrder вы просто изменяете свойства заказа. В CancelOrder вы явно сообщаете о намерении отменить заказ и указываете причину. Это выражает поведение и бизнес-намерение.
Вопрос, который вы должны задать себе: «Сообщает ли выполняемая мной операция бизнес-намерение?»
В этом примере цель в отмене заказа. Как только эта цель становится чётко выраженной, можно начать задавать полезные вопросы:
- Можно ли отменить заказ, учитывая, как давно он был размещён?
- Был ли уже зарезервирован товар на складе?
- Был ли заказ отправлен?
- Нужен ли клиенту возврат средств?
Эти вопросы вытекают из цели операции. Когда всё рассматривается как обновление, эта цель теряется. Также теряется естественное место, где должны существовать бизнес-правила, проверка и решения по рабочим процессам.
Окончание следует…
Источник: https://codeopinion.com/5-software-architecture-mistakes/
5 Архитектурных Ошибок, Которые Затрудняют Изменение Системы. Начало
Вы можете следовать любым лучшим практикам, использовать чистую архитектуру, event-sourcing, микросервисы или что-либо ещё популярное. В итоге вы всё равно оказываетесь в той же ситуации. У вас система, которую очень сложно изменить. Когда вы всё-таки вносите изменения, вы боитесь что-нибудь сломать. В конце концов всё чаще возникает мысль: «Лучше бы это переписать».
Кодовая база может даже выглядеть не так уж плохо. Она может быть организованной и относительно простой для понимания. Но почему-то очень трудно что-то изменить. Причина, вероятно, кроется в одной из 5 архитектурных ошибок. В каждом случае вы принимаете дорогостоящее решение, прежде чем по-настоящему поймёте бизнес или проблемы, которые пытаетесь решить. Вопрос не в том, какую архитектуру использовать? Вопрос должен звучать: «Что мы понимаем о проблеме, что оправдывает архитектурное решение, которое мы собираемся принять?»
1. Выбор архитектуры до понимания предметной области
Если в начале обсуждения проекта речь идёт о необходимости микросервисов, событийного моделирования, CQRS, Kafka и Kubernetes, но никто не может объяснить бизнес-процессы или рабочие процессы, вы делаете всё наоборот.
Может кто-нибудь объяснить ограничения? Какие проблемы с согласованностью могут возникнуть? Какие возможные режимы отказов? Какие части системы часто меняются? Где допустимы задержки?
Архитектурные шаблоны и инструменты сопряжены с компромиссами и дополнительной сложностью:
- Нужна независимая развёртываемость? Теперь у вас распределённые операции.
- Масштабировать части системы независимо? Придётся бороться со сбоями в сети.
- Автономия команды? Потребуется межсервисное и межкомандное взаимодействие.
- Изоляция между границами? Получите проблемы согласованности между этими границами.
Всегда есть компромисс. У каждого решения есть своя цена. Основное внимание следует уделить факторам, влияющим на вашу систему, и проблемам, которые вы пытаетесь решить.
Есть ли проблемы с согласованностью? Есть ли в системе часть, где правила быстро меняются и которую следует изолировать? Содержит ли рабочий процесс задержки, которые необходимо учитывать в последующих процессах?
Если вы сначала принимаете технические решения, вы лишь предполагаете, что когда-нибудь столкнётесь с проблемой, которую эти решения должны решить.
2. Создание сервисов сущностей, управляемых CRUD-операциями
При этом система полностью управляется своей моделью данных, а не поведением. Код может выглядеть отлично, быть хорошо организован. Но сущности на самом деле представляют собой просто таблицы или графы объектов, отображающие клиентов, заказы, товары и т.п.
Вот простой заказ:
public class Order
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public string Status { get; set; }
public decimal Total { get; set; }
}
Что нам говорит эта модель? Что какие-то данные существуют. Она ничего не говорит нам о том, как ведёт себя заказ. Можно ли его отменить? Возможно да, но как? Просто изменить статус? Когда заказ может быть отправлен? Должен ли он находиться в определённом статусе? Можно ли изменить статус после того, как товар зарезервирован? Модель предоставляет нам только данные. Вся бизнес-логика при этом обычно разбросана повсюду: в контроллерах, обработчиках сообщений, хранимых процедурах или даже во фронтэнде.
Сами по себе анемичные модель не плохи. В системе всегда есть части, которые ими по сути являются. Проблема в том, когда всё становится анемичными моделями, а разработка сводится к операциям над обновляемой записью:
UpdateOrder(order);
// или
CancelOrder(order, reason);
В UpdateOrder вы просто изменяете свойства заказа. В CancelOrder вы явно сообщаете о намерении отменить заказ и указываете причину. Это выражает поведение и бизнес-намерение.
Вопрос, который вы должны задать себе: «Сообщает ли выполняемая мной операция бизнес-намерение?»
В этом примере цель в отмене заказа. Как только эта цель становится чётко выраженной, можно начать задавать полезные вопросы:
- Можно ли отменить заказ, учитывая, как давно он был размещён?
- Был ли уже зарезервирован товар на складе?
- Был ли заказ отправлен?
- Нужен ли клиенту возврат средств?
Эти вопросы вытекают из цели операции. Когда всё рассматривается как обновление, эта цель теряется. Также теряется естественное место, где должны существовать бизнес-правила, проверка и решения по рабочим процессам.
Окончание следует…
Источник: https://codeopinion.com/5-software-architecture-mistakes/
👍9
День 2746. #ЗаметкиНаПолях #Architecture
5 Архитектурных Ошибок, Которые Затрудняют Изменение Системы. Окончание
Начало
3. Использование схемы БД в качестве точки интеграции
Представьте две части системы: отдел продаж и склад. Они используют один экземпляр БД, но база в некоторой степени разделена на данные, относящиеся к продажам, и данные, относящиеся к складу. Отдел продаж взаимодействует с данными, которыми он владеет. Но также запрашивает данные, которые, как кажется, принадлежат складу. Или хуже – изменяет данные склада. Кому на самом деле принадлежат эти данные?
В этот момент нет реального разделения и чёткого права собственности. Схема БД становится точкой интеграции. Некоторые скажут, что им всё равно. Они хотят использовать БД напрямую. Это может быть нормально, если вы понимаете последствия.
Когда данные записываются, кто контролирует, как они записываются? Кто является владельцем этого изменения? Какие другие части системы могут сломаться?
Одно из решений — предоставить API склада. Этот API становится контрактом, который отдел продаж может использовать для отправки запросов к складу или получения информации.
Представления (view) базы данных также могут быть контрактом. Они могут явно определять, к каким данным разрешён доступ другим частям системы. Совместное использование экземпляра БД не является автоматически проблемой. Разные части системы могут владеть отдельными схемами в рамках одного экземпляра базы.
Проблема в отсутствии права собственности. Когда БД доступна для всех, и что угодно может читать или изменять любые данные, в итоге начинают возникать проблемы. Вдруг заказ перешёл в недопустимое состояние. Как? Понятия не имеем. Что угодно могло его изменить. Обновление могло не пройти через необходимый процесс, бизнес-правила и проверку, требуемые для корректного перехода в новое состояние, потому что никто явно не отвечал за процесс его изменения.
4. Создание абстракций без понимания, что именно меняется
Обычно все начинается с разумной идеи: «Возможно, нам понадобится что-то заменить позже». Вы начинаете с общего сервиса. Затем получается универсальный рабочий процесс. «А что, если придётся заменить БД или брокер сообщений?» - создаётся абстракция и вокруг них. Только после всего этого вы начинаете создавать само приложение.
Звучит логично. Нас всех учат ценить повторное использование. Также постоянно возникает вопрос: «А что, если…?».
Проблема в том, что если у вас только одна реализация создаваемой абстракции, то, вероятно, у вас нет и самой абстракции. У вас недостаточно информации. Вы не понимаете другие конкретные реализации, где они пересекаются, а где различаются. Вы пытаетесь обобщить что-то, имея только один пример.
RabbitMQ и Azure Service Bus имеют существенные различия. Kafka — ещё больше отличий. Все они могут казаться связанными с отправкой и получением сообщений, но у них разная семантика. Если вы построите абстракцию вокруг одной, эта абстракция будет полностью сформирована единственной известной вам реализацией. Вы не устранили зависимость. Вы скрыли её за интерфейсом.
Плохая абстракция притворяется, что устраняет зависимость, хотя в реальности затрудняет понимание системы, т.к. разработчикам теперь нужно понимать как абстракцию, так и конкретную технологию, скрытую за ней. Создавайте абстракции, основываясь на реальных, понятных вам вариациях, а не на тех, которые, как вам кажется, будут существовать в будущем.
5. Создание системы с учетом масштабируемости, которой пока нет
Важное слово — «пока». Т.е. нужно оплатить всю стоимость создания системы такого уровня или типа масштабируемости, о котором вы даже не подозреваете.
Это не значит проектировать систему, которая не сможет масштабироваться. Но не нужно оплачивать всю стоимость авансом, основываясь на гипотетических сценариях:
- У нас могут быть миллионы пользователей.
- Может потребоваться заменить БД.
- В итоге система может стать глобальной.
Что на самом деле означают все эти утверждения? Система должна масштабироваться - это больше пользователей, больше данных, больше транзакций или несколько языков?
Также необходимо понимать, что логические границы и физические границы — это не одно и то же. Определяя логические границы и избегая ненужной их взаимосвязи, вы значительно повышаете свои шансы на масштабирование различных частей системы при необходимости. Поэтому модульный монолит и микросервисы часто масштабируются одинаково. Вы можете определить осмысленные границы, не превращая сразу каждую границу в отдельно развёртываемый сервис. Начните с границ. Определитесь с моделью физического развёртывания, когда у вас будет достаточно информации, чтобы это оправдать.
Итого
В каждом случае проблема одна и та же. Вы принимаете дорогостоящее решение, не имея достаточной информации. Архитектура не должна начинаться с шаблонов, фреймворков или инфраструктуры. Она должна начинаться с понимания бизнеса, рабочих процессов, ограничений и реальных проблем, которые система должна решать. Тогда вы сможете принять подходящее архитектурное решение.
Источник: https://codeopinion.com/5-software-architecture-mistakes/
5 Архитектурных Ошибок, Которые Затрудняют Изменение Системы. Окончание
Начало
3. Использование схемы БД в качестве точки интеграции
Представьте две части системы: отдел продаж и склад. Они используют один экземпляр БД, но база в некоторой степени разделена на данные, относящиеся к продажам, и данные, относящиеся к складу. Отдел продаж взаимодействует с данными, которыми он владеет. Но также запрашивает данные, которые, как кажется, принадлежат складу. Или хуже – изменяет данные склада. Кому на самом деле принадлежат эти данные?
В этот момент нет реального разделения и чёткого права собственности. Схема БД становится точкой интеграции. Некоторые скажут, что им всё равно. Они хотят использовать БД напрямую. Это может быть нормально, если вы понимаете последствия.
Когда данные записываются, кто контролирует, как они записываются? Кто является владельцем этого изменения? Какие другие части системы могут сломаться?
Одно из решений — предоставить API склада. Этот API становится контрактом, который отдел продаж может использовать для отправки запросов к складу или получения информации.
Представления (view) базы данных также могут быть контрактом. Они могут явно определять, к каким данным разрешён доступ другим частям системы. Совместное использование экземпляра БД не является автоматически проблемой. Разные части системы могут владеть отдельными схемами в рамках одного экземпляра базы.
Проблема в отсутствии права собственности. Когда БД доступна для всех, и что угодно может читать или изменять любые данные, в итоге начинают возникать проблемы. Вдруг заказ перешёл в недопустимое состояние. Как? Понятия не имеем. Что угодно могло его изменить. Обновление могло не пройти через необходимый процесс, бизнес-правила и проверку, требуемые для корректного перехода в новое состояние, потому что никто явно не отвечал за процесс его изменения.
4. Создание абстракций без понимания, что именно меняется
Обычно все начинается с разумной идеи: «Возможно, нам понадобится что-то заменить позже». Вы начинаете с общего сервиса. Затем получается универсальный рабочий процесс. «А что, если придётся заменить БД или брокер сообщений?» - создаётся абстракция и вокруг них. Только после всего этого вы начинаете создавать само приложение.
Звучит логично. Нас всех учат ценить повторное использование. Также постоянно возникает вопрос: «А что, если…?».
Проблема в том, что если у вас только одна реализация создаваемой абстракции, то, вероятно, у вас нет и самой абстракции. У вас недостаточно информации. Вы не понимаете другие конкретные реализации, где они пересекаются, а где различаются. Вы пытаетесь обобщить что-то, имея только один пример.
RabbitMQ и Azure Service Bus имеют существенные различия. Kafka — ещё больше отличий. Все они могут казаться связанными с отправкой и получением сообщений, но у них разная семантика. Если вы построите абстракцию вокруг одной, эта абстракция будет полностью сформирована единственной известной вам реализацией. Вы не устранили зависимость. Вы скрыли её за интерфейсом.
Плохая абстракция притворяется, что устраняет зависимость, хотя в реальности затрудняет понимание системы, т.к. разработчикам теперь нужно понимать как абстракцию, так и конкретную технологию, скрытую за ней. Создавайте абстракции, основываясь на реальных, понятных вам вариациях, а не на тех, которые, как вам кажется, будут существовать в будущем.
5. Создание системы с учетом масштабируемости, которой пока нет
Важное слово — «пока». Т.е. нужно оплатить всю стоимость создания системы такого уровня или типа масштабируемости, о котором вы даже не подозреваете.
Это не значит проектировать систему, которая не сможет масштабироваться. Но не нужно оплачивать всю стоимость авансом, основываясь на гипотетических сценариях:
- У нас могут быть миллионы пользователей.
- Может потребоваться заменить БД.
- В итоге система может стать глобальной.
Что на самом деле означают все эти утверждения? Система должна масштабироваться - это больше пользователей, больше данных, больше транзакций или несколько языков?
Также необходимо понимать, что логические границы и физические границы — это не одно и то же. Определяя логические границы и избегая ненужной их взаимосвязи, вы значительно повышаете свои шансы на масштабирование различных частей системы при необходимости. Поэтому модульный монолит и микросервисы часто масштабируются одинаково. Вы можете определить осмысленные границы, не превращая сразу каждую границу в отдельно развёртываемый сервис. Начните с границ. Определитесь с моделью физического развёртывания, когда у вас будет достаточно информации, чтобы это оправдать.
Итого
В каждом случае проблема одна и та же. Вы принимаете дорогостоящее решение, не имея достаточной информации. Архитектура не должна начинаться с шаблонов, фреймворков или инфраструктуры. Она должна начинаться с понимания бизнеса, рабочих процессов, ограничений и реальных проблем, которые система должна решать. Тогда вы сможете принять подходящее архитектурное решение.
Источник: https://codeopinion.com/5-software-architecture-mistakes/
👍7
День 2747. #TipsAndTricks #VisualStudio
Загадочный Флажок .dev.localhost
При создании нового проекта ASP.NET Core в Visual Studio есть флажок, который легко пропустить: Use the .dev.localhost TLD in the application URL (Использовать домен верхнего уровня .dev.localhost в URL приложения). Давайте разберёмся, что он делает.
Проблема
Когда вы работаете над несколькими локальными веб-проектами, все они располагаются по одному и тому же адресу
Есть вторая, менее заметная проблема: поскольку все используют имя
Что такое .dev.localhost?
Начиная с .NET 10, ASP.NET Core развивает эту идею и добавляет полноценную поддержку поддомена
вы получите:
Для этого и флажок. Настройка также доступна из командной строки, если вы не используете мастер Visual Studio:
Примечание: Kestrel распознаёт адреса
Зачем?
- Вы сможете различать свои приложения. Название проекта отображается прямо в адресной строке, а не в номере порта, который вам нужно запоминать.
- Никаких конфликтов кук и хранилищ. Поскольку каждое приложение получает свой собственный поддомен, хранилище браузера, привязанное к домену, больше не является общим для всех ваших локальных проектов.
- HTTPS по-прежнему работает «из коробки». Сертификат разработчика ASP.NET Core уже указывает
- Ничего не сломается. Kestrel продолжает прослушивать обычный
Единственная загвоздка: Safari
Safari на macOS не разрешает имена
То же самое относится и к клиентам, не являющимся браузерами. Некоторые HTTP-клиенты и инструменты разрешают имена
Источник: https://bartwullems.blogspot.com/2026/07/the-mysterious-devlocalhost-checkbox.html
Загадочный Флажок .dev.localhost
При создании нового проекта ASP.NET Core в Visual Studio есть флажок, который легко пропустить: Use the .dev.localhost TLD in the application URL (Использовать домен верхнего уровня .dev.localhost в URL приложения). Давайте разберёмся, что он делает.
Проблема
Когда вы работаете над несколькими локальными веб-проектами, все они располагаются по одному и тому же адресу
localhost. Различить их можно только по номеру порта. Если у вас в работе 3 проекта, то в браузере может быть localhost:5001, localhost:5215, localhost:7099 — и вы не будете знать, какой из них какой, пока не посмотрите на страницу.Есть вторая, менее заметная проблема: поскольку все используют имя
localhost, файлы cookie и другие хранилища браузера, привязанные к домену, также используются всеми вашими локальными приложениями. Это обычно нежелательно при тестировании.Что такое .dev.localhost?
.localhost — это зарезервированный домен верхнего уровня, определённый в RFC2606 и RFC6761 специально для локального тестирования. Современные браузеры уже разрешают всё, что заканчивается на .localhost, напрямую в локальный адрес (127.0.0.1/::1), поэтому myapp.localhost ведет себя точно так же, как localhost, если только вы не отредактировали файл .hosts или настройки DNS.Начиная с .NET 10, ASP.NET Core развивает эту идею и добавляет полноценную поддержку поддомена
.dev.localhost. Шаблоны проектов для ASP.NET Core Empty и Blazor Web App могут объединить имя вашего проекта с этим суффиксом, поэтому вместо:https://localhost:7099
вы получите:
https://myapp.dev.localhost:7099
Для этого и флажок. Настройка также доступна из командной строки, если вы не используете мастер Visual Studio:
dotnet new web -n MyApp --localhost-tld
Примечание: Kestrel распознаёт адреса
.localhost. Когда ваш профиль запуска или ASPNETCORE_URLS указывает на имя .dev.localhost, Kestrel привязывается только к локальному адресу (127.0.0.1/::1), а не ко всем интерфейсам. Он также регистрирует как адрес .localhost, так и обычный localhost при запуске, поэтому оба варианта по-прежнему работают.Зачем?
- Вы сможете различать свои приложения. Название проекта отображается прямо в адресной строке, а не в номере порта, который вам нужно запоминать.
- Никаких конфликтов кук и хранилищ. Поскольку каждое приложение получает свой собственный поддомен, хранилище браузера, привязанное к домену, больше не является общим для всех ваших локальных проектов.
- HTTPS по-прежнему работает «из коробки». Сертификат разработчика ASP.NET Core уже указывает
*.dev.localhost в качестве альтернативного имени субъекта. Вам не нужно ничего дополнительно генерировать — dotnet dev-certs https уже это обеспечивает. Сертификат с подстановочным знаком для *.localhost сам по себе недействителен для домена верхнего уровня, именно поэтому существует поддомен .dev.- Ничего не сломается. Kestrel продолжает прослушивать обычный
localhost, поэтому инструменты или скрипты, которые обращаются к localhost:7099, продолжат работать.Единственная загвоздка: Safari
Safari на macOS не разрешает имена
*.localhost автоматически. Если вы тестируете в Safari, используйте обычный адрес localhost.То же самое относится и к клиентам, не являющимся браузерами. Некоторые HTTP-клиенты и инструменты разрешают имена
.localhost через обычный стек DNS вместо обработки их в особом порядке, и, если ваш DNS не знает, что с этим делать, запрос просто завершится неудачей. В таких случаях продолжайте использовать обычный localhost.Источник: https://bartwullems.blogspot.com/2026/07/the-mysterious-devlocalhost-checkbox.html
👍12👎1
День 2748. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
42. Шаблоны взаимодействия в микросервисах
«Можете ли вы рассказать о различных шаблонах взаимодействия, используемых в микросервисной архитектуре, и как бы вы реализовали их в приложении .NET?»
Хороший ответ
Эффективная коммуникация между микросервисами имеет решающее значение для успеха архитектуры. Существует несколько распространённых шаблонов коммуникации, каждый из которых подходит для разных сценариев:
- HTTP/REST/gRPC: наиболее распространённый метод синхронной коммуникации, при котором сервисы используют HTTP-запросы для связи. Он прост и не имеет состояния.
- Очереди сообщений: используются для децентрализованной, надёжной асинхронной коммуникации. Они помогают справляться с пиковыми нагрузками и обеспечивают механизм, гарантирующий, что данные не будут потеряны при передаче.
- Событийно-ориентированная модель «публикация/подписка»: эта модель усиливает децентрализацию сервисов, позволяя сервисам подписываться на определённые события, не зная источника этих событий.
Преимущества
- Децентрализация: сервисы не зависят друг от друга напрямую, что повышает отказоустойчивость и масштабируемость.
- Масштабируемость: асинхронные и событийно-ориентированные подходы позволяют системам эффективно обрабатывать изменяющиеся нагрузки.
- Надёжность: очереди сообщений гарантируют доставку сообщений даже если части системы выходят из строя.
В таких реализациях крайне важно обрабатывать сбои, повторные попытки и идемпотентность, особенно в асинхронных сценариях, чтобы обеспечить надёжность и согласованность системы.
Часто встречающийся ошибочный ответ
«Для связи между микросервисами используются HTTP-запросы. Это просто и гарантирует, что сервисы могут общаться в режиме реального времени».
Почему это неправильно
- Чрезмерная зависимость от синхронной связи: этот подход игнорирует преимущества асинхронных моделей связи. Хотя HTTP прост и эффективен для определённых сценариев, он может создавать тесную взаимосвязь и плохо масштабируется при высокой нагрузке или в сложных системах.
- Игнорирование преимуществ очередей сообщений: не используя очереди сообщений или архитектуры, управляемые событиями, ответ упускает из виду устойчивость, которую предлагают эти модели, особенно с точки зрения надёжности, слабой связанности и асинхронной обработки.
- Риск системных сбоев: упор исключительно на HTTP-запросы может привести к сбоям, если какая-либо отдельная часть системы станет недоступной, что повлияет на доступность всей системы.
Эта ошибка часто возникает из-за недостаточного понимания лучших архитектурных практик в микросервисах или чрезмерного упрощения потребностей в коммуникации в распределённых системах. Это отражает необходимость более глубокого знания различных стратегий коммуникации и соответствующих сценариев их использования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
42. Шаблоны взаимодействия в микросервисах
«Можете ли вы рассказать о различных шаблонах взаимодействия, используемых в микросервисной архитектуре, и как бы вы реализовали их в приложении .NET?»
Хороший ответ
Эффективная коммуникация между микросервисами имеет решающее значение для успеха архитектуры. Существует несколько распространённых шаблонов коммуникации, каждый из которых подходит для разных сценариев:
- HTTP/REST/gRPC: наиболее распространённый метод синхронной коммуникации, при котором сервисы используют HTTP-запросы для связи. Он прост и не имеет состояния.
- Очереди сообщений: используются для децентрализованной, надёжной асинхронной коммуникации. Они помогают справляться с пиковыми нагрузками и обеспечивают механизм, гарантирующий, что данные не будут потеряны при передаче.
- Событийно-ориентированная модель «публикация/подписка»: эта модель усиливает децентрализацию сервисов, позволяя сервисам подписываться на определённые события, не зная источника этих событий.
Преимущества
- Децентрализация: сервисы не зависят друг от друга напрямую, что повышает отказоустойчивость и масштабируемость.
- Масштабируемость: асинхронные и событийно-ориентированные подходы позволяют системам эффективно обрабатывать изменяющиеся нагрузки.
- Надёжность: очереди сообщений гарантируют доставку сообщений даже если части системы выходят из строя.
В таких реализациях крайне важно обрабатывать сбои, повторные попытки и идемпотентность, особенно в асинхронных сценариях, чтобы обеспечить надёжность и согласованность системы.
Часто встречающийся ошибочный ответ
«Для связи между микросервисами используются HTTP-запросы. Это просто и гарантирует, что сервисы могут общаться в режиме реального времени».
Почему это неправильно
- Чрезмерная зависимость от синхронной связи: этот подход игнорирует преимущества асинхронных моделей связи. Хотя HTTP прост и эффективен для определённых сценариев, он может создавать тесную взаимосвязь и плохо масштабируется при высокой нагрузке или в сложных системах.
- Игнорирование преимуществ очередей сообщений: не используя очереди сообщений или архитектуры, управляемые событиями, ответ упускает из виду устойчивость, которую предлагают эти модели, особенно с точки зрения надёжности, слабой связанности и асинхронной обработки.
- Риск системных сбоев: упор исключительно на HTTP-запросы может привести к сбоям, если какая-либо отдельная часть системы станет недоступной, что повлияет на доступность всей системы.
Эта ошибка часто возникает из-за недостаточного понимания лучших архитектурных практик в микросервисах или чрезмерного упрощения потребностей в коммуникации в распределённых системах. Это отражает необходимость более глубокого знания различных стратегий коммуникации и соответствующих сценариев их использования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍3👎3
День 2749. #BestPractices #SQL
Как Оптимизировать SQL-запросы. Часть 1
Медленный SQL-запрос — один из самых простых способов испортить быстрое приложение. У вас может быть чистая архитектура, отличный кэш и мощный сервер — и всё равно страница может зависать из-за того, что один запрос сканирует миллион строк без индекса. Большинство успехов достигается за счёт одного и того же небольшого набора методов, применяемых снова и снова. Некоторые из них очевидны. Некоторые противоречат советам, которые вы, вероятно, уже слышали. Советы можно условно разделить на 6 групп.
Замечание: здесь мы рассматриваем PostgreSQL. Те же принципы применимы и к другим БД, хотя точный синтаксис может отличаться.
Группа I. Написание запросов, удобных для индексации
Индекс полезен только в том случае, если ваш запрос позволяет БД его использовать.
1. Разумно используйте индексы
Индексы — самый мощный метод повышения производительности чтения. Это отсортированная структура данных, которая позволяет БД находить строки, не сканируя всю таблицу, подобно тому, как оглавление книги избавляет вас от необходимости пролистывать все страницы.
Создавайте индексы по столбцам, по которым вы чаще всего выполняете фильтрацию, соединение, сортировку и группировку — столбцам в
Этот индекс ускоряет запросы, фильтрующие по статусу, а также по статусу и дате заказа. Порядок столбцов имеет значение: индекс по
Вы также можете создать покрывающий индекс, который хранит дополнительные значения столбцов внутри индекса. Это полезно для небольших частых запросов на поиск, когда запросу нужны только столбцы, доступные в индексе, чтобы БД могла избежать чтения фактических строк таблицы:
Замечание: индексы не бесплатны. Каждый индекс необходимо обновлять при каждой вставке, обновлении и удалении, и это занимает место на диске. Индексируйте столбцы, которые фактически используются вашими запросами, а не каждый столбец.
2. Избегайте функций в WHERE
Обёртывание столбца в функцию — один из наиболее распространённых способов случайно отключить индекс. Когда вы вызываете функцию для столбца, БД должна вычислить значение функции для каждой строки, прежде чем сможет сравнить его, поэтому она не может использовать индекс для исходного столбца:
Перепишите условие так, чтобы оно сравнивало исходный столбец с диапазоном:
Оба запроса возвращают одни и те же строки, но только второй может использовать индекс по
То же правило применяется к
3. Избегайте символов подстановки в начале запроса LIKE
Шаблон LIKE, начинающийся с символа подстановки, не может использовать обычный индекс. БД считывает индекс слева направо, поэтому ей необходимо знать начало значения. Шаблон типа
Если вам действительно нужно выполнить contains-поиск в тексте, используйте полнотекстовый поиск или триграммный индекс (расширение pg_trgm в PostgreSQL), созданный специально для этой задачи.
4. Точное соответствие типов данных
Сравнение двух разных типов данных заставляет БД преобразовывать один из них, и это преобразование может незаметно отключить индекс.
Если столбец является целым числом, но вы сравниваете его со строкой, или соединяете int-ключ с bigint-ключом, БД добавляет неявное приведение типов — и индекс по исходному столбцу может быть пропущен. Сохраняйте одинаковые типы с обеих сторон каждого соединения и фильтрации:
Определите
Продолжение следует…
https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как Оптимизировать SQL-запросы. Часть 1
Медленный SQL-запрос — один из самых простых способов испортить быстрое приложение. У вас может быть чистая архитектура, отличный кэш и мощный сервер — и всё равно страница может зависать из-за того, что один запрос сканирует миллион строк без индекса. Большинство успехов достигается за счёт одного и того же небольшого набора методов, применяемых снова и снова. Некоторые из них очевидны. Некоторые противоречат советам, которые вы, вероятно, уже слышали. Советы можно условно разделить на 6 групп.
Замечание: здесь мы рассматриваем PostgreSQL. Те же принципы применимы и к другим БД, хотя точный синтаксис может отличаться.
Группа I. Написание запросов, удобных для индексации
Индекс полезен только в том случае, если ваш запрос позволяет БД его использовать.
1. Разумно используйте индексы
Индексы — самый мощный метод повышения производительности чтения. Это отсортированная структура данных, которая позволяет БД находить строки, не сканируя всю таблицу, подобно тому, как оглавление книги избавляет вас от необходимости пролистывать все страницы.
Создавайте индексы по столбцам, по которым вы чаще всего выполняете фильтрацию, соединение, сортировку и группировку — столбцам в
WHERE, JOIN, ORDER BY и GROUP BY. Когда используется несколько столбцов одновременно, один составной индекс, охватывающий их, намного лучше, чем отдельные индексы по одному столбцу:-- Составной индекс для частой фильтрации по статусу и дате
CREATE INDEX idx_orders_status_order_date
ON orders (status, order_date);
Этот индекс ускоряет запросы, фильтрующие по статусу, а также по статусу и дате заказа. Порядок столбцов имеет значение: индекс по
(status, order_date) помогает запросам, которые сначала фильтруют по статусу, но не запросам, которые фильтруют только по дате заказа.Вы также можете создать покрывающий индекс, который хранит дополнительные значения столбцов внутри индекса. Это полезно для небольших частых запросов на поиск, когда запросу нужны только столбцы, доступные в индексе, чтобы БД могла избежать чтения фактических строк таблицы:
-- Добавляем часто читаемые данные
CREATE INDEX idx_orders_status_order_date_covering
ON orders (status, order_date)
INCLUDE (customer_id, total_amount);
SELECT customer_id, total_amount
FROM orders
WHERE status = 'paid'
AND order_date >= DATE '2026-01-01';
Замечание: индексы не бесплатны. Каждый индекс необходимо обновлять при каждой вставке, обновлении и удалении, и это занимает место на диске. Индексируйте столбцы, которые фактически используются вашими запросами, а не каждый столбец.
2. Избегайте функций в WHERE
Обёртывание столбца в функцию — один из наиболее распространённых способов случайно отключить индекс. Когда вы вызываете функцию для столбца, БД должна вычислить значение функции для каждой строки, прежде чем сможет сравнить его, поэтому она не может использовать индекс для исходного столбца:
-- Плохо: функция по order_date отключает сканирование по индексу
SELECT * FROM orders
WHERE EXTRACT(YEAR FROM order_date) = 2025;
Перепишите условие так, чтобы оно сравнивало исходный столбец с диапазоном:
-- Хорошо: диапазон по чистому значению столбца
SELECT * FROM orders
WHERE order_date >= '2025-01-01'
AND order_date < '2026-01-01';
Оба запроса возвращают одни и те же строки, но только второй может использовать индекс по
order_date.То же правило применяется к
LOWER(email), CAST(…) и арифметическим операциям по столбцу. Если вам часто нужно фильтровать по вычисляемому значению, создайте вместо этого функциональный индекс для этого конкретного выражения.3. Избегайте символов подстановки в начале запроса LIKE
Шаблон LIKE, начинающийся с символа подстановки, не может использовать обычный индекс. БД считывает индекс слева направо, поэтому ей необходимо знать начало значения. Шаблон типа
'%son' скрывает начало и заставляет выполнять полное сканирование:-- Плохо: полное сканирование таблицы
SELECT * FROM customers
WHERE last_name LIKE '%son';
-- Хорошо: сканирование индекса по известному префиксу
SELECT * FROM customers
WHERE last_name LIKE 'Anders%';
Если вам действительно нужно выполнить contains-поиск в тексте, используйте полнотекстовый поиск или триграммный индекс (расширение pg_trgm в PostgreSQL), созданный специально для этой задачи.
4. Точное соответствие типов данных
Сравнение двух разных типов данных заставляет БД преобразовывать один из них, и это преобразование может незаметно отключить индекс.
Если столбец является целым числом, но вы сравниваете его со строкой, или соединяете int-ключ с bigint-ключом, БД добавляет неявное приведение типов — и индекс по исходному столбцу может быть пропущен. Сохраняйте одинаковые типы с обеих сторон каждого соединения и фильтрации:
CREATE TABLE logs (
log_id int PRIMARY KEY,
event_date timestamptz NOT NULL,
user_id int NOT NULL
);
Определите
logs.user_id так, чтобы он соответствовал типу users.id, и тогда соединение будет использовать индекс с обеих сторон. Продолжение следует…
https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍10
День 2750. #BestPractices #SQL
Как оптимизировать SQL-запросы. Часть 2
Часть 1
Часть II. Извлекайте только необходимые данные
Быстрее всего обрабатываются данные, которые вы не читаете. Каждый столбец и каждая строка, которые вы извлекаете, требуют операций ввода-вывода на диске, памяти и сетевого времени. Следующие методы позволяют максимально сократить результирующий набор данных на ранней стадии.
1. Прекратите использовать SELECT *
Явное указание столбцов также безопаснее. Ваш запрос не изменит свою форму и не сломается незаметно, когда кто-то добавит или изменит порядок столбцов.
2. Фильтрация на ранних этапах
Чем меньше набор данных, с которым вы работаете, тем быстрее происходит обработка данных на последующих этапах. Возможно, вы слышали распространённый совет: «Применяйте наиболее избирательные фильтры в начале, чтобы сократить количество строк до того, как их обработают соединения и агрегирования».
Замечание: в большинстве случаев не нужно размещать эти фильтры вручную. Современный стоимостной планировщик сам размещает предикаты в
3. Keyset-пагинация вместо OFFSET, когда возможно
Стоимость каждой страницы одинакова, будь то страница 2 или страница 2000, поскольку индекс переходит непосредственно к
Компромисс: keyset-пагинация обеспечивает быструю навигацию по следующей и предыдущей страницам, но вы не можете реализовать случайные переходы к любому номеру страницы.
4. Запрашивайте только то, что изменилось
Повторное чтение всей таблицы при каждом запуске неэффективно, если изменилось всего несколько строк. Отслеживайте «водяной знак» — последнюю обработанную точку — и извлекайте только строки, более новые, чем он:
Это превращает полное сканирование таблицы в небольшое чтение диапазона с помощью индекса. Индексируйте
Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Часть 2
Часть 1
Часть II. Извлекайте только необходимые данные
Быстрее всего обрабатываются данные, которые вы не читаете. Каждый столбец и каждая строка, которые вы извлекаете, требуют операций ввода-вывода на диске, памяти и сетевого времени. Следующие методы позволяют максимально сократить результирующий набор данных на ранней стадии.
1. Прекратите использовать SELECT *
SELECT * извлекает все столбцы, включая те, которые вам не нужны. Это означает больше данных для чтения с диска, больше данных для передачи по сети и больше памяти для хранения — всё это для столбцов, которые ваш код игнорирует. Это также блокирует покрывающие индексы, когда индекс сам по себе может ответить на запрос, не затрагивая таблицу:-- Плохо: извлечение всех столбцов
SELECT * FROM customers;
-- Хорошо: только используемые столбцы
SELECT customer_id, first_name, last_name
FROM customers;
Явное указание столбцов также безопаснее. Ваш запрос не изменит свою форму и не сломается незаметно, когда кто-то добавит или изменит порядок столбцов.
2. Фильтрация на ранних этапах
Чем меньше набор данных, с которым вы работаете, тем быстрее происходит обработка данных на последующих этапах. Возможно, вы слышали распространённый совет: «Применяйте наиболее избирательные фильтры в начале, чтобы сократить количество строк до того, как их обработают соединения и агрегирования».
SELECT o.order_id, o.total
FROM orders o
WHERE o.completed = true
AND o.order_date >= '2026-01-01'
AND o.order_date < '2026-02-01';
Замечание: в большинстве случаев не нужно размещать эти фильтры вручную. Современный стоимостной планировщик сам размещает предикаты в
WHERE как можно раньше и самостоятельно переупорядочивает соединения. Изменение порядка в тексте предложения WHERE редко меняет план. На самом деле помогает предоставление планировщику селективного фильтра и индекса для его применения. Поэтому сосредоточьтесь на том, чтобы сделать фильтр удобным для индекса, а не на том, где он в запросе.3. Keyset-пагинация вместо OFFSET, когда возможно
OFFSET кажется простым способом реализации пагинации, но чем глубже вы углубляетесь, тем медленнее она становится. Чтобы вернуть OFFSET 100000, БД прочитает и отбросит 100 000 строк перед ней. Keyset-пагинация (также называемая поисковой пагинацией) запоминает последнее увиденное значение и переходит непосредственно за него:-- Keyset-пагинация по индексированному столбцу
SELECT * FROM orders
WHERE order_id > 1000
ORDER BY order_id
LIMIT 10;
Стоимость каждой страницы одинакова, будь то страница 2 или страница 2000, поскольку индекс переходит непосредственно к
order_id > 1000.Компромисс: keyset-пагинация обеспечивает быструю навигацию по следующей и предыдущей страницам, но вы не можете реализовать случайные переходы к любому номеру страницы.
4. Запрашивайте только то, что изменилось
Повторное чтение всей таблицы при каждом запуске неэффективно, если изменилось всего несколько строк. Отслеживайте «водяной знак» — последнюю обработанную точку — и извлекайте только строки, более новые, чем он:
-- Читаем только записи, обновлённые с последнего запуска
SELECT * FROM records
WHERE modified_date > '2026-08-10 00:00';
Это превращает полное сканирование таблицы в небольшое чтение диапазона с помощью индекса. Индексируйте
modified_date, сохраняйте новую точку последнего изменения после каждого запуска, и ваша задача синхронизации останется быстрой даже при росте таблицы.Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍2
День 2751. #BestPractices #SQL
Как оптимизировать SQL-запросы. Часть 3
Часть 1
Часть 2
Часть III. Упростите операции соединения и подзапросы
Это место, где запросы становятся дорогостоящими и где скрываются самые большие возможности улучшения. Цель в том, чтобы заставить БД выполнять меньше работы и представить эту работу в форме, с которой она лучше всего справляется.
1. Уменьшайте сложность операций соединения
Каждая операция соединения — это дополнительная работа. Чем меньше таблиц базе нужно coединить, тем быстрее запрос. Распространённая ошибка — соединение таблицы, из которой вы фактически не читаете данные:
Удалите ненужное соединение:
Прочитайте столбцы в
2. Выбирайте правильный тип соединения
Тип соединения влияет как на результат, так и на стоимость. Используйте
3. Заменяйте избыточные подзапросы соединениями (JOIN) или CTE
Подзапрос выполняется для каждой строки, что может быть крайне неэффективно при работе с большим набором результатов. В данном случае подзапрос выполняется для каждого заказа, просто чтобы найти имя клиента:
Простое соединение выполнит ту же работу один раз:
Когда один и тот же подзапрос требуется несколько раз в одном запросе, преобразуйте его в общее табличное выражение (CTE) с помощью оператора
А вот более быстрый запрос, использующий CTE:
4. EXISTS лучше, чем IN
EXISTS может привести к прерыванию обработки: он останавливается на первой совпадающей строке, в то время как IN может сначала сформировать полный список значений:
Примечание: не следует воспринимать «
Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Часть 3
Часть 1
Часть 2
Часть III. Упростите операции соединения и подзапросы
Это место, где запросы становятся дорогостоящими и где скрываются самые большие возможности улучшения. Цель в том, чтобы заставить БД выполнять меньше работы и представить эту работу в форме, с которой она лучше всего справляется.
1. Уменьшайте сложность операций соединения
Каждая операция соединения — это дополнительная работа. Чем меньше таблиц базе нужно coединить, тем быстрее запрос. Распространённая ошибка — соединение таблицы, из которой вы фактически не читаете данные:
-- Плохо: соединение с suppliers, которая не используется
SELECT p.product_name, c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id
JOIN suppliers s ON p.supplier_id = s.supplier_id;
Удалите ненужное соединение:
SELECT p.product_name, c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id;
Прочитайте столбцы в
SELECT и WHERE и удалите все соединения с таблицами, на которые нет ссылок.2. Выбирайте правильный тип соединения
Тип соединения влияет как на результат, так и на стоимость. Используйте
INNER JOIN, когда нужны совпадающие строки с обеих сторон, LEFT JOIN только тогда, когда действительно нужны и несовпадающие строки, и EXISTS, когда просто нужно узнать, существует ли совпадение:-- Проверка существования: EXISTS останавливается на первом совпадении
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
AND o.total > 100
);
LEFT JOIN часто используется там, где подошло бы INNER JOIN — оно заставляет базу сохранять несовпадающие строки, которые будут проигнорированы позже.3. Заменяйте избыточные подзапросы соединениями (JOIN) или CTE
Подзапрос выполняется для каждой строки, что может быть крайне неэффективно при работе с большим набором результатов. В данном случае подзапрос выполняется для каждого заказа, просто чтобы найти имя клиента:
-- Плохо: подзапрос на каждую строку
SELECT o.order_id,
(SELECT c.name FROM customers c
WHERE c.customer_id = o.customer_id) AS customer_name
FROM orders o;
Простое соединение выполнит ту же работу один раз:
-- Хорошо: одно соединение
SELECT o.order_id, c.name AS customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
Когда один и тот же подзапрос требуется несколько раз в одном запросе, преобразуйте его в общее табличное выражение (CTE) с помощью оператора
WITH, чтобы он был написан один раз и его было легче читать. Вот пример медленного запроса:-- Плохо: подзапрос повторяется
SELECT
c.customer_id,
c.name,
(
SELECT SUM(o.total_amount)
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.order_date >= DATE '2026-01-01'
) AS total_spent,
(
SELECT COUNT(*)
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.order_date >= DATE '2026-01-01'
) AS order_count
FROM customers c;
А вот более быстрый запрос, использующий CTE:
-- Хорошо: считаем результат один раз и переиспользуем
WITH customer_order_totals AS (
SELECT
customer_id,
SUM(total_amount) AS total_spent,
COUNT(*) AS order_count
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY customer_id
)
SELECT
c.customer_id, c.name,
COALESCE(t.total_spent, 0) AS total_spent,
COALESCE(t.order_count, 0) AS order_count
FROM customers c
LEFT JOIN customer_order_totals t ON t.customer_id = c.customer_id;
4. EXISTS лучше, чем IN
EXISTS может привести к прерыванию обработки: он останавливается на первой совпадающей строке, в то время как IN может сначала сформировать полный список значений:
SELECT p.product_id, p.product_name
FROM products p
WHERE EXISTS (
SELECT 1 FROM order_details od
WHERE od.product_id = p.product_id
);
Примечание: не следует воспринимать «
EXISTS всегда лучше IN» как жёсткое правило. В современных версиях PostgreSQL запросы IN, EXISTS и даже некоторые соединения часто переписываются в один и тот же план выполнения, поэтому они могут работать идентично. Однако проблема всё ещё возникает с NOT IN в подзапросе, который может возвращать NULL — это приводит к неожиданным результатам и худшему плану выполнения, поэтому в этом случае предпочтительнее использовать NOT EXISTS. Как всегда, проверяйте план выполнения, а не гадайте.Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
День 2752. #BestPractices #SQL
Как оптимизировать SQL-запросы. Части 4-5
Части 1, 2, 3
Часть IV. Разрабатывайте схему для чтения
Некоторые запросы работают медленно, независимо от способа их написания, потому что они постоянно пересчитывают один и тот же ресурсоёмкий результат. Решение заключается в изменении структуры данных.
1. Нормализуйте данные с умом
Нормализация поддерживает чистоту и согласованность данных и является правильным вариантом по умолчанию. Однако полностью нормализованные данные могут медленно читаться, когда часто выполняемый запрос должен соединять и агрегировать одни и те же таблицы при каждом запросе. Для путей с интенсивным чтением допустимо денормализовывать данные: предварительно агрегировать данные и сохранять их.
Теперь чтение представляет собой простой поиск, а не агрегацию в реальном времени по всей таблице
2. Использование материализованных представлений
Материализованное представление физически хранит результат запроса, поэтому чтение обращается к предварительно вычисленным строкам, а не пересчитывает их. Это вариант сводной таблицы, описанной выше:
Запросы к
Часть V. Повышение эффективности операций записи и транзакций
Медленная запись и длительные транзакции вызывают конфликты блокировок, заставляя остальные запросы ждать.
1. Пакетная обработка больших операций
Выполнение оператора для каждой строки приводит к перегрузке БД запросами и транзакционными издержками. Но один оператор, затрагивающий миллионы строк, также представляет проблему — он удерживает блокировки в течение длительного времени и может привести к переполнению журнала предварительной записи (WAL).
Промежуточным решением является пакетная обработка: обработка фиксированного фрагмента за раз. Следующий запрос перемещает строки в архивную таблицу по 1000 за раз, удаляя каждый фрагмент после его копирования:
Запустите его в цикле, пока он не станет затрагивать 0 строк. Каждая партия фиксируется быстро, удерживает мало блокировок и поддерживает отзывчивость системы во время выполнения основной задачи.
2. Сокращайте транзакции
Транзакция удерживает блокировки до момента фиксации, и все другие запросы, которым нужны эти строки, должны ждать. Чем дольше транзакция остаётся открытой, тем больше конкуренции она создаёт. Держите транзакцию открытой только для операций записи, а медленные операции — вызовы API, файловый ввод-вывод, ресурсоёмкие вычисления — выполняйте вне её:
Эта транзакция открывается, выполняет две связанные операции записи и немедленно фиксируется. Никогда не оставляйте транзакцию открытой, ожидая ввода пользователя или ответа по сети.
Окончание следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Части 4-5
Части 1, 2, 3
Часть IV. Разрабатывайте схему для чтения
Некоторые запросы работают медленно, независимо от способа их написания, потому что они постоянно пересчитывают один и тот же ресурсоёмкий результат. Решение заключается в изменении структуры данных.
1. Нормализуйте данные с умом
Нормализация поддерживает чистоту и согласованность данных и является правильным вариантом по умолчанию. Однако полностью нормализованные данные могут медленно читаться, когда часто выполняемый запрос должен соединять и агрегировать одни и те же таблицы при каждом запросе. Для путей с интенсивным чтением допустимо денормализовывать данные: предварительно агрегировать данные и сохранять их.
-- Сохраняем агрегированные данные в сводной таблице
CREATE TABLE sales_summary AS
SELECT product_id, SUM(quantity) AS total_sold
FROM order_details
GROUP BY product_id;
Теперь чтение представляет собой простой поиск, а не агрегацию в реальном времени по всей таблице
order_details. Компромисс заключается в необходимости синхронизации сводной таблицы — её обновления по расписанию или при изменении исходных данных. Денормализацию следует проводить целенаправленно, для конкретных часто используемых запросов, а не повсеместно.2. Использование материализованных представлений
Материализованное представление физически хранит результат запроса, поэтому чтение обращается к предварительно вычисленным строкам, а не пересчитывает их. Это вариант сводной таблицы, описанной выше:
-- Храним агрегированные данные
CREATE MATERIALIZED VIEW mv_total_sales AS
SELECT product_id, SUM(quantity) AS total_qty
FROM order_details
GROUP BY product_id;
-- Уникальный индекс позволяет представлению обновляться без блокирования чтения
CREATE UNIQUE INDEX idx_mv_total_sales_product
ON mv_total_sales (product_id);
-- Обновление по расписанию; читатели будут получать старые данные до завершения обновления
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_total_sales;
Запросы к
mv_total_sales выполняются быстро, потому что агрегация уже была выполнена. Данные актуальны только на момент последнего обновления, поэтому используйте материализованные представления для ресурсоёмких агрегаций, которые могут допускать небольшое устаревание — панели мониторинга, отчёты и таблицы лидеров.Часть V. Повышение эффективности операций записи и транзакций
Медленная запись и длительные транзакции вызывают конфликты блокировок, заставляя остальные запросы ждать.
1. Пакетная обработка больших операций
Выполнение оператора для каждой строки приводит к перегрузке БД запросами и транзакционными издержками. Но один оператор, затрагивающий миллионы строк, также представляет проблему — он удерживает блокировки в течение длительного времени и может привести к переполнению журнала предварительной записи (WAL).
Промежуточным решением является пакетная обработка: обработка фиксированного фрагмента за раз. Следующий запрос перемещает строки в архивную таблицу по 1000 за раз, удаляя каждый фрагмент после его копирования:
WITH batch AS (
DELETE FROM source_table
WHERE ctid IN (
SELECT ctid
FROM source_table
WHERE processed = false
LIMIT 1000
)
RETURNING col1, col2
)
INSERT INTO archive_table (col1, col2)
SELECT col1, col2 FROM batch;
Запустите его в цикле, пока он не станет затрагивать 0 строк. Каждая партия фиксируется быстро, удерживает мало блокировок и поддерживает отзывчивость системы во время выполнения основной задачи.
2. Сокращайте транзакции
Транзакция удерживает блокировки до момента фиксации, и все другие запросы, которым нужны эти строки, должны ждать. Чем дольше транзакция остаётся открытой, тем больше конкуренции она создаёт. Держите транзакцию открытой только для операций записи, а медленные операции — вызовы API, файловый ввод-вывод, ресурсоёмкие вычисления — выполняйте вне её:
BEGIN;
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
INSERT INTO transactions (account_id, amount)
VALUES (1, -100);
COMMIT;
Эта транзакция открывается, выполняет две связанные операции записи и немедленно фиксируется. Никогда не оставляйте транзакцию открытой, ожидая ввода пользователя или ответа по сети.
Окончание следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍2