Pular para conteúdo

Performance Monitoring

Sistema centralizado de monitoramento de performance com limiares configuráveis por tipo, namespace e categoria.

Visão Geral

O monitoramento de performance utiliza uma classe de opções singleton (PerformanceMonitorOptions) que define limiares de tempo para diferentes categorias de operações. Cada operação é medida com Stopwatch e logada conforme o tempo decorrido em relação aos limiares configurados.

Níveis de log:

Condição Nível
elapsed >= WarningMs Warning
elapsed >= SlowMs Information
elapsed < SlowMs Debug

Arquivos Principais

Arquivo Descrição
src/Ciba.Infrastructure/Configuration/PerformanceMonitorOptions.cs Classe de opções com API fluente e resolução de limiares
src/Ciba.Api/Extensions/PerformanceMonitoringExtensions.cs Configuração dos 28 overrides para operações naturalmente lentas
src/Ciba.Infrastructure/Behaviors/LoggingBehavior.cs MediatR behavior que monitora handlers
src/Ciba.Infrastructure/Behaviors/SlowHttpClientHandler.cs DelegatingHandler para HTTP clients
src/Ciba.Infrastructure/Persistence/Interceptors/SlowQueryInterceptor.cs EF Core interceptor para queries
src/Ciba.Api/_Shared/Pipelines/Pipeline.cs Pipeline genérico com timing por step e total
src/Ciba.Infrastructure/Services/Storage/R2StorageService.cs Instrumentação de timing para storage R2

PerformanceMonitorOptions

Limiares por Categoria (Defaults)

public ThresholdPair Handlers    { get; set; } = new(500, 2_000);
public ThresholdPair Queries     { get; set; } = new(200, 1_000);
public ThresholdPair Steps       { get; set; } = new(500, 2_000);
public ThresholdPair Pipelines   { get; set; } = new(2_000, 5_000);
public ThresholdPair HttpClients { get; set; } = new(1_000, 5_000);
public ThresholdPair Storage     { get; set; } = new(1_000, 5_000);
Categoria Slow (ms) Warning (ms) Consumidores
Handlers 500 2.000 LoggingBehavior (MediatR)
Queries 200 1.000 SlowQueryInterceptor (EF Core)
Steps 500 2.000 Pipeline<TContext> (por step)
Pipelines 2.000 5.000 Pipeline<TContext> (total)
HttpClients 1.000 5.000 SlowHttpClientHandler
Storage 1.000 5.000 R2StorageService

API Fluente

// Override por tipo exato (handler, step, request DTO)
perf.Override<PlaygroundChatRequest>(3_000, 15_000);

// Override por prefixo de namespace
perf.OverrideNamespace("Ciba.Api.Features.Webhook", 3_000, 10_000);

// Override por nome de HTTP client
perf.OverrideHttpClient("Claude", 8_000, 20_000);

Prioridade de Resolução

1. Tipo exato   → Override<T>() registrado com FullName do tipo
2. Namespace    → OverrideNamespace() mais específico (prefixo mais longo vence)
3. Default      → Limiar padrão da categoria (ex: options.Handlers)

Para HTTP clients, a resolução é separada:

1. Nome exato   → OverrideHttpClient("Claude") registrado
2. Default      → options.HttpClients

Overrides Configurados

Configuração em PerformanceMonitoringExtensions.AddPerformanceMonitoringDefaults():

HTTP Clients (5 overrides)

Client Slow (ms) Warning (ms) Motivo
Claude 8.000 20.000 LLM streaming/completion
OpenAI 6.000 15.000 LLM completion
Whisper 10.000 30.000 Transcrição de áudio
Embedding 5.000 15.000 Geração de embeddings
Evolution 1.500 5.000 WhatsApp API

Pipeline Steps (8 overrides)

Step Slow (ms) Warning (ms) Motivo
InvokeAiServiceStep 3.000 12.000 Chamada ao LLM
InvokeDeferredAiStep 3.000 12.000 Chamada ao LLM (deferred)
ProcessMediaStep 2.000 10.000 Download + OCR/transcrição
ApplyResponseDelayStep 5.000 15.000 Delay intencional de digitação
SendWhatsAppResponseStep 800 3.000 Envio via Evolution API
SendAttachmentsStep 1.000 5.000 Upload de anexos
SendDeferredResponseStep 800 3.000 Envio via Evolution API (deferred)
ResolveConversationStep 800 3.000 Resolução de conversa

Handlers: Playground (3 overrides)

Request Slow (ms) Warning (ms) Motivo
PlaygroundChatRequest 3.000 15.000 LLM + embeddings + vision
PlaygroundTranscribeRequest 2.000 10.000 Whisper transcription
PlaygroundDescribeImageRequest 2.000 8.000 Vision API

Handlers: Knowledge & FAQ (4 overrides)

Request Slow (ms) Warning (ms) Motivo
CreateKnowledgeBlockRequest 1.500 6.000 Chunking + embeddings
UpdateKnowledgeBlockRequest 1.500 6.000 Chunking + embeddings
CreateFaqItemRequest 800 2.500 Embedding geração
UpdateFaqItemRequest 800 2.500 Embedding geração

Handlers: Agent/Step (4 overrides)

Request Slow (ms) Warning (ms) Motivo
CreateAgentRequest 1.500 5.000 Prompt optimization via LLM
UpdateAgentRequest 1.500 5.000 Prompt optimization via LLM
CreateAgentStepRequest 1.500 5.000 Prompt optimization via LLM
UpdateAgentStepRequest 1.500 5.000 Prompt optimization via LLM

Handlers: WhatsApp + Storage (3 overrides)

Request Slow (ms) Warning (ms) Motivo
SendMessageRequest 1.000 4.000 Envio via Evolution API
UploadAttachmentRequest 1.000 5.000 Upload para R2
ConnectInstanceRequest 1.000 3.500 Conexão WhatsApp

Consumidores

LoggingBehavior (MediatR Handlers)

Registrado como IPipelineBehavior<TRequest, TResponse>, monitora todos os handlers MediatR:

var thresholds = options.Resolve(typeof(TRequest), options.Handlers);

Resolve o limiar usando o tipo do request DTO (ex: PlaygroundChatRequest), com fallback para options.Handlers.

SlowQueryInterceptor (EF Core)

DbCommandInterceptor que monitora todas as queries SQL:

var thresholds = options.Queries; // Sempre usa o default de Queries

Não suporta overrides por tipo (queries são genéricas).

SlowHttpClientHandler (HTTP Clients)

DelegatingHandler adicionado via extension method:

services.AddHttpClient<ILlmClient, ClaudeClient>()
    .AddPerformanceMonitoring("Claude");

Resolve o limiar pelo nome do client:

var thresholds = options.ResolveHttpClient(clientName);

Pipeline (Steps e Pipelines)

Monitora cada step individualmente e o pipeline como um todo:

// Por step: resolve pelo tipo do step
var thresholds = _options.Resolve(stepType, _options.Steps);

// Total do pipeline: usa default de Pipelines
var thresholds = _options.Pipelines;

R2StorageService (Storage)

Monitora upload e fetch de arquivos:

var thresholds = _perfOptions.Storage; // Sempre usa o default de Storage

Registro no DI

A configuração deve ser registrada antes de AddInfrastructure() para que todos os consumidores recebam as opções:

// Program.cs
builder.Services.AddPerformanceMonitoringDefaults(); // Registra singleton
builder.Services.AddInfrastructure(builder.Configuration); // Consumidores recebem via DI
// Infrastructure ServiceCollectionExtensions.cs
public static IServiceCollection AddPerformanceMonitoring(
    this IServiceCollection services,
    Action<PerformanceMonitorOptions>? configure = null)
{
    var options = new PerformanceMonitorOptions();
    configure?.Invoke(options);
    services.AddSingleton(options);
    return services;
}

Regras

  1. Só adicionar override quando necessário -- operações que são naturalmente mais lentas que o default da categoria
  2. Overrides por tipo usam o FullName do tipo (namespace completo)
  3. Overrides por namespace são ordenados por especificidade (prefixo mais longo vence)
  4. HTTP client names são case-insensitive
  5. Não usar overrides para queries SQL -- o interceptor usa sempre o default de Queries
  6. Não usar overrides para storage -- o R2StorageService usa sempre o default de Storage