Aias00 opened a new issue, #7708:
URL: https://github.com/apache/shenyu/issues/7708

   ### Current behavior
   
   `AiStatisticServerHttpResponse.writeAndFlushWith()` maps each inner 
publisher to `appendResponse()`. Each call allocates a new `BodyWriter` and, 
for gzip, a new decoder. The parser and fallback tail therefore have 
inner-publisher scope instead of response scope.
   
   The normal Netty streaming writer creates an inner publisher per received 
buffer (`body.map(Flux::just)`). Network buffers do not have to align with SSE 
lines or JSON fields. A valid usage event split between buffers can thus never 
be reassembled by the fallback.
   
   ### Locations / production path
   
   - 
[AiTokenLimiterPlugin.java:225–242](https://github.com/apache/shenyu/blob/66d7d954ca37d651a5ad916f988a8081157a7ab4/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java#L225)
   - 
[NettyClientMessageWriter.java:75–76](https://github.com/apache/shenyu/blob/66d7d954ca37d651a5ad916f988a8081157a7ab4/shenyu-plugin/shenyu-plugin-response/src/main/java/org/apache/shenyu/plugin/response/strategy/NettyClientMessageWriter.java#L75)
   
   ### Reproduction / validation
   
   Use an uncompressed `text/event-stream` response containing this single 
valid SSE event, split across two buffers at the indicated boundary:
   
   ```text
   data: {"usage":{"completion_ | tokens":12}}
   
   
   ```
   
   (The ` | ` is a boundary marker, not payload.) Deliver the same two buffers 
first via `writeWith(Flux.just(first, second))`, then via the production 
streaming shape `writeAndFlushWith(Flux.just(Mono.just(first), 
Mono.just(second)))`.
   
   A standalone Java probe compiled the current token-limiter plugin and 
decoder sources and used a real mock WebFlux response with a recording token 
consumer:
   
   ```text
   WRITE recorded=[12] SUM=12
   FLUSH recorded=[0, 0] SUM=0
   ```
   
   This proves a response-accounting regression dependent only on buffer/flush 
boundaries; Redis and a live AI provider were not needed. The bytes forwarded 
to the client still form a valid SSE event.
   
   ### Impact / priority
   
   **P2 / Medium; confirmed, high confidence.** Valid streaming responses can 
consume upstream tokens without recording the reported usage, making window 
counters inaccurate. This differs from the designed delay between request 
admission and later accounting.
   
   ### Suggested fix
   
   Own SSE framing, fallback tail and decoder state for the entire response. 
Preserve streaming flush behavior while carrying incomplete lines/events across 
inner publishers. Finalize accounting/decoder cleanup once per response rather 
than independently for each transport buffer; keep retained memory bounded.
   
   ### Related existing work
   
   #6515 concerns a gzip header split across buffers and its old 
header-skipping decoder. This reproduction is uncompressed and the defect is 
state reset between flush publishers. #6758 concerns bounded tail retention. 
#6801 concerns per-chunk regex overhead. #7702 concerns admission/reservation 
semantics, not missing known response usage. The ordinary `writeWith` path has 
a cross-buffer tail fallback and is explicitly the passing control here.
   
   
   ### Reviewed revision
   
   Apache ShenYu 2.7.2-SNAPSHOT, 
`master@66d7d954ca37d651a5ad916f988a8081157a7ab4`. Unit-test and CI issue 
categories excluded.
   


-- 
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]

Reply via email to