Skip to content

perf(spanner): single-pass protobuf value encoding in codec - #9350

Open
olavloite wants to merge 1 commit into
mainfrom
spanner-single-pass-encoding
Open

olavloite wants to merge 1 commit into
mainfrom
spanner-single-pass-encoding

Conversation

@olavloite

Copy link
Copy Markdown
Contributor

codec.encode previously executed two passes for every query parameter and mutation value: first calling encodeValue to construct an intermediate JavaScript representation, and then passing that to GrpcService.encodeValue_. GrpcService.encodeValue_ allocated a new ObjectToStructConverter instance and an internal Set for each value, using .map() and bound closures for array transformations.

This change converts codec.encode to emit google.protobuf.Value messages directly in a single pass:

  • Primitives dispatch directly via a typeof switch.
  • Array members are converted in an indexed loop into a pre-sized array without intermediate collections or closures.
  • The import of GrpcService is removed from src/codec.ts, eliminating the dependency on ./common-grpc/service.
  • Encoding rules for Spanner types are preserved: finite non-integers encode as numbers; integers, NaN, and ±Infinity encode as strings; and wrapped Float/Float32 instances retain their numeric representation.
  • Cross-realm Date instances (e.g. from vm contexts) are handled via a tag check fallback on the object path.
  • Sparse arrays now fail immediately on missing indices rather than producing arrays with holes that fail during protobuf serialization.

`codec.encode` previously executed two passes for every query parameter
and mutation value: first calling `encodeValue` to construct an
intermediate JavaScript representation, and then passing that to
`GrpcService.encodeValue_`. `GrpcService.encodeValue_` allocated a new
`ObjectToStructConverter` instance and an internal `Set` for each value,
using `.map()` and bound closures for array transformations.

This change converts `codec.encode` to emit `google.protobuf.Value`
messages directly in a single pass:
- Primitives dispatch directly via a `typeof` switch.
- Array members are converted in an indexed loop into a pre-sized array
  without intermediate collections or closures.
- The import of `GrpcService` is removed from `src/codec.ts`, eliminating
  the dependency on `./common-grpc/service`.
- Encoding rules for Spanner types are preserved: finite non-integers encode
  as numbers; integers, `NaN`, and `±Infinity` encode as strings; and
  wrapped `Float`/`Float32` instances retain their numeric representation.
- Cross-realm `Date` instances (e.g. from `vm` contexts) are handled via
  a tag check fallback on the object path.
- Sparse arrays now fail immediately on missing indices rather than
  producing arrays with holes that fail during protobuf serialization.
@olavloite
olavloite requested a review from a team as a code owner September 16, 2026 07:33
@product-auto-label product-auto-label Bot added the api: spanner Issues related to the Spanner API. label Sep 16, 2026

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

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.

Code Review

This pull request refactors the encoding logic in codec.ts to directly emit the google.protobuf.Value shape in a single pass, removing the dependency on GrpcService and improving performance. It also updates and expands the test suite in test/codec.ts to verify the new encoding behavior across various data types and edge cases. Feedback on the changes suggests reordering the type checks in encodeObject to handle boxed primitives and cross-realm dates before generic objects, preventing potential serialization issues.

Comment thread handwritten/spanner/src/codec.ts
@olavloite

Copy link
Copy Markdown
Contributor Author

/gemini review

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

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.

Code Review

This pull request refactors the encode function in codec.ts to directly emit the google.protobuf.Value shape in a single pass, removing the dependency on GrpcService and improving performance by avoiding intermediate allocations. The test suite in test/codec.ts has been updated to remove GrpcService stubbing and expanded with comprehensive test cases covering various data types, nested structures, boxed primitives, and error handling for unsupported values. There are no review comments to address, and I have no further feedback to provide.

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

Labels

api: spanner Issues related to the Spanner API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant