From 8b890a97fae3aa66837180e6f1cedcc68fe4a6b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 8 May 2026 09:23:17 +0000 Subject: [PATCH] fix(otelutil,search-service): env-skip OTLP and env-drive search indices Two small dev-loop quality fixes bundled into one PR. pkg/otelutil: - InitTracer skips the OTLP gRPC exporter setup when neither OTEL_EXPORTER_OTLP_ENDPOINT nor OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set, installing only the W3C TraceContext propagator and returning a no-op shutdown. Stops `traces export: connection refused` log spam in local dev / CI when no OTel collector runs. With the env set, behavior is unchanged. search-service: - New SPOTLIGHT_INDEX env var (required), plumbed through handlerConfig into searchRooms. handler.go was hardcoding the package-level const `SpotlightIndex = "spotlight"` which 404s against site-suffixed indices written by search-sync-worker. - Tighten USER_ROOM_INDEX from `envDefault:""` to `,required` so the empty-default 404 is caught at startup instead of silently returning zero results. - deploy/docker-compose.yml pins both env vars to the concrete site-local indices so `make up` works against a fresh dev stack. Prod uses aliases owned by ops/IaC. https://claude.ai/code/session_01UkLD7hpaypxjeh5zbEWTjp --- pkg/otelutil/otel.go | 10 ++++++++-- search-service/deploy/docker-compose.yml | 3 +++ search-service/handler.go | 3 ++- search-service/handler_test.go | 5 ++++- search-service/integration_test.go | 1 + search-service/main.go | 4 +++- search-service/query_rooms.go | 5 ----- 7 files changed, 21 insertions(+), 10 deletions(-) diff --git a/pkg/otelutil/otel.go b/pkg/otelutil/otel.go index 147980d1d..9d320e077 100644 --- a/pkg/otelutil/otel.go +++ b/pkg/otelutil/otel.go @@ -3,6 +3,7 @@ package otelutil import ( "context" "fmt" + "os" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" @@ -14,9 +15,14 @@ import ( semconv "go.opentelemetry.io/otel/semconv/v1.26.0" ) -// InitTracer creates and registers a TracerProvider with OTLP gRPC exporter. -// Returns a shutdown function. +// InitTracer is a no-op (propagator only) when no OTLP endpoint env var is +// set, to avoid `traces export: connection refused` spam in dev / CI. func InitTracer(ctx context.Context, serviceName string) (func(context.Context) error, error) { + if os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") == "" && os.Getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") == "" { + otel.SetTextMapPropagator(propagation.TraceContext{}) + return func(context.Context) error { return nil }, nil + } + exp, err := otlptracegrpc.New(ctx) if err != nil { return nil, fmt.Errorf("otlp exporter: %w", err) diff --git a/search-service/deploy/docker-compose.yml b/search-service/deploy/docker-compose.yml index b4c191c99..9402c4b9e 100644 --- a/search-service/deploy/docker-compose.yml +++ b/search-service/deploy/docker-compose.yml @@ -19,6 +19,9 @@ services: - SEARCH_RECENT_WINDOW=8760h - SEARCH_REQUEST_TIMEOUT=10s - SEARCH_METRICS_ADDR=:9090 + # Local dev pins to site-suffixed indices; prod uses aliases owned by ops/IaC. + - SEARCH_USER_ROOM_INDEX=user-room-site-local + - SEARCH_SPOTLIGHT_INDEX=spotlight-site-local-v1-chat ports: # Expose /metrics so Prometheus / curl can scrape from the host # during local dev. The listener is bound on 0.0.0.0:9090 inside diff --git a/search-service/handler.go b/search-service/handler.go index 9e2cfafa6..06753a9e4 100644 --- a/search-service/handler.go +++ b/search-service/handler.go @@ -20,6 +20,7 @@ type handlerConfig struct { RecentWindow time.Duration RequestTimeout time.Duration UserRoomIndex string + SpotlightIndex string } type handler struct { @@ -136,7 +137,7 @@ func (h *handler) searchRooms(c *natsrouter.Context, req model.SearchRoomsReques } observeESDone := observeES() - raw, err := h.store.Search(ctx, []string{SpotlightIndex}, body) + raw, err := h.store.Search(ctx, []string{h.cfg.SpotlightIndex}, body) observeESDone() if err != nil { slog.Error("room search backend failed", "account", account, "error", err) diff --git a/search-service/handler_test.go b/search-service/handler_test.go index 3a13f2aeb..9fa720847 100644 --- a/search-service/handler_test.go +++ b/search-service/handler_test.go @@ -14,6 +14,8 @@ import ( "github.com/hmchangw/chat/pkg/natsrouter" ) +const testSpotlightIndex = "spotlight" + type fakeStore struct { searchCalls []searchCall searchBody json.RawMessage @@ -84,6 +86,7 @@ func newTestHandler(store SearchStore, cache RestrictedRoomCache) *handler { MaxDocCounts: 100, RestrictedRoomsCacheTTL: 5 * time.Minute, RecentWindow: 365 * 24 * time.Hour, + SpotlightIndex: testSpotlightIndex, }) } @@ -234,7 +237,7 @@ func TestHandler_SearchRooms_ScopeAllHappyPath(t *testing.T) { assert.Equal(t, "r1", resp.Results[0].RoomID) require.Len(t, store.searchCalls, 1) - assert.Equal(t, []string{SpotlightIndex}, store.searchCalls[0].indices) + assert.Equal(t, []string{testSpotlightIndex}, store.searchCalls[0].indices) } func TestHandler_SearchRooms_ScopeAppRejected(t *testing.T) { diff --git a/search-service/integration_test.go b/search-service/integration_test.go index d5505c4d3..4dfc2d813 100644 --- a/search-service/integration_test.go +++ b/search-service/integration_test.go @@ -125,6 +125,7 @@ func setupCCSFixture(t *testing.T) *ccsFixture { RestrictedRoomsCacheTTL: 5 * time.Minute, RecentWindow: 365 * 24 * time.Hour, UserRoomIndex: userRoomIndex, + SpotlightIndex: "spotlight-test", }) router := natsrouter.New(serverNC, "search-service-test") diff --git a/search-service/main.go b/search-service/main.go index 212f5d2d7..57de16233 100644 --- a/search-service/main.go +++ b/search-service/main.go @@ -48,7 +48,8 @@ type SearchConfig struct { RestrictedRoomsCacheTTL time.Duration `env:"RESTRICTED_ROOMS_CACHE_TTL" envDefault:"5m"` RecentWindow time.Duration `env:"RECENT_WINDOW" envDefault:"8760h"` RequestTimeout time.Duration `env:"REQUEST_TIMEOUT" envDefault:"10s"` - UserRoomIndex string `env:"USER_ROOM_INDEX" envDefault:""` + UserRoomIndex string `env:"USER_ROOM_INDEX,required"` + SpotlightIndex string `env:"SPOTLIGHT_INDEX,required"` MetricsAddr string `env:"METRICS_ADDR" envDefault:":9090"` } @@ -116,6 +117,7 @@ func main() { RecentWindow: cfg.Search.RecentWindow, RequestTimeout: cfg.Search.RequestTimeout, UserRoomIndex: cfg.Search.UserRoomIndex, + SpotlightIndex: cfg.Search.SpotlightIndex, }) router := natsrouter.New(nc, "search-service") diff --git a/search-service/query_rooms.go b/search-service/query_rooms.go index 9c536c16a..bfeac5e38 100644 --- a/search-service/query_rooms.go +++ b/search-service/query_rooms.go @@ -8,11 +8,6 @@ import ( "github.com/hmchangw/chat/pkg/natsrouter" ) -// SpotlightIndex is the (local-only) room-typeahead index. Always one -// document per (user, room) pair — the spotlight index keeps search-as-you- -// type docs locally even when messages live on remote sites. -const SpotlightIndex = "spotlight" - // room scope filter values accepted on SearchRoomsRequest.Scope. const ( scopeAll = "all"