Skip to content

[Performance] Debugging issue with node#8287

Draft
surbhigarg92 wants to merge 7 commits into
googleapis:mainfrom
surbhigarg92:debugging-issue-with-node
Draft

[Performance] Debugging issue with node#8287
surbhigarg92 wants to merge 7 commits into
googleapis:mainfrom
surbhigarg92:debugging-issue-with-node

Conversation

@surbhigarg92
Copy link
Copy Markdown
Contributor

No description provided.

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 introduces a benchmarking suite for Spanner insert operations, including Go and Node.js implementations, containerization scripts, and Kubernetes manifests. However, the PR also includes significant regressions in the core library: it disables OpenTelemetry context initialization, guts tracing functionality, removes automatic project ID token replacement in the base gRPC service, and stops injecting request ID headers. Feedback highlights that these changes, while potentially useful for performance isolation, should be controlled by flags rather than hardcoded in the source. Additionally, inconsistencies were found in the benchmark scripts regarding Spanner versions, hardcoded build platforms, and gRPC connection pool sizes.

this._observabilityOptions?.enableEndToEndTracing,
);
ensureInitialContextManagerSet();
// ensureInitialContextManagerSet();
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

Commenting out ensureInitialContextManagerSet() disables the initialization of the OpenTelemetry context manager. This will break context propagation for tracing and metrics unless the user has manually configured a global context manager. If this was intended for performance debugging, it should be controlled via a flag rather than being commented out in the source code.

delete reqOpts.objectMode;

return replaceProjectIdToken(reqOpts, this.projectId);
return reqOpts;
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

Removing replaceProjectIdToken from the base GrpcService class breaks the automatic placeholder resolution for all services extending this class. While the Spanner class has been modified to handle this (albeit with issues), other services or future extensions will fail if they rely on this base functionality.

Suggested change
return reqOpts;
return replaceProjectIdToken(reqOpts, this.projectId);

}
},
);
return cb(new noopSpan());
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

Gutting the startTrace function effectively disables all tracing functionality in the library. While this might be useful for isolating performance bottlenecks during debugging, it is a major regression in functionality that should not be merged into production code.


attempt = attempt || 1;
return _metadataWithRequestId(session, nthRequest!, attempt, headers);
return headers;
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

Disabling the injection of the x-goog-spanner-request-id header removes the ability to correlate client-side requests with server-side logs. This is a critical diagnostic tool for Spanner support and debugging.


ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
IMAGE_REPO="${IMAGE_REPO:-us-central1-docker.pkg.dev/span-cloud-testing/gargsurbhi-images}"
SPANNER_VERSION="${SPANNER_VERSION:-8.17.1}"
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

The default SPANNER_VERSION is set to 8.17.1, but the Kubernetes manifests and other Dockerfiles refer to 8.7.1. This discrepancy might lead to image pull errors or inconsistent benchmark results.

Suggested change
SPANNER_VERSION="${SPANNER_VERSION:-8.17.1}"
SPANNER_VERSION="${SPANNER_VERSION:-8.7.1}"

Comment on lines +12 to +14
$CONTAINER_TOOL build --platform linux/amd64 -f issue/Dockerfile.node-raw -t $IMAGE_REPO/issue-raw-grpc-node:latest .
# Build Go Raw gRPC image
$CONTAINER_TOOL build --platform linux/amd64 -f issue/Dockerfile.go-raw -t $IMAGE_REPO/issue-raw-grpc-go:latest .
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

The platform is hardcoded to linux/amd64 here, ignoring the $PLATFORM variable defined earlier in the script. This prevents building for other architectures (e.g., linux/arm64).

Suggested change
$CONTAINER_TOOL build --platform linux/amd64 -f issue/Dockerfile.node-raw -t $IMAGE_REPO/issue-raw-grpc-node:latest .
# Build Go Raw gRPC image
$CONTAINER_TOOL build --platform linux/amd64 -f issue/Dockerfile.go-raw -t $IMAGE_REPO/issue-raw-grpc-go:latest .
$CONTAINER_TOOL build --platform "$PLATFORM" -f issue/Dockerfile.node-raw -t $IMAGE_REPO/issue-raw-grpc-node:latest .
# Build Go Raw gRPC image
$CONTAINER_TOOL build --platform "$PLATFORM" -f issue/Dockerfile.go-raw -t $IMAGE_REPO/issue-raw-grpc-go:latest .

fmt.Println("gRPC Connection Pool Size: 1")

// Initialize the generated client with a single connection to match Node.js default behavior
client, err := gapic.NewClient(ctx, option.WithGRPCConnectionPool(4))
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

There is a mismatch between the code and the preceding comment. The comment states the client is initialized with a single connection, but the code uses a pool size of 4. Additionally, this is inconsistent with main.go which uses a pool size of 1, potentially making performance comparisons between the two benchmarks misleading.

Suggested change
client, err := gapic.NewClient(ctx, option.WithGRPCConnectionPool(4))
client, err := gapic.NewClient(ctx, option.WithGRPCConnectionPool(1))

let reqOpts;
if (!this.projectIdReplaced_) {
reqOpts = extend(true, {}, config.reqOpts);
reqOpts = replaceProjectIdToken(reqOpts, projectId!);
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.

Isn't this 'undoing' the change further up? As in: Isn't this method called for every gRPC invocation, which means that this method will still be called for each request?

Copy link
Copy Markdown
Contributor Author

@surbhigarg92 surbhigarg92 May 18, 2026

Choose a reason for hiding this comment

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

prepareGapicRequest_ is called for each request. For the first request the projectIdReplaced_ will be replaced and set to true . And then database.formattedName_ and instance.formattedName_ will replace {{projectId}} to the actual project ID.

On subsequent requests the reqOpts will be constructed from database.formattedName_ and instance.formattedName_. which will have replaced projectID.

Having said that, I will do more testing to confirm this behavior, specially for parallel calls.

);
});
});
config.headers[CLOUD_RESOURCE_HEADER] = replaceProjectIdToken(
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.

And here also? (Or is this method only called once at startup?)

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.

3 participants