Senior Go Interview Prep - Core Go: https://go.vbloher.org/docs/01-core-go/ - Механика defer в Go: https://go.vbloher.org/docs/01-core-go/defer/ - Встраивание структур и интерфейсов (Embedding): https://go.vbloher.org/docs/01-core-go/embedding/ - Ошибки в Go: error, wrapping, errors.Is/As/Join: https://go.vbloher.org/docs/01-core-go/errors/ - Дженерики в Go (1.18+): https://go.vbloher.org/docs/01-core-go/generics/ - Интерфейсы в Go: https://go.vbloher.org/docs/01-core-go/interfaces/ - Устройство map в Go: https://go.vbloher.org/docs/01-core-go/maps/ - panic / recover: механика, раскрутка стека и runtime-паники: https://go.vbloher.org/docs/01-core-go/panic-recover/ - Указатели в Go: https://go.vbloher.org/docs/01-core-go/pointers/ - Рефлексия в Go (reflect): https://go.vbloher.org/docs/01-core-go/reflection/ - Внутреннее устройство слайсов в Go: https://go.vbloher.org/docs/01-core-go/slices/ - Строки, руны и байты в Go: https://go.vbloher.org/docs/01-core-go/strings-runes-bytes/ - Система типов Go: defined types, alignment, memory layout: https://go.vbloher.org/docs/01-core-go/type-system/ - Concurrency: https://go.vbloher.org/docs/02-concurrency/ - sync/atomic: https://go.vbloher.org/docs/02-concurrency/atomic/ - Буферизованные vs небуферизованные каналы: https://go.vbloher.org/docs/02-concurrency/buffered-unbuffered/ - Канал vs Mutex: когда что выбрать: https://go.vbloher.org/docs/02-concurrency/channel-vs-mutex/ - Каналы: устройство hchan: https://go.vbloher.org/docs/02-concurrency/channels/ - Утечки горутин, дедлоки, livelock, starvation: https://go.vbloher.org/docs/02-concurrency/common-leaks-deadlocks/ - sync.Cond: https://go.vbloher.org/docs/02-concurrency/cond/ - context: https://go.vbloher.org/docs/02-concurrency/context/ - Горутины: жизненный цикл, стоимость, стек: https://go.vbloher.org/docs/02-concurrency/goroutines-lifecycle/ - sync.Mutex и sync.RWMutex: https://go.vbloher.org/docs/02-concurrency/mutex-rwmutex/ - sync.Once: https://go.vbloher.org/docs/02-concurrency/once/ - Паттерны конкурентности: https://go.vbloher.org/docs/02-concurrency/patterns/ - Race Detector (гонки данных и -race): https://go.vbloher.org/docs/02-concurrency/race-detector/ - Планировщик GMP: https://go.vbloher.org/docs/02-concurrency/scheduler-gmp/ - select: https://go.vbloher.org/docs/02-concurrency/select/ - sync.WaitGroup: https://go.vbloher.org/docs/02-concurrency/waitgroup/ - Runtime и память: https://go.vbloher.org/docs/03-runtime-memory/ - Паттерны аллокаций и снижение давления на GC: https://go.vbloher.org/docs/03-runtime-memory/allocation-patterns/ - Escape Analysis: когда переменная убегает в кучу: https://go.vbloher.org/docs/03-runtime-memory/escape-analysis/ - Сборщик мусора Go: concurrent tri-color mark-sweep: https://go.vbloher.org/docs/03-runtime-memory/gc/ - Тюнинг GC: GOGC и GOMEMLIMIT: https://go.vbloher.org/docs/03-runtime-memory/gogc-gomemlimit/ - GOMAXPROCS: параллелизм планировщика и проблема контейнеров: https://go.vbloher.org/docs/03-runtime-memory/gomaxprocs/ - Утечки горутин (goroutine leaks): https://go.vbloher.org/docs/03-runtime-memory/goroutine-leaks/ - Утечки памяти в Go (несмотря на GC): https://go.vbloher.org/docs/03-runtime-memory/memory-leaks/ - Модель памяти Go (Go Memory Model): happens-before и синхронизация: https://go.vbloher.org/docs/03-runtime-memory/memory-model/ - pprof: профилирование CPU, памяти и блокировок в Go: https://go.vbloher.org/docs/03-runtime-memory/pprof/ - Execution Tracer и runtime/trace: тайминги вместо агрегатов: https://go.vbloher.org/docs/03-runtime-memory/runtime-tracing/ - Стек vs Куча: где живут данные в Go: https://go.vbloher.org/docs/03-runtime-memory/stack-vs-heap/ - Тестирование: https://go.vbloher.org/docs/04-testing/ - testify, assert/require и golden files: https://go.vbloher.org/docs/04-testing/assertions-testify/ - Бенчмарки в Go: https://go.vbloher.org/docs/04-testing/benchmarks/ - Покрытие, -race и флаки-тесты: https://go.vbloher.org/docs/04-testing/coverage-race/ - Нативный fuzzing в Go (1.18+): https://go.vbloher.org/docs/04-testing/fuzzing/ - Интеграционные тесты, testcontainers-go, TestMain: https://go.vbloher.org/docs/04-testing/integration-testcontainers/ - Моки, стабы и тестируемость: https://go.vbloher.org/docs/04-testing/mocks/ - Table-driven тесты, subtests и параллельность: https://go.vbloher.org/docs/04-testing/table-driven/ - Backend: https://go.vbloher.org/docs/05-backend/ - Аутентификация и авторизация: AuthN/AuthZ, сессии vs токены, RBAC/ABAC, API keys, mTLS, секреты: https://go.vbloher.org/docs/05-backend/auth-authz/ - Graceful Shutdown HTTP/gRPC сервера в Go: https://go.vbloher.org/docs/05-backend/graceful-shutdown/ - gRPC: типы RPC, интерсепторы, контекст, метаданные, error model: https://go.vbloher.org/docs/05-backend/grpc/ - JWT (JSON Web Token): https://go.vbloher.org/docs/05-backend/jwt/ - Middleware-паттерн в Go: https://go.vbloher.org/docs/05-backend/middleware/ - net/http: Server, Handler, ServeMux, таймауты, Client и контекст: https://go.vbloher.org/docs/05-backend/net-http/ - OAuth2: роли, grant types, OIDC, токены и типовые ошибки: https://go.vbloher.org/docs/05-backend/oauth2/ - OpenAPI/Swagger, code generation, contract-first vs code-first, валидация: https://go.vbloher.org/docs/05-backend/openapi/ - Protocol Buffers: схемы, wire format, эволюция и совместимость: https://go.vbloher.org/docs/05-backend/protobuf/ - REST: принципы, версионирование, идемпотентность, статусы, пагинация, ошибки: https://go.vbloher.org/docs/05-backend/rest/ - Сети и протоколы: https://go.vbloher.org/docs/06-networking/ - Пулы соединений: http.Transport, БД, утечки: https://go.vbloher.org/docs/06-networking/connection-pooling/ - DNS: записи, резолвинг, кэширование, DNS в Go: https://go.vbloher.org/docs/06-networking/dns/ - Версии HTTP: 1.1, 2, 3: https://go.vbloher.org/docs/06-networking/http-versions/ - TCP/IP: модель, транспорт и что важно бэкендеру: https://go.vbloher.org/docs/06-networking/tcp-ip/ - TLS: handshake, сертификаты, mTLS, производительность: https://go.vbloher.org/docs/06-networking/tls/ - UDP и надёжность поверх UDP: https://go.vbloher.org/docs/06-networking/udp/ - WebSocket: upgrade, фреймы, масштабирование: https://go.vbloher.org/docs/06-networking/websocket/ - Базы данных: https://go.vbloher.org/docs/07-databases/ - Пул соединений к PostgreSQL в Go: database/sql, pgx, pgxpool, PgBouncer: https://go.vbloher.org/docs/07-databases/connection-pooling-pgx/ - Взаимоблокировки (Deadlocks) в PostgreSQL: https://go.vbloher.org/docs/07-databases/deadlocks/ - Индексы в PostgreSQL: https://go.vbloher.org/docs/07-databases/indexes/ - Уровни изоляции транзакций в PostgreSQL: https://go.vbloher.org/docs/07-databases/isolation-levels/ - MVCC в PostgreSQL: версии строк, видимость, VACUUM и bloat: https://go.vbloher.org/docs/07-databases/mvcc/ - Обзор NoSQL и Redis: https://go.vbloher.org/docs/07-databases/nosql-redis/ - Партиционирование таблиц в PostgreSQL: https://go.vbloher.org/docs/07-databases/partitioning/ - Архитектура PostgreSQL: https://go.vbloher.org/docs/07-databases/postgresql-architecture/ - Планирование и оптимизация запросов в PostgreSQL: https://go.vbloher.org/docs/07-databases/query-planning/ - Репликация в PostgreSQL: https://go.vbloher.org/docs/07-databases/replication/ - Шардирование (горизонтальное масштабирование): https://go.vbloher.org/docs/07-databases/sharding/ - Транзакции в PostgreSQL и Go (database/sql, pgx): https://go.vbloher.org/docs/07-databases/transactions/ - Распределённые системы: https://go.vbloher.org/docs/08-distributed-systems/ - CAP теорема: https://go.vbloher.org/docs/08-distributed-systems/cap-theorem/ - Circuit Breaker: https://go.vbloher.org/docs/08-distributed-systems/circuit-breaker/ - Консенсус и Raft: репликация состояния в присутствии отказов: https://go.vbloher.org/docs/08-distributed-systems/consensus-raft/ - Модели согласованности: https://go.vbloher.org/docs/08-distributed-systems/consistency/ - Гарантии доставки сообщений: at-most-once / at-least-once / exactly-once: https://go.vbloher.org/docs/08-distributed-systems/delivery-guarantees/ - Eventual Consistency: https://go.vbloher.org/docs/08-distributed-systems/eventual-consistency/ - Идемпотентность в распределённых системах: https://go.vbloher.org/docs/08-distributed-systems/idempotency/ - Apache Kafka: https://go.vbloher.org/docs/08-distributed-systems/kafka/ - Transactional Outbox: https://go.vbloher.org/docs/08-distributed-systems/outbox/ - RabbitMQ: AMQP 0-9-1, маршрутизация, надёжность доставки и сравнение с Kafka: https://go.vbloher.org/docs/08-distributed-systems/rabbitmq/ - Ретраи: backoff, jitter, budgets и идемпотентность: https://go.vbloher.org/docs/08-distributed-systems/retries/ - Saga Pattern: https://go.vbloher.org/docs/08-distributed-systems/saga/ - Observability: https://go.vbloher.org/docs/09-observability/ - Grafana: https://go.vbloher.org/docs/09-observability/grafana/ - Метрики: RED, USE, Golden Signals: https://go.vbloher.org/docs/09-observability/metrics/ - OpenTelemetry: https://go.vbloher.org/docs/09-observability/opentelemetry/ - Prometheus: https://go.vbloher.org/docs/09-observability/prometheus/ - SLI / SLO / SLA: https://go.vbloher.org/docs/09-observability/slo-sli/ - Структурированное логирование (slog): https://go.vbloher.org/docs/09-observability/structured-logging/ - Distributed Tracing: https://go.vbloher.org/docs/09-observability/tracing/ - System Design: https://go.vbloher.org/docs/10-system-design/ - Analytics Pipeline: https://go.vbloher.org/docs/10-system-design/analytics-pipeline/ - Chat System: https://go.vbloher.org/docs/10-system-design/chat/ - Фреймворк System Design интервью: https://go.vbloher.org/docs/10-system-design/framework/ - Notification Service: https://go.vbloher.org/docs/10-system-design/notification-service/ - Order Service: https://go.vbloher.org/docs/10-system-design/order-service/ - Payment Service: https://go.vbloher.org/docs/10-system-design/payment-service/ - Rate Limiter: https://go.vbloher.org/docs/10-system-design/rate-limiter/ - URL Shortener: https://go.vbloher.org/docs/10-system-design/url-shortener/ - DevOps: https://go.vbloher.org/docs/11-devops/ - CI/CD: пайплайны, стадии, стратегии деплоя: https://go.vbloher.org/docs/11-devops/cicd/ - Облака (AWS / GCP) для бэкендера: https://go.vbloher.org/docs/11-devops/cloud-aws-gcp/ - Docker для Go-разработчика: https://go.vbloher.org/docs/11-devops/docker/ - GitHub Actions и GitLab CI: https://go.vbloher.org/docs/11-devops/github-gitlab-ci/ - Kubernetes для Go-разработчика: https://go.vbloher.org/docs/11-devops/kubernetes/ - Terraform / Infrastructure as Code: https://go.vbloher.org/docs/11-devops/terraform/ - Алгоритмы: https://go.vbloher.org/docs/12-algorithms/ - Типовые алгоритмические задачи и паттерны: https://go.vbloher.org/docs/12-algorithms/common-problems/ - Асимптотическая сложность (Big-O): https://go.vbloher.org/docs/12-algorithms/complexity/ - Структуры данных в Go: https://go.vbloher.org/docs/12-algorithms/data-structures/ - Специфика live-coding на Go: https://go.vbloher.org/docs/12-algorithms/go-specifics/ - Behavioral: https://go.vbloher.org/docs/13-behavioral/ - Конфликты, разногласия и работа со стейкхолдерами: https://go.vbloher.org/docs/13-behavioral/conflicts/ - Как проходит senior-интервью: этапы, оценка, оффер: https://go.vbloher.org/docs/13-behavioral/interview-flow/ - Лидерство и менторство: https://go.vbloher.org/docs/13-behavioral/leadership-mentoring/ - Типовые поведенческие вопросы для Senior: https://go.vbloher.org/docs/13-behavioral/senior-questions/ > Модуль: Observability · Уровень: Senior ## TL;DR OpenTelemetry (OTel) — это вендор-нейтральный стандарт и набор SDK для сбора телеметрии: **traces, metrics, logs** (три сигнала + появляющиеся profiles). Ключевая идея — разделить **API** (стабильный контракт, который вызывает код приложения и библиотеки) и **SDK** (реализация, которая семплирует, батчит и экспортирует). Данные текут через **pipeline**: `Instrumentation → Provider → Processor → Exporter → Collector → Backend`. **Collector** — отдельный процесс/демон, который принимает (receivers), обрабатывает (processors) и отправляет (exporters) телеметрию, отвязывая приложение от конкретного бэкенда. Context propagation через `context.Context` + W3C `traceparent` связывает спаны между сервисами. В Go главные грабли: не забыть `Shutdown()` (иначе теряются батчи), не плодить cardinality в метриках, прокидывать `ctx` сквозь весь стек. ## Теория ### Три сигнала | Сигнал | Что отвечает | Модель данных | Стоимость хранения | |--------|--------------|---------------|--------------------| | **Traces** | "Где время потерялось в одном запросе?" | Дерево спанов с causality | Высокая (per-request), нужен sampling | | **Metrics** | "Сколько / как быстро / насколько плохо в агрегате?" | Числовые временные ряды с labels | Низкая (агрегаты), но взрыв от cardinality | | **Logs** | "Что конкретно произошло в этой точке?" | Структурированные записи с timestamp/severity | Средняя-высокая | Сила OTel — в **корреляции**: один `TraceID` связывает спаны, метрики (через exemplars) и логи (через trace_id в атрибутах). На senior-собеседовании ждут, что вы понимаете: сигналы дополняют друг друга, а не дублируют. Метрики → "что-то сломалось и насколько" (дёшево, всегда включены). Трейсы → "почему именно этот запрос медленный" (sampled). Логи → "детали конкретного события". ### API vs SDK Это центральное архитектурное различие OTel и любимый вопрос. - **API** (`go.opentelemetry.io/otel`, `.../trace`, `.../metric`) — интерфейсы и no-op реализации. Библиотеки (например, HTTP-клиент, драйвер БД) инструментируются **только через API**. Если SDK не подключён — вызовы превращаются в no-op (нулевой оверхед), приложение работает. - **SDK** (`go.opentelemetry.io/otel/sdk/...`) — конкретная реализация: семплеры, процессоры, экспортёры, resource detection. Подключается **только в `main()` приложения**, никогда в библиотеке. Это позволяет автору библиотеки инструментировать код, не навязывая пользователю зависимость от тяжёлого SDK или конкретного вендора. ```go // Библиотека: использует ТОЛЬКО API import "go.opentelemetry.io/otel" var tracer = otel.Tracer("github.com/me/mylib") // именованный по имени модуля func DoWork(ctx context.Context) error { ctx, span := tracer.Start(ctx, "DoWork") defer span.End() // ... return nil } ``` Глобальный `otel.Tracer(...)` берёт `TracerProvider` из глобального реестра. Пока приложение не вызвало `otel.SetTracerProvider(sdkProvider)`, это no-op провайдер. ### Полная инициализация SDK для Go-сервиса ```go import ( "context" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" ) func initTracer(ctx context.Context) (func(context.Context) error, error) { // Resource — описывает сам сервис (service.name ОБЯЗАТЕЛЕН для бэкендов) res, err := resource.New(ctx, resource.WithAttributes( semconv.ServiceName("payments"), semconv.ServiceVersion("1.4.2"), semconv.DeploymentEnvironment("production"), ), resource.WithFromEnv(), // OTEL_RESOURCE_ATTRIBUTES resource.WithHost(), resource.WithProcess(), ) if err != nil { return nil, err } // Exporter: OTLP/gRPC в Collector exp, err := otlptracegrpc.New(ctx, otlptracegrpc.WithEndpoint("otel-collector:4317"), otlptracegrpc.WithInsecure(), ) if err != nil { return nil, err } tp := sdktrace.NewTracerProvider( // BatchSpanProcessor — буферизует и шлёт батчами (НЕ Simple в проде!) sdktrace.WithBatcher(exp, sdktrace.WithMaxQueueSize(2048), sdktrace.WithBatchTimeout(5*time.Second), ), sdktrace.WithResource(res), // ParentBased + TraceIDRatio: уважаем решение родителя, иначе семплируем 10% sdktrace.WithSampler( sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1)), ), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, // W3C traceparent/tracestate propagation.Baggage{}, // W3C baggage )) return tp.Shutdown, nil // ВАЖНО: вызвать при graceful shutdown } ``` ```go func main() { ctx := context.Background() shutdown, err := initTracer(ctx) if err != nil { log.Fatal(err) } defer func() { // даём время дослать батчи sdctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = shutdown(sdctx) }() // ... запуск сервера } ``` ### Инструментирование HTTP/gRPC Не пишите ручные спаны для транспорта — используйте contrib-инструментацию: ```go import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // Сервер: автоматически создаёт server-span, извлекает traceparent из заголовков handler := otelhttp.NewHandler(mux, "http.server", otelhttp.WithSpanNameFormatter(func(_ string, r *http.Request) string { return r.Method + " " + r.Pattern // route, НЕ raw URL (cardinality!) }), ) // Клиент: создаёт client-span, инжектит traceparent в исходящий запрос client := http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)} ``` Для gRPC — `otelgrpc.NewServerHandler()` / `otelgrpc.NewClientHandler()` через `grpc.StatsHandler(...)` (старые interceptor-API deprecated). ### Context propagation: как трейс «прошивает» сервисы ``` Service A Service B ───────── ───────── ctx, span := tracer.Start(ctx) └─ TraceID, SpanID в ctx http req ──[inject]──> заголовок: ──[extract]──> ctx с теми же TraceID, traceparent: 00---01 parent = SpanID из A ctx, span := tracer.Start(ctx) └─ тот же TraceID, новый SpanID, parent = A ``` - **Inject** — пропагатор кладёт `traceparent` из `SpanContext` в исходящие заголовки (делает `otelhttp.Transport` автоматически). - **Extract** — на входе достаёт `traceparent` и кладёт remote `SpanContext` в `ctx` (делает `otelhttp.Handler`). - **Baggage** — отдельный механизм: произвольные key=value, путешествующие по всему трейсу (`tenant_id`, `feature_flag`). Осторожно: baggage едет в КАЖДОМ заголовке каждого hop'а — не суйте туда много/секретное. W3C формат: `traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01` = `version-traceid(16б)-spanid(8б)-flags`. Флаг `01` = sampled. ### Collector — почему он нужен Приложение шлёт OTLP в Collector, а не в бэкенд напрямую. Зачем: - **Развязка**: сменить вендора (Jaeger → Tempo → Datadog) = поменять конфиг Collector, не передеплоивать сервисы. - **Буферизация/ретраи/батчинг** вне процесса приложения. - **Обработка**: добавить/удалить атрибуты, redact PII, переименовать, агрегировать. - **Tail-based sampling** (см. tracing.md) — невозможен на стороне приложения, нужен Collector, видящий весь трейс. ```yaml # collector-config.yaml receivers: otlp: protocols: grpc: { endpoint: 0.0.0.0:4317 } http: { endpoint: 0.0.0.0:4318 } processors: batch: { timeout: 5s, send_batch_size: 1024 } memory_limiter: { check_interval: 1s, limit_mib: 512 } attributes: actions: - { key: http.request.header.authorization, action: delete } # redact PII exporters: otlp/tempo: { endpoint: tempo:4317, tls: { insecure: true } } prometheus: { endpoint: 0.0.0.0:8889 } service: pipelines: traces: { receivers: [otlp], processors: [memory_limiter, attributes, batch], exporters: [otlp/tempo] } metrics: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [prometheus] } ``` **Deployment-паттерны**: *agent* (DaemonSet/sidecar рядом с приложением — быстрый offload) + *gateway* (centralized — tail sampling, агрегация). Часто оба слоя сразу. ### Metrics в OTel-стиле (отличия от Prometheus client) OTel-метрики используют **instruments**: `Counter`, `UpDownCounter`, `Histogram`, и **асинхронные** (observable) `Gauge`/`Counter` с callback. Модель — push (OTLP) или pull (Prometheus exporter). Важная фича — **Views**: переименование, фильтрация атрибутов, кастомные бакеты гистограмм, drop ненужных инструментов — настраивается централизованно в SDK без правки кода инструментирования. ```go meter := otel.Meter("payments") reqDur, _ := meter.Float64Histogram("http.server.duration", metric.WithUnit("s"), metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5), ) reqDur.Record(ctx, elapsed.Seconds(), metric.WithAttributes(attribute.String("route", "/checkout"), attribute.Int("status", 200))) ``` ### Logs OTel logs в Go (`go.opentelemetry.io/contrib/bridges/otelslog`) — мост к `slog`: записи получают `trace_id`/`span_id` автоматически из контекста и экспортируются по OTLP. Часто на практике логи всё ещё собирают агентом из stdout, а через OTLP идут traces+metrics — но bridge даёт нативную корреляцию. ## Подводные камни / gotchas - **Забытый `Shutdown()`** → последний батч спанов/метрик теряется при остановке. Всегда `defer shutdown(ctx)` с таймаутом. - **`SimpleSpanProcessor` в проде** — синхронный экспорт на каждый `span.End()`, блокирует hot path. Только `BatchSpanProcessor`. - **Не прокинут `ctx`** (новый `context.Background()` где-то в середине) → разрыв трейса, спаны без родителя, "осиротевшие" trace_id в логах. - **`tracer.Start` без `defer span.End()`** → утечка незакрытых спанов, они не экспортируются. - **Высокая cardinality в атрибутах** (user_id, raw URL с ID, timestamp в label метрики) → взрыв временных рядов / стоимости. Кладите ID в спан-атрибуты (там ок), но НЕ в метрики. - **`record.Error()` ≠ статус спана**. `span.RecordError(err)` добавляет event, но НЕ помечает спан как failed. Нужно ещё `span.SetStatus(codes.Error, msg)`. - **Пропагатор не настроен** → каждый сервис стартует новый трейс, межсервисная связь рвётся. Дефолтный propagator в Go SDK — no-op, его НУЖНО задать явно. - **Семплинг без `ParentBased`** → один трейс семплируется частично (часть сервисов записала, часть нет) = битые трейсы. Всегда оборачивайте в `ParentBased`. - **`service.name` не задан** → бэкенд показывает `unknown_service`. Это самый частый «почему ничего не видно». - **Batch loss под нагрузкой**: при переполнении очереди (`MaxQueueSize`) спаны дропаются молча. Мониторьте `otel_sdk_*` метрики самого SDK. ## Вопросы на собеседовании **В:** В чём принципиальное различие между OTel API и SDK и почему оно так устроено? **О:** API — стабильный контракт с no-op реализацией, его используют библиотеки и код приложения. SDK — тяжёлая реализация (семплинг, батчинг, экспорт), подключается только в `main()`. Это позволяет инструментировать библиотеки без навязывания зависимости от вендора/SDK: если SDK не подключён — нулевой оверхед. Меняя SDK/экспортёр, не трогаешь инструментированный код. **В:** Зачем нужен Collector, если можно слать прямо в бэкенд? **О:** Развязка от вендора (смена бэкенда = конфиг, не редеплой), вынос батчинга/ретраев/буферизации из приложения, обработка (redact PII, нормализация атрибутов), и главное — tail-based sampling, который требует видеть весь трейс целиком и невозможен на стороне отдельного сервиса. **В:** Как трейс связывается между двумя сервисами? **О:** Через context propagation. На исходящем запросе пропагатор инжектит W3C `traceparent` (trace_id + span_id + flags) в заголовки. На входящем — извлекает в `context.Context` как remote parent. Дочерний спан наследует тот же `trace_id`, ссылается на родительский `span_id`. В Go это делают `otelhttp`/`otelgrpc` автоматически, при условии что `ctx` прокинут и пропагатор настроен. **В:** Разница между `RecordError` и `SetStatus`? **О:** `RecordError(err)` добавляет в спан событие типа exception (stacktrace, тип, сообщение) — но спан остаётся «успешным». `SetStatus(codes.Error, msg)` помечает сам спан как failed, что влияет на error-rate в UI и tail-sampling. Для корректного отображения ошибки нужны оба. **В:** Почему `BatchSpanProcessor`, а не `Simple`? **О:** Simple экспортирует синхронно на каждый `End()` — блокирует горячий путь и убивает латенси под нагрузкой. Batch складывает в очередь и шлёт батчами в фоне по таймеру/размеру, с back-pressure. Simple оправдан только в тестах/CLI. **В:** Что такое Baggage и в чём опасность? **О:** Baggage — произвольные key=value, пропагируемые по всему трейсу (tenant_id, feature flag) для прокидывания контекста между сервисами без явной передачи. Опасность: едет в заголовках каждого hop'а (overhead), может попасть в downstream-логи/трейсы (утечка PII), и легко раздуть размер. Не класть секреты, держать минимальным. **В:** Как сделать так, чтобы трейс был либо полностью записан, либо полностью пропущен, при вероятностном семплинге? **О:** `ParentBased(TraceIDRatioBased(p))`. Корневой спан принимает решение по детерминированному хешу trace_id с вероятностью p; все потомки через `ParentBased` уважают флаг sampled родителя (передаётся в traceparent). Так весь трейс консистентен. Без ParentBased каждый сервис решал бы независимо → битые частичные трейсы. ## На что копают на senior+ - **Архитектура pipeline под нагрузкой**: где теряются данные (queue overflow, memory_limiter дропает), как мониторить сам OTel SDK/Collector, back-pressure и его эффект на приложение. - **Tail-based vs head-based sampling**: trade-offs, почему tail требует gateway-collector с буфером всех спанов трейса до решения, проблема с долгими трейсами и памятью. - **Semantic conventions**: знание стандартных атрибутов (`http.request.method`, `db.system`, `service.name`) и почему их стабильность важна для дашбордов/алертов; миграция версий semconv. - **Cost engineering**: контроль cardinality, sampling-стратегии, exemplars для связи метрик и трейсов без полного хранения трейсов. - **Корреляция трёх сигналов**: один trace_id в логах (через slog bridge), exemplars в гистограммах метрик, и переход metric → trace → log в UI (Grafana Tempo/Loki/Mimir). - **Миграция/совместимость**: OpenCensus → OpenTelemetry bridge, переход с vendor-специфичных SDK, поддержка W3C + B3 пропагаторов одновременно для постепенной миграции.