Skip to content

Fix slow client creation by adding passthrough:/// and SkipDialSettingsValidation#8

Merged
apstndb merged 3 commits intomainfrom
fix/passthrough-skip-dial-settings-validation
Mar 7, 2026
Merged

Fix slow client creation by adding passthrough:/// and SkipDialSettingsValidation#8
apstndb merged 3 commits intomainfrom
fix/passthrough-skip-dial-settings-validation

Conversation

@apstndb
Copy link
Copy Markdown
Owner

@apstndb apstndb commented Mar 7, 2026

Summary

  • Add passthrough:/// prefix to endpoint in ClientOptions() and defaultClientOpts() to bypass gRPC name resolution and the slow auth path
  • Add internaloption.SkipDialSettingsValidation() (required because passthrough:/// fails default endpoint validation)
  • Add comments explaining the rationale and upstream references

Background

The Spanner client library's init() hooks automatically apply passthrough:/// and SkipDialSettingsValidation when SPANNER_EMULATOR_HOST is set (googleapis/google-cloud-go#10947). Since spanemuboost connects via emu.URI() without setting that env var, these options were missing.

Currently the auth layer uses grpc.DialContext (passthrough resolver by default), so the performance issue is not actively triggered. However, when the auth layer migrates to grpc.NewClient (dns resolver by default), spanemuboost users would silently regress with ~100x slower client creation. This is a defensive fix that aligns with the same pattern used by Spanner, Bigtable, and Datastore SDKs for their emulator paths.

See #7 for the full timeline and analysis.

Test plan

  • go build ./... passes
  • go vet ./... passes
  • go test -run TestEmulatorAccessors -v (updated assertion from 3 to 4 options)
  • Full integration tests: go test ./... -count=1

Fixes #7

🤖 Generated with Claude Code

…gsValidation

Without SPANNER_EMULATOR_HOST set, the Spanner client library doesn't add
the passthrough:/// prefix or skip dial settings validation. This causes
~100x slowdown (~1s per client creation) because the new authentication
library hits a slow auth path. Since spanemuboost connects via emu.URI()
without setting the env var, the fix from googleapis/google-cloud-go#10947
was being bypassed.

Add passthrough:/// prefix to endpoint and internaloption.SkipDialSettingsValidation()
to both ClientOptions() and defaultClientOpts().

Fixes #7

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical performance bottleneck affecting client creation for the Spanner emulator, particularly when the SPANNER_EMULATOR_HOST environment variable is not set. By introducing specific gRPC client options, including a passthrough:/// prefix for endpoints and skipping dial settings validation, the changes drastically reduce the time required to instantiate clients, improving overall developer experience and efficiency.

Highlights

  • Performance Fix: Implemented changes to significantly reduce client creation time for the Spanner emulator, addressing a ~100x slowdown when the SPANNER_EMULATOR_HOST environment variable is not set.
  • gRPC Optimization: Added passthrough:/// prefix to endpoint configurations in ClientOptions() and defaultClientOpts() to bypass gRPC name resolution overhead.
  • Validation Skip: Included internaloption.SkipDialSettingsValidation() to avoid unnecessary dial settings validation for emulator connections.
Changelog
  • emulator.go
    • Imported google.golang.org/api/option/internaloption.
    • Modified ClientOptions() to prefix the endpoint with passthrough:/// and added internaloption.SkipDialSettingsValidation().
  • internal.go
    • Imported google.golang.org/api/option/internaloption.
    • Modified defaultClientOpts() to prefix the endpoint with passthrough:/// and added internaloption.SkipDialSettingsValidation().
  • spanemuboost_test.go
    • Updated the expected count of client options in TestEmulatorAccessors from 3 to 4.
Activity
  • go build ./... passes.
  • go vet ./... passes.
  • The TestEmulatorAccessors test assertion was updated from 3 to 4 options.
  • Full integration tests (go test ./... -count=1) are planned.
  • The PR was generated with Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@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 aims to fix slow client creation by adding passthrough:/// to the endpoint and SkipDialSettingsValidation. These changes should effectively address the performance overhead with emulator connections. The use of google.golang.org/api/option/internaloption introduces a dependency on an internal, unstable API, which has been highlighted in a comment due to potential maintainability risks.

emulator.go Outdated
option.WithEndpoint("passthrough:///" + e.container.URI()),
option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
option.WithoutAuthentication(),
internaloption.SkipDialSettingsValidation(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using internaloption.SkipDialSettingsValidation() introduces a dependency on an internal package of the Google Cloud client library. Internal APIs are not guaranteed to be stable and can change or be removed in future versions without warning, which could break this library.

While the performance gain is significant, it's important to acknowledge this trade-off. Consider adding a comment here explaining why this internal option is used and the potential for future breakage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good point. Added inline comments and a doc comment explaining:

Regarding the internaloption stability concern: this is a valid trade-off, but the same internal option is used by multiple Google Cloud SDKs in their emulator code paths, so it's unlikely to be removed without a replacement. If it does break, the fix would be straightforward.

…n rationale

Document why the passthrough:/// scheme and internaloption.SkipDialSettingsValidation
are used, referencing the upstream Spanner/Bigtable/Datastore SDKs that follow
the same pattern. Note that this is currently a defensive measure for the planned
grpc.DialContext → grpc.NewClient migration in the auth layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@apstndb
Copy link
Copy Markdown
Owner Author

apstndb commented Mar 7, 2026

/gemini review

Copy link
Copy Markdown

@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 introduces a performance optimization for client creation by adding the passthrough:/// prefix and SkipDialSettingsValidation option. This is a good defensive measure to align with the practices of official Google Cloud SDKs and prevent future performance regressions. The changes are well-documented and the test updates are appropriate. My review includes a suggestion to refactor a small amount of code duplication to improve long-term maintainability.

emulator.go Outdated
Comment on lines 40 to 49
return []option.ClientOption{
option.WithEndpoint(e.container.URI()),
// passthrough:/// tells gRPC to use the address as-is without DNS resolution.
option.WithEndpoint("passthrough:///" + e.container.URI()),
option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
option.WithoutAuthentication(),
// SkipDialSettingsValidation is required because the passthrough:/// prefix
// fails the default endpoint validation. This is an internal option also used
// by the Spanner, Bigtable, and Datastore client libraries for emulator paths.
internaloption.SkipDialSettingsValidation(),
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This function's implementation is identical to defaultClientOpts in internal.go. To avoid code duplication and improve maintainability, I suggest refactoring this to call defaultClientOpts. I've made a related suggestion on internal.go to move the inline comments there to keep the explanation with the implementation.

	return defaultClientOpts(e.container)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done. ClientOptions() now delegates to defaultClientOpts(e.container). Inline comments moved to defaultClientOpts as the single implementation.

defaultClientOpts is shared between ClientOptions() (new API) and the deprecated newClients path. Once the deprecated path is removed, defaultClientOpts should be inlined back into ClientOptions().

Comment on lines 326 to 331
return []option.ClientOption{
option.WithEndpoint(emulator.URI()),
option.WithEndpoint("passthrough:///" + emulator.URI()),
option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
option.WithoutAuthentication(),
internaloption.SkipDialSettingsValidation(),
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To centralize the logic for creating client options as suggested in the comment on emulator.go, the inline comments explaining the options should be moved here. This keeps the implementation and its documentation together.

	return []option.ClientOption{
		// passthrough:/// tells gRPC to use the address as-is without DNS resolution.
		option.WithEndpoint("passthrough:///" + emulator.URI()),
		option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
		option.WithoutAuthentication(),
		// SkipDialSettingsValidation is required because the passthrough:/// prefix
		// fails the default endpoint validation. This is an internal option also used
		// by the Spanner, Bigtable, and Datastore client libraries for emulator paths.
		internaloption.SkipDialSettingsValidation(),
	}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done. Inline comments are now in defaultClientOpts alongside the implementation.

ClientOptions() and defaultClientOpts() had identical implementations.
Refactor ClientOptions() to delegate to defaultClientOpts(), and move
inline comments to defaultClientOpts() as the single source of truth.

Note that defaultClientOpts should be inlined back into ClientOptions()
once the deprecated newClients path is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@apstndb
Copy link
Copy Markdown
Owner Author

apstndb commented Mar 7, 2026

/gemini review

Copy link
Copy Markdown

@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 proactively addresses a potential performance issue with Spanner client creation by adding the passthrough:/// prefix to the endpoint and including internaloption.SkipDialSettingsValidation. The code is well-commented and refactored to avoid duplication. I have one suggestion to improve the corresponding test to make it more specific.

Comment on lines +256 to 258
if opts := emu.ClientOptions(); len(opts) != 4 {
t.Errorf("ClientOptions() returned %d options, want 4", len(opts))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This test correctly checks that the number of client options has increased to 4. However, it could be made more robust by also verifying the content of the options. Specifically, it would be valuable to assert that one of the options sets the endpoint with the passthrough:/// prefix, as this is a key part of the change.

While inspecting option.ClientOption can be tricky and might require reflection, it would provide a stronger guarantee that ClientOptions() is behaving as expected and make the test less brittle to future changes that might alter the number of options.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks for the suggestion. I'll leave this as a count check for now — option.ClientOption is an opaque interface, so verifying content would require reflection or type assertions against internal types, adding complexity without proportional value. The count check serves as a sufficient guard to catch unintended additions or removals.

@apstndb
Copy link
Copy Markdown
Owner Author

apstndb commented Mar 7, 2026

/gemini summary

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

This pull request addresses a potential future performance regression in Spanner emulator client creation by proactively optimizing gRPC client options. By introducing a 'passthrough:///' endpoint prefix and skipping dial settings validation, the changes ensure efficient client instantiation, preventing a significant slowdown that would occur with a planned migration in the authentication layer. This aligns the library's behavior with established patterns in other Google Cloud SDKs for emulator connections.

Highlights

  • Performance Optimization: Implemented a defensive fix to prevent a ~100x slowdown in Spanner emulator client creation by bypassing gRPC name resolution and the slow authentication path.
  • gRPC Client Options: Added the 'passthrough:///' prefix to the endpoint in client options to ensure direct connection without DNS resolution.
  • Validation Bypass: Included 'internaloption.SkipDialSettingsValidation()' to accommodate the 'passthrough:///' prefix, which would otherwise fail default endpoint validation.
  • Code Alignment: Aligned the client option configuration with patterns used by other Google Cloud SDKs (Spanner, Bigtable, Datastore) for their emulator paths.
Changelog
  • emulator.go
    • Removed unused gRPC imports.
    • Refactored 'ClientOptions()' to delegate to 'defaultClientOpts()' for shared logic.
    • Added detailed comments explaining the rationale for the client options.
  • internal.go
    • Imported 'google.golang.org/api/option/internaloption'.
    • Modified 'defaultClientOpts()' to include 'passthrough:///' prefix for the endpoint and 'internaloption.SkipDialSettingsValidation()'.
    • Added extensive comments to 'defaultClientOpts()' explaining the purpose of the new options and their background.
  • spanemuboost_test.go
    • Updated the assertion in 'TestEmulatorAccessors' to expect 4 client options instead of 3, reflecting the addition of 'SkipDialSettingsValidation'.
Activity
  • 'go build ./...' passes.
  • 'go vet ./...' passes.
  • The 'TestEmulatorAccessors' test assertion was updated from 3 to 4 options.
  • Full integration tests ('go test ./... -count=1') pass.
  • A review comment from gemini-code-assist[bot] highlighted the use of an internal option and potential stability concerns, which the author addressed by adding explanatory comments.
  • A review comment from gemini-code-assist[bot] suggested refactoring 'ClientOptions()' to call 'defaultClientOpts()', which the author implemented.
  • A review comment from gemini-code-assist[bot] suggested moving inline comments to 'defaultClientOpts()', which the author implemented.
  • A review comment from gemini-code-assist[bot] suggested making the 'ClientOptions()' test more robust by verifying content, but the author decided against it due to complexity versus value.

@apstndb apstndb merged commit 4edd68d into main Mar 7, 2026
2 checks passed
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.

perf: Add passthrough:/// and SkipDialSettingsValidation to Emulator.ClientOptions()

1 participant