Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions handwritten/spanner/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
/coverage
/.nyc_output
/docs/
dist/
/out/
/build/
system-test/secrets.js
system-test/*key.json
*.lock
.DS_Store
package-lock.json
*.cpuprofile
__pycache__
11 changes: 11 additions & 0 deletions handwritten/spanner/issue/Dockerfile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.25-bookworm AS build
WORKDIR /src
COPY issue/go/go.mod ./go.mod
COPY issue/go/go.sum ./go.sum
RUN go mod download
COPY issue/go/main.go ./main.go
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/insert-benchmark ./main.go

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/insert-benchmark /insert-benchmark
ENTRYPOINT ["/insert-benchmark"]
11 changes: 11 additions & 0 deletions handwritten/spanner/issue/Dockerfile.go-raw
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.25-bookworm AS build
WORKDIR /src
COPY issue/go/go.mod ./go.mod
COPY issue/go/go.sum ./go.sum
RUN go mod download
COPY issue/go/raw_grpc_benchmark.go ./raw_grpc_benchmark.go
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/raw-grpc-benchmark ./raw_grpc_benchmark.go

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/raw-grpc-benchmark /raw-grpc-benchmark
ENTRYPOINT ["/raw-grpc-benchmark"]
10 changes: 10 additions & 0 deletions handwritten/spanner/issue/Dockerfile.node
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:22-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
ARG SPANNER_VERSION=8.7.1
RUN npm init -y \
&& npm install --omit=dev "@google-cloud/spanner@${SPANNER_VERSION}" @grpc/grpc-js tsx \
&& npm cache clean --force
COPY --chown=node:node --chmod=0444 issue/node/benchmark-insert-repl.ts ./benchmark-insert-repl.mts
USER node
ENTRYPOINT ["/app/node_modules/.bin/tsx", "/app/benchmark-insert-repl.mts"]
27 changes: 27 additions & 0 deletions handwritten/spanner/issue/Dockerfile.node-current
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
FROM node:22-bookworm-slim AS local-lib
WORKDIR /workspace/spanner
COPY package*.json ./
RUN if [ -f package-lock.json ]; then \
npm ci --ignore-scripts; \
else \
npm install --ignore-scripts; \
fi
COPY . .
RUN npm run compile \
&& mkdir -p /tmp/spanner-pack \
&& npm pack --pack-destination /tmp/spanner-pack \
&& cp /tmp/spanner-pack/google-cloud-spanner-*.tgz /tmp/google-cloud-spanner.tgz

FROM node:22-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
RUN npm init -y >/dev/null 2>&1 || true \
&& npm install --omit=dev @grpc/grpc-js tsx \
&& npm cache clean --force
COPY --from=local-lib /tmp/google-cloud-spanner.tgz /tmp/google-cloud-spanner.tgz
RUN npm install --omit=dev /tmp/google-cloud-spanner.tgz \
&& npm cache clean --force \
&& node -e "console.log(require('@google-cloud/spanner/package.json').version)"
COPY --chown=node:node --chmod=0444 issue/node/benchmark-insert-repl.ts ./benchmark-insert-repl.mts
USER node
ENTRYPOINT ["/app/node_modules/.bin/tsx", "/app/benchmark-insert-repl.mts"]
26 changes: 26 additions & 0 deletions handwritten/spanner/issue/Dockerfile.node-raw
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM node:22-bookworm-slim AS local-lib
WORKDIR /workspace/spanner
COPY package*.json ./
RUN if [ -f package-lock.json ]; then \
npm ci --ignore-scripts; \
else \
npm install --ignore-scripts; \
fi
COPY . .
RUN npm run compile \
&& mkdir -p /tmp/spanner-pack \
&& npm pack --pack-destination /tmp/spanner-pack \
&& cp /tmp/spanner-pack/google-cloud-spanner-*.tgz /tmp/google-cloud-spanner.tgz

FROM node:22-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
RUN npm init -y >/dev/null 2>&1 || true \
&& npm install --omit=dev @grpc/grpc-js \
&& npm cache clean --force
COPY --from=local-lib /tmp/google-cloud-spanner.tgz /tmp/google-cloud-spanner.tgz
RUN npm install --omit=dev /tmp/google-cloud-spanner.tgz \
&& npm cache clean --force
COPY --chown=node:node --chmod=0444 issue/node/raw_grpc_benchmark.js ./raw_grpc_benchmark.js
USER node
ENTRYPOINT ["node", "/app/raw_grpc_benchmark.js"]
89 changes: 89 additions & 0 deletions handwritten/spanner/issue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Insert benchmark issue repro

Build and push all three images:

```sh
IMAGE_REPO=us-central1-docker.pkg.dev/span-cloud-testing/gargsurbhi-images \
SPANNER_VERSION=8.7.1 \
BUILD_CURRENT=true \
./issue/build-images.sh
```

Run jobs:

Connect to Kubernetes cluster
```sh
gcloud container clusters get-credentials cluster-1 --region us-central1 --project span-cloud-testing
```

Release baseline uses `issue-insert-node:latest` from npm `SPANNER_VERSION`. Current branch uses `issue-insert-node:current`. Release cluster uses `issue-insert-node-cluster:release-8.6.0`; current cluster uses `issue-insert-node-cluster:current`.

```sh
kubectl apply -f issue/k8s/go-insert-benchmark.yaml
kubectl apply -f issue/k8s/node-insert-benchmark.yaml
kubectl apply -f issue/k8s/node-current-insert-benchmark.yaml
kubectl apply -f issue/k8s/node-cluster-insert-benchmark.yaml
kubectl apply -f issue/k8s/node-current-cluster-insert-benchmark.yaml
```

Watch logs:

```sh
kubectl -n spanner-ns logs -f job/issue-insert-go
kubectl -n spanner-ns logs -f job/issue-insert-node
kubectl -n spanner-ns logs -f job/issue-insert-node-current
kubectl -n spanner-ns logs -f job/issue-insert-node-cluster
kubectl -n spanner-ns logs -f job/issue-insert-node-current-cluster
```

Defaults match the customer repro shape:

- `INSERT_COUNT=1000`
- `INSERT_CONCURRENCY=110`
- `BATCH_COUNT=1`
- 3 CPU request/limit

Node cluster job uses `CLUSTER_WORKERS=3`. It splits total work across workers:

- total batches stays `1000`
- total concurrency stays about `110` (`37 + 37 + 36`)
- each worker has a separate Node event loop and Spanner client

Use `VERBOSE_BATCH_LOGS=false` to remove per-batch logging overhead.

## Reference table setup for Go benchmark

`issue/go/main.go` samples IDs from these tables before inserting into `DeviceRecentActivityLog`:

- `tracking.Devices(deviceRecordId)`
- `tracking.DeviceDetails(deviceDetailsId)`
- `tracking.HttpRequestDetails(httpRequestDetailsId)`
- `tracking.HttpRequestLocations(httpRequestLocationId)`

The insert target table does not require these rows unless you add foreign keys. They are only needed by the Go benchmark script. Minimum to run: 1 row in the first 3 tables; locations can be empty. Recommended for default `SAMPLE_SIZE=10000`: seed 10000 rows in each table.

DDL:

```sh
gcloud spanner databases ddl update jack_henry_db \
--instance=gargsurbhi-testing1 \
--project=span-cloud-testing \
--ddl-file=issue/create-reference.sql
```

Seed 10000 rows per reference table:

```sh
node issue/seed-reference-data.js
```

Use env overrides if needed:

```sh
DB_PROJECT_ID=span-cloud-testing \
DB_INSTANCE=gargsurbhi-testing1 \
DB_DATABASE=jack_henry_db \
DB_SCHEMA=tracking \
SAMPLE_SIZE=10000 \
node issue/seed-reference-data.js
```
41 changes: 41 additions & 0 deletions handwritten/spanner/issue/build-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail

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}"

PUSH="${PUSH:-true}"
PLATFORM="${PLATFORM:-linux/amd64}"
CONTAINER_TOOL="${CONTAINER_TOOL:-podman}"

# Build Node Raw gRPC image
$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 .
Comment on lines +12 to +14
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 .


# Build Go image
"$CONTAINER_TOOL" build --platform "$PLATFORM" -f "$ROOT/issue/Dockerfile.go" \
-t "$IMAGE_REPO/issue-insert-go:latest" \
"$ROOT"

# Build Node image
"$CONTAINER_TOOL" build --platform "$PLATFORM" -f "$ROOT/issue/Dockerfile.node" \
--build-arg "SPANNER_VERSION=$SPANNER_VERSION" \
-t "$IMAGE_REPO/issue-insert-node:release-${SPANNER_VERSION}" \
-t "$IMAGE_REPO/issue-insert-node:latest" \
"$ROOT"

# Build Node image with current code changes
"$CONTAINER_TOOL" build --platform "$PLATFORM" -f "$ROOT/issue/Dockerfile.node-current" \
-t "$IMAGE_REPO/issue-insert-node:current" \
"$ROOT"


if [[ "$PUSH" == "true" ]]; then
$CONTAINER_TOOL push $IMAGE_REPO/issue-raw-grpc-node:latest
$CONTAINER_TOOL push $IMAGE_REPO/issue-raw-grpc-go:latest
"$CONTAINER_TOOL" push "$IMAGE_REPO/issue-insert-go:latest"
"$CONTAINER_TOOL" push "$IMAGE_REPO/issue-insert-node:release-${SPANNER_VERSION}"
"$CONTAINER_TOOL" push "$IMAGE_REPO/issue-insert-node:latest"
"$CONTAINER_TOOL" push "$IMAGE_REPO/issue-insert-node:current"
fi
23 changes: 23 additions & 0 deletions handwritten/spanner/issue/create-reference.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Minimal reference tables needed by issue/go/main.go.
-- DeviceRecentActivityLog insert itself has no foreign keys in issue/create.sql;
-- these tables only feed benchmark ID sampling.

CREATE TABLE tracking.Devices (
deviceRecordId STRING(64) NOT NULL,
createdAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (deviceRecordId);

CREATE TABLE tracking.DeviceDetails (
deviceDetailsId STRING(64) NOT NULL,
createdAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (deviceDetailsId);

CREATE TABLE tracking.HttpRequestDetails (
httpRequestDetailsId STRING(64) NOT NULL,
createdAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (httpRequestDetailsId);

CREATE TABLE tracking.HttpRequestLocations (
httpRequestLocationId STRING(64) NOT NULL,
createdAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (httpRequestLocationId);
20 changes: 20 additions & 0 deletions handwritten/spanner/issue/create.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CREATE TABLE
tracking.DeviceRecentActivityLog ( deviceRecentActivityLogId STRING(64) NOT NULL,
deviceRecordId STRING(64) NOT NULL,
httpRequestDetailsId STRING(64) NOT NULL,
deviceDetailsId STRING(64) NOT NULL,
ipAddress BYTES(24) NOT NULL,
ipAddressText STRING(MAX) AS (NET.IP_TO_STRING(ipAddress)),
xRequestId STRING(64) NOT NULL,
institutionId STRING(64),
userId STRING(64),
username STRING(64),
httpRequestLocationId STRING(64),
latency INT64,
createdAt TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP()),
sessionId STRING(256),
)
PRIMARY KEY
(deviceRecentActivityLogId),
ROW DELETION POLICY (OLDER_THAN(createdAt,
INTERVAL 20 DAY));
55 changes: 55 additions & 0 deletions handwritten/spanner/issue/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module spanner-insert-benchmark

go 1.25.0

require (
cloud.google.com/go/spanner v1.91.0
google.golang.org/api v0.278.0
google.golang.org/grpc v1.81.0
)

require (
cel.dev/expr v0.25.1 // indirect
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/auth v0.20.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
cloud.google.com/go/monitoring v1.25.0 // indirect
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect
github.com/googleapis/gax-go/v2 v2.22.0 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
Loading
Loading