codeAnqiang-ma opened a new pull request, #3655:
URL: https://github.com/apache/dubbo-go/pull/3655

   ### Description
   Fixes #3654
   
   Over the Triple (non-gRPC) unary transport, a server-returned `CodeBizError` 
(17) reaches the client as `CodeUnknown` (2); the gRPC transport in the same 
package preserves it. Downstream, `failover.isBizError` stops recognizing 
business errors and retries them (default up to 3 attempts, so non-idempotent 
calls run repeatedly), and `metrics/rpc` counts them as unknown.
   
   **Root cause.** `tripleWireError.asError()` (`protocol_triple.go:694`) 
clamps any code outside `[minCode, maxCode]` = `[1,16]` to `CodeUnknown`. That 
clamp comes from connect-go, which only defines codes 1..16; `CodeBizError = 
17` is a dubbo-go extension and `maxCode` was never updated. `code.go` 
deliberately supports such codes — the `UnmarshalText` branch commented 
*"Ensure that non-canonical codes round-trip through MarshalText and 
UnmarshalText"* keeps out-of-range codes for exactly this reason. `asError()` 
then discards what `UnmarshalText` just preserved.
   
   **Fix.** Exempt `CodeBizError` from the clamp (one line), plus 
`TestBizErrorCodePreservedAcrossProtocols`: a real `httptest` server whose 
handler returns `NewError(CodeBizError, ...)`, asserting the client-visible 
code over both transports. Before the fix the `triple` subtest fails (`got: 
unknown, want: code_17`) while `grpc` passes; after, both pass.
   
   <details>
   <summary>Why not raise <code>maxCode</code> to <code>CodeBizError</code> 
instead</summary>
   
   That breaks the round-trip. The `UnmarshalText` fallback only keeps codes 
*outside* `[minCode, maxCode]`:
   
   ```go
   if err == nil && (code < uint64(minCode) || code > uint64(maxCode)) {
        *c = Code(code)
        return nil
   }
   ```
   
   With `maxCode = CodeBizError`, the string `code_17` no longer satisfies that 
condition and falls through to `invalid code %q`. Making it work needs a 
canonical string for `CodeBizError` in both `String()` and `UnmarshalText`, 
which changes the wire representation of the code. The clamp exemption keeps 
the existing wire format and touches one line.
   </details>
   
   <details>
   <summary>Test output (before / after)</summary>
   
   Before the fix — only `protocol_triple.go` reverted, test unchanged:
   
   ```
   $ go test ./protocol/triple/triple_protocol/ -run 
TestBizErrorCodePreservedAcrossProtocols -v
   === NAME  TestBizErrorCodePreservedAcrossProtocols/triple
       triple_ext_test.go:772:
           assertion:   assert.Equal
           got: unknown
           want:        code_17
   --- FAIL: TestBizErrorCodePreservedAcrossProtocols (0.00s)
       --- FAIL: TestBizErrorCodePreservedAcrossProtocols/triple (0.01s)
       --- PASS: TestBizErrorCodePreservedAcrossProtocols/grpc (0.01s)
   FAIL dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol    4.304s
   ```
   
   After:
   
   ```
   --- PASS: TestBizErrorCodePreservedAcrossProtocols (0.00s)
       --- PASS: TestBizErrorCodePreservedAcrossProtocols/triple (0.00s)
       --- PASS: TestBizErrorCodePreservedAcrossProtocols/grpc (0.02s)
   ok   dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol    0.970s
   ```
   
   Existing tests, including the packages that consume the code:
   
   ```
   $ go test ./protocol/triple/... ./cluster/cluster/failover/... 
./metrics/rpc/...
   ok   dubbo.apache.org/dubbo-go/v3/cluster/cluster/failover   1.675s
   ok   dubbo.apache.org/dubbo-go/v3/metrics/rpc        2.234s
   ok   dubbo.apache.org/dubbo-go/v3/protocol/triple    5.790s
   ok   dubbo.apache.org/dubbo-go/v3/protocol/triple/health     1.687s
   ok   dubbo.apache.org/dubbo-go/v3/protocol/triple/openapi    2.138s
   ok   dubbo.apache.org/dubbo-go/v3/protocol/triple/reflection 2.667s
   ok   dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol    18.123s
   ```
   
   `go vet ./protocol/triple/triple_protocol/...` and `gofmt -l` are both clean.
   
   Not run locally: the full `make test` and the integration suite (they need 
ZooKeeper / Nacos / etcd / Docker, which I don't have set up), and 
golangci-lint. Leaving those to CI.
   </details>
   
   ### Checklist
   - [x] I confirm the target branch is `develop`
   - [x] Code has passed local testing
   - [x] I have added tests that prove my fix is effective or that my feature 
works
   
   _Assisted-by: Cursor (Claude Opus 5). Reproduced, reviewed and tested 
locally by the author._
   


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