Skip to content

[v2/resolve] Routing Refactoring & Header Overrides#1925

Open
clincoln8 wants to merge 6 commits into
datacommonsorg:masterfrom
clincoln8:routing
Open

[v2/resolve] Routing Refactoring & Header Overrides#1925
clincoln8 wants to merge 6 commits into
datacommonsorg:masterfrom
clincoln8:routing

Conversation

@clincoln8
Copy link
Copy Markdown
Contributor

Overview

This PR refactors V2Resolve routing logic to support request-time backend overrides for indicator resolution, simplifies the configuration model by managing default routing via a feature flag, and forces topic resolution to bypass the dispatcher (routing to local in-memory cache).

Key Changes

  • Granular Routing Handler (internal/server/handler_v2.go): Introduced shouldRouteResolveToDispatcher helper.
    • Place: Follows standard diversion (shouldDivertV2).
    • Topic: Bypasses dispatcher (returns false) to utilize local TopicCache, preventing empty candidates when Spanner is enabled.
    • Indicator: Supports request-time backend override via X-V2Resolve-Indicator-Backend header (spanner / legacy). Defaults to Spanner if Spanner is configured and EnableSpannerSearchEmbeddings flag is true.
  • Datasource Decoupling (internal/server/spanner/datasource.go): Removed the enableSpannerSearchEmbeddings flag from SpannerDataSource constructor and methods. The datasource execution capability is now determined purely by Spanner configuration at startup, with routing managed in the handler.
  • Flag Repurposing (internal/featureflags/featureflags.go): Updated EnableSpannerSearchEmbeddings flag description to define its role as the default routing toggle for indicators.
  • Unit Tests (internal/server/handler_v2_test.go): Added TestShouldRouteResolveToDispatcher to verify routing combinations and fail-fast behavior.
  • Boilerplate Updates (cmd/main.go, test/setup.go, spanner unit/golden tests): Refactored NewSpannerDataSource call sites to remove the flag argument.

Integration tests

Verified routing logic E2E with local NL embeddings server and test Spanner instances:

  • Indicators: Header overrides (spanner / legacy / absent) and feature flag defaults behave as expected. Fail-fast error verified.
  • Topics: Topic resolution for dc/topic/Agriculture successfully bypasses dispatcher and returns identical resolved candidates regardless of whether cache is loaded from Spanner or Bigtable.

Follow-up Work (PR 2)

We will migrate resolver=topic requests to resolve through the dispatcher when Spanner is enabled. This will involve:

  1. Allowing SpannerDataSource to delegate topic resolution to TopicExpander via closure-based lazy injection to break circular startup dependencies.
  2. Decoupling TopicCacheManager initialization from the embeddings resolver flag.
  3. Updating routing to divert topic resolver requests to the dispatcher when Spanner is enabled.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the routing logic for V2Resolve requests by introducing a shouldRouteResolveToDispatcher helper, which handles routing for place, topic, and indicator resolvers, and supports request-time backend overrides via the X-V2Resolve-Indicator-Backend header. It also cleans up the SpannerDataSource initialization by removing the unused enableSpannerSearchEmbeddings parameter and adds architectural documentation on flag usage. The review feedback highlights a potential initialization bug in cmd/main.go where s.useSpannerGraph might be incorrectly set, and suggests validating the new header values to prevent silent fallbacks on typos, along with adding corresponding test coverage.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

switch backendHeader {
case util.V2ResolveIndicatorBackendSpanner:
// Force Spanner: Fail fast if Spanner backend is not configured
if !s.useSpannerGraph {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Potential Bug: In cmd/main.go (line 473), NewMixerServer is initialized using *useSpannerGraph (the CLI flag) instead of shouldUseSpannerGraph (which also accounts for flags.UseSpannerGraph from feature flags).

As a result, if Spanner is enabled via feature flags but the CLI flag is false, s.useSpannerGraph will be false here, causing this check to incorrectly fail-fast even though the Spanner backend is fully initialized and running.

Please update cmd/main.go to pass shouldUseSpannerGraph to NewMixerServer.

Comment on lines +149 to +163
switch backendHeader {
case util.V2ResolveIndicatorBackendSpanner:
// Force Spanner: Fail fast if Spanner backend is not configured
if !s.useSpannerGraph {
slog.Error("Spanner backend requested via header, but Spanner is not enabled on this server")
return false, status.Errorf(codes.FailedPrecondition, "Spanner backend is not enabled in this mixer")
}
return true, nil
case util.V2ResolveIndicatorBackendLegacy:
// Force Legacy Remote Service
return false, nil
default:
// Default: use Spanner if configured AND default routing flag is true
return s.useSpannerGraph && s.flags.EnableSpannerSearchEmbeddings, nil
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the client provides an invalid or misspelled value for the X-V2Resolve-Indicator-Backend header (e.g., "spaner"), the switch statement will silently fall through to the default case and use the default routing.

To prevent silent failures and help clients debug typos, consider validating the header value and returning an InvalidArgument error if it is non-empty and unrecognized.

		switch backendHeader {
		case util.V2ResolveIndicatorBackendSpanner:
			// Force Spanner: Fail fast if Spanner backend is not configured
			if !s.useSpannerGraph {
				slog.Error("Spanner backend requested via header, but Spanner is not enabled on this server")
				return false, status.Errorf(codes.FailedPrecondition, "Spanner backend is not enabled in this mixer")
			}
			return true, nil
		case util.V2ResolveIndicatorBackendLegacy:
			// Force Legacy Remote Service
			return false, nil
		case "":
			// Default: use Spanner if configured AND default routing flag is true
			return s.useSpannerGraph && s.flags.EnableSpannerSearchEmbeddings, nil
		default:
			return false, status.Errorf(codes.InvalidArgument, "invalid backend requested via header: %s", backendHeader)
		}

Comment on lines +345 to +346
},
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To verify the validation of the X-V2Resolve-Indicator-Backend header, consider adding a test case for an invalid header value.

		},
		{
			desc:            "Indicator resolver - Invalid header value -> error",
			useSpannerGraph: true,
			resolver:        resolve.ResolveResolverIndicator,
			headerVal:       "invalid-backend",
			wantErr:         true,
		},
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant