timw commented on PR #862:
URL: https://github.com/apache/pekko-grpc/pull/862#issuecomment-5431549288

   > ### Correctness
   > **1. `handleUnary` silently drops the caller's `eHandler` when marshalling 
fails** — 
`runtime/src/main/scala/org/apache/pekko/grpc/scaladsl/GrpcMarshalling.scala`
   
   Good catch. This is why I don't really like default args in general, but 
should have caught this.
   Fixed with a test.
   
   > **2. `GoogleProtobufSerializer.serialize(t, frame, offset)` ignores 
`offset`** — 
`runtime/src/main/scala/org/apache/pekko/grpc/javadsl/GoogleProtobufSerializer.scala`
   
   Thanks for spotting. 
   Fixed with a test.
   
   > ### API / binary compatibility
   > **3. Moving the frame-serialization methods onto public 
`ProtobufSerializer` is a source and binary break.**
   
   I hadn't realised this was public - originally I didn't like the leaking of 
`AbstractGrpcProtocol` mechanics into the serializer, but it makes sense to 
retain it as a distinct operation.
   Have reinstated this as an optional extension interface with `@ApiMayChange` 
as it's a useful opt-in optimisation for an implementation.
   
   > Related: against the `1.0.2` MiMa baseline I'd also expect flags for the 
removal of `GrpcProtocolWriter.frameEncoder` and the `encodeDataToResponse` 
signature change (`ByteString` -> `ElementFrame[?]`), plus 
`copy`/`apply`/`this` on that case class. `GrpcProtocol` is `@InternalApi` but 
MiMa doesn't read the annotation, and there's nothing covering it in 
`runtime/src/main/mima-filters/`. Worth confirming `sbt 
+mimaReportBinaryIssues` comes back clean, with excludes added where the break 
really is internal-only.
   
   I'll fix these up once the API surface changes have settled down.
    
   > ### Benchmark numbers
   > **4. `GrpcMarshallingBenchmark.handleUnaryStrict` regressed 53.4M -> 23.0M 
ops/s (-57%)** — that's a 2.3x slowdown on exactly the path #686 optimised, 
measured with the same new benchmark against both trees. Calling the 
`GrpcMarshallingBenchmark` dips "probably too fine grained" undersells it a 
bit. The honest framing is: ~43ns vs ~19ns on an operation that sits inside a 
~2us end-to-end handler, so it's lost in the noise at the handler level (and 
`generatedUnaryStrict` did go 509k -> 533k). That's a defensible trade for the 
code deletion — but I'd state it in the PR body rather than wave it away, so 
whoever reads these numbers next understands what was traded.
   
   This is a fair criticism - I was lax in trying to describe essentially what 
you have: that these benchmarks are so fine grained that even small tweaks 
result in big swings in results that are negligible or compensated for in 
overall throughput of higher level benchmarks.
   
   > **5. `marshallStream` regressed 873k -> 793k (-9%)**, and there's a 
plausible cause in `GrpcEntityHelpers.chunks`:
   > 
   > ```scala
   > e.map { msg => ElementFrame(msg) }.map(writer.encodeFrame).via(...)
   > ```
   
   Thanks - fixed this and the performance regression is down to 0-3%.
   Will redo full benchmarks once some of the other changes have stabilised.
   
   > ### Smaller things
   > * **No new tests.** The only runtime test change is a deletion 
(`GrpcMarshallingSpec` "recover a failed unary response stage"), fair enough 
since `handleUnaryResponse` is gone — but nothing replaces it. Given (1) 
slipped through unnoticed, a test asserting that a custom `eHandler` maps a 
marshalling failure on a unary call would pay for itself.
   
   Fair - have added 'regression' tests for the issues above, but will review 
overall test coverage before finalising.
   
   > * **`benchmarks/src/main/protobuf/helloworld.proto`** is a verbatim copy 
of the plugin-tester proto, `//#walkthrough` / `//#options` paradox snippet 
markers and all. Those markers are meaningless here and the copy will drift. 
Worth at least stripping them, or sharing the source.
   
   Fixed
   
   > * **Wildcard imports**: the new benchmark files use `import 
pekko.http.scaladsl.model.*` / `pekko.http.javadsl.model.*`. Project convention 
elsewhere is `._`. Cosmetic, but easy to align.
   
   Fixed
   
   > * **`@inline` sprinkling**: the build does enable the Scala 2 inliner so 
these aren't pure no-ops on 2.13, but they do nothing on Scala 3, and several 
are on methods taking function parameters or overriding abstract members, where 
the call site can't resolve a concrete target anyway. I'd keep only the ones 
you can tie to a number.
   
   I did see some small shifts in the benchmark results, but it could be within 
the noise - will review and remove them if there's nothing significant that the 
JIT doesn't eventually get to.
   
   > * **`BenchRunner`** loses the sorted summary report it used to print — 
that's a UX change for anyone running `benchmarks/run` directly. Intentional?
   
   This one was unintentional - I had pruned that code as it was effectively 
printing the same results as the default jmh runner output, but I missed the 
fact it was sorted. Reinstated with some minor formatting improvements.
   
   > * **`ByteStringOutputStream.toByteStringUnsafe`** hands out a `ByteString` 
wrapping the still-live `ByteArrayOutputStream` buffer. Safe at both current 
call sites (the stream is dead afterwards), but it's a general-purpose 
`private[grpc]` utility now — worth a scaladoc line saying the stream must not 
be written to afterwards.
   
   Added comments to this effect.
   
   > * Naming: `ElementFrame`'s parameter is still called `data` in 
`encodeDataToResponse`/`encodeDataToFrameBytes` even though it's now a frame, 
and the `serialize(t)` / `serialize(t, frame, offset)` overload pair reads 
ambiguously — `serializeTo` would be clearer.
   
   Fixed as suggested.
    
   > ### Design note
   > Putting a `ProtobufSerializer` inside a `Frame` moves protobuf knowledge 
into the protocol writer, which had been a purely byte-oriented layer. It does 
buy the single-allocation encode across all protocol variants and codecs, which 
is the point — but I'd frame it as trading one leak for another rather than as 
strictly better encapsulation, and it's the direct cause of (3). Scoping the 
new methods to `ProtobufFrameSerializer` keeps the win without the public-API 
cost.
   
   Yeah, I'm not entirely happy with the approach - I like it better than the 
existing, which shifts the logic of what consitutes an optimisable protocol 
frame out of the GrpcProtocol classes into marshalling code, but there's 
probably a middle ground.
   I'm pushing a few options as incremental fixup commits:
    - A `DeferredDataFrame`, which captures the serialized data output of an 
element as a function
    - An alternative `DeferredDataFrame`, which captures the element and a 
`DefferedDataWriter` instance, with the `ProtobufFrameSerializer` extending that
    
   I'm getting some mixed benchmark results on these, so need to double check 
the results a bit.
   Of the options, I think I prefer the function based `DeferredDataFrame`.
   
   The other part I didn't really like is introducing an asymmetry `Frame` in 
that some of the frames are outbound only now - I've addressed that by 
introducing `InboundFrame` and `OutboundFrame` partitions of the `Frame` sealed 
trait.
   I have the `InboundFrame` concept in my client improvements branch as well, 
as it helps clean up the logic there.
   
   Remaining tasks:
    - agree on approach for the deferred element write and 
`ProtobufFrameSerializer` shape and squash fixup commits
    - MiMa updates
    - review `@inline` annotations for benefit
    - review test coverage
    - run full benchmarks with stabilised approach and confirm
    - possibly redo PR, or at least rewrite description with clarified 
motivation/results
    - possibly rebase on main if #863 gets merged first


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to