Zpět na domů

Pozorovatelnost v Go: QueryTracer, metriky a OTel

Článek srovnává nástroje pozorovatelnosti v Go: QueryTracer, metriky a OpenTelemetry. Vysvětluje rozdíly v použití a typické chyby. Uvádí příklady kódu a doporučení pro výběr stacku pro různé scénáře.

QueryTracer vs OpenTelemetry: jak vybrat nástroje pozorovatelnosti v Go
Advertisement 728x90

Pozorovatelnost v Go: volba mezi QueryTracer, metrikami a OpenTelemetry

Při integraci PostgreSQL do Go aplikací se vývojáři často potýkají s volbou nástrojů pro pozorovatelnost. Metriky, QueryTracer a OpenTelemetry řeší různé úkoly, ale snadno se je zamění. Probereme, kdy použít každý z nich, a jak se vyhnout typickým chybám na základě reálného případu s dekorátorem a transakcemi.

Tři úrovně pozorovatelnosti: metriky, tracing a kontext

Metriky představují základní úroveň pozorovatelnosti. Odpovídají na otázku „co se děje“: například růst počtu chyb po nasazení nebo zvýšení p99 času odpovědi endpointu z 50 ms na 800 ms. Sběr metrik je vůči zdrojům levný a umožňuje rychle odhalit anomálii. Metriky však neposkytují kontext: ukazují, že požadavek zpomalil, ale nevysvětlují proč. Pro odpověď na tuto otázku je potřeba tracing.

QueryTracer je rozhraní poskytované ovladačem pgx pro zachycení operací na úrovni ovladače. Generuje samostatné události pro každou fázi transakce: BEGIN, samotný dotaz a COMMIT. Není to nástroj pro sběr metrik, ale bod pro vložení (hook), do kterého můžete umístit logiku pro vytváření spanů, logování SQL nebo sběr metrik. Je důležité pochopit: QueryTracer sám data neodesílá – pouze poskytuje mechanismus pro jejich generování.

Google AdInline article slot

OpenTelemetry (OTel) řeší distribuovaný tracing. Span se vytváří na úrovni obslužného HTTP požadavku a předává se přes kontext (context.Context) hlouběji do zásobníku: služba, repozitář, ovladač. Každá vrstva přidává dětský span a formuje strom provedení. V systému vizualizace (např. Jaeger) vidíte celý obrázek: který konkrétní dotaz k DB způsobil zpoždění a v jakém kontextu. OTel běží neustále, ale pro snížení zátěže používá sampling: např. traceuje jen 10 % požadavků. Pokud požadavek spadne do vzorku, zaznamená se celý strom spanů.

Příklad s dekorátorem: kde se skrývá problém

V typických projektech se pro sběr metrik používá dekorátor nad repozitářem. Podívejme se na příklad implementace:

func WithDBMetricsValueT any (T, error)) (T, error) {
    start := time.Now()
    result, err := fn()
    seconds := time.Since(start).Seconds()

    metrics.ObserveDatabaseQueryDuration(operation, seconds)
    if err != nil {
        metrics.IncDatabaseErrors(operation)
        return result, CheckDBError(operation, err)
    }

    return result, err
}

Pro metody provádějící jeden SQL dotaz funguje dekorátor přesně: metrika odpovídá času provedení dotazu. U metod s transakcemi se však situace mění. Vezměme příklad:

Google AdInline article slot
func (r *BookingRepo) CreateBooking(ctx context.Context, b *models.Booking) (int64, error) {
    const operation = "create_booking"

    return WithDBMetricsValue(operation, func() (int64, error) {
        if err := r.validateBooking(b); err != nil {
            return 0, err
        }

        tx, err := r.db.BeginTxx(ctx, nil)
        if err != nil {
            return 0, err
        }
        defer func() { _ = tx.Rollback() }()

        var id int64
        err = tx.QueryRowContext(ctx, createBookingAtomicQuery, ...).Scan(&id)

        if err != nil {
            if errors.Is(err, sql.ErrNoRows) {
                return 0, models.ErrSlotOccupied
            }
            return 0, err
        }

        if err = tx.Commit(); err != nil {
            return 0, err
        }

        return id, nil
    })
}

Dekorátor měří čas celého bloku včetně validace, zahájení transakce, provedení dotazu a commitu. Metrika create_booking = 45ms neodhalí, která fáze zabírá 40 ms. QueryTracer naopak rozkládá transakci na tři události:

  • BEGIN = 0.1ms
  • INSERT = 40ms
  • COMMIT = 2ms

Tato detailizace je klíčová pro diagnostiku úzkých míst v výkonu.

Implementace QueryTracer: rozhraní a pastě

QueryTracer je soubor rozhraní v pgx, každé pro jiný typ operace. Např. pro Batch operace se používá BatchTracer:

Google AdInline article slot
type BatchTracer interface {
    TraceBatchStart(ctx context.Context, conn *Conn, data TraceBatchStartData) context.Context
    TraceBatchQuery(ctx context.Context, conn *Conn, data TraceBatchQueryData)
    TraceBatchEnd(ctx context.Context, conn *Conn, data TraceBatchEndData)
}

Implementace vyžaduje strukturu, která splňuje tato rozhraní:

type MyTracer struct{}

func (t *MyTracer) TraceQueryStart(ctx context.Context, conn *Conn, data TraceQueryStartData) context.Context {
    // Onchalo request: sozdaem span or logiruem
    return ctx
}

func (t *MyTracer) TraceQueryEnd(ctx context.Context, conn *Conn, data TraceQueryEndData) {
    // Konets request: fiksiruem time, otpravlyaem metriku
    //
}

// Podklyuchenie to konfiguratsii
config.ConnConfig.Tracer = &MyTracer{}

Klíčové pasti:

  • Pokud neimplementujete BatchTracer, Batch operace zůstanou nepozorované. Chyba nevznikne, ale data se ztratí.
  • Pro práci s OTel je nutné ručně vytvářet spany uvnitř metod QueryTracer a správně předávat kontext.
  • QueryTracer nenahrazuje metriky: pro agregaci dat budete potřebovat další logiku.

Srovnání stacků: sqlx, pgx a vlastní řešení

Volba nástroje závisí na aktuálním stacku a požadavcích na detail. Podívejme se na tři varianty:

  • sqlx + otelsql

- Připojení: jedna řádka kódu.

- Výhody: nevyžaduje změnu ovladače, podpora základního tracingu.

- Omezení: chybí podpora Batch a CopyFrom, nelze přidat vlastní logiku.

- Doporučení: vhodné pro jednoduché projekty bez složitých operací s DB.

  • pgx + otelpgx

- Připojení: jedna řádka kódu.

- Výhody: plná podpora Batch a CopyFrom, automatické vytváření spanů.

- Omezení: vyžaduje přechod z sqlx na pgx.

- Doporučení: optimální volba pro nové projekty nebo migraci stávajících.

  • pgx + MyTracer

- Připojení: ruční implementace rozhraní.

- Výhody: plná kontrola nad logikou (vlastní metriky, rozšířené logování).

- Omezení: vysoká složitost, nutnost údržby.

- Doporučení: pouze při specifických požadavcích, které otelpgx nepokrývá.

Důležité: vůbec všech případech musí být kontext (context.Context) předáván všemi vrstvami aplikace – od handleru po ovladač. Bez toho budou spany kořenové a ztratí spojení s původním uživatelským požadavkem.

Co je důležité

  • Metriky ukazují problém, ale ne příčinu. Používejte je pro monitorování, ne pro diagnostiku úzkých míst.
  • QueryTracer je hook pro tracing, ne pro metriky. Rozkládá transakce na fáze, což je nutné pro detailní analýzu.
  • OpenTelemetry vyžaduje správné předávání kontextu. Ověřte, že context.Context prochází všemi vrstvami aplikace.
  • Dekorátor je vhodný pro jednoduché metriky, ne pro transakce. Pro diagnostiku uvnitř transakcí použijte QueryTracer nebo OTel.
  • Volba stacku: pokud projekt už používá sqlx a nepotřebuje složité operace – zůstaňte u otelsql. Pro úplný pohled přejděte na pgx s otelpgx.

— Editorial Team

Advertisement 728x90

Číst dál