Belyenochi opened a new issue, #1231:
URL: https://github.com/apache/rocketmq-client-go/issues/1231

   ## BUG REPORT
   
   ### 1. Issue Description
   
   **What did you do (The steps to reproduce)?**
   
   Initialize multiple producers connecting to **different NameServer 
addresses** in the same process:
   ```go
   // First producer - connects to NameServer A
   p1, _ := rocketmq.NewProducer(
       
producer.WithNsResolver(primitive.NewPassthroughResolver([]string{"ns-a.example.com:9876"})),
       producer.WithTrace(&primitive.TraceConfig{
           TraceTopic: "trace-topic",
           NamesrvAddrs: []string{"ns-a.example.com:9876"},
       }),
   )
   
   // Second producer - connects to NameServer B (DIFFERENT from first)
   p2, _ := rocketmq.NewProducer(
       
producer.WithNsResolver(primitive.NewPassthroughResolver([]string{"ns-b.example.com:9876"})),
       producer.WithTrace(&primitive.TraceConfig{
           TraceTopic: "trace-topic",
           NamesrvAddrs: []string{"ns-b.example.com:9876"},
       }),
   )
   ```
   
   **What did you expect to see?**
   
   Either:
   - Both producers initialize successfully, OR
   - The second producer returns an error explaining the conflict
   
   **What did you see instead?**
   
   Panic on the second producer initialization:
   ```
   panic: runtime error: invalid memory address or nil pointer dereference
   goroutine 1 [running]:
   
github.com/apache/rocketmq-client-go/v2/internal.(*traceDispatcher).Start(0x0)
       internal/trace.go:306
   github.com/apache/rocketmq-client-go/v2/producer.newTraceInterceptor(...)
       producer/interceptor.go:47
   ```
   
   ### 2. Environment
   
   - **OS:** Linux (Ubuntu 22.04) / Also reproducible on macOS
   - **Go version:** 1.21+
   - **Client version:** v2.1.1 (latest)
   - **RocketMQ version:** N/A (issue is in Go client)
   
   ### 3. Root Cause Analysis
   
   The bug is in `producer/interceptor.go` lines 44-48:
   ```go
   func newTraceInterceptor(dispatcher internal.TraceDispatcher) 
primitive.Interceptor {
       if dispatcher != nil {  // ← This check is ineffective!
           dispatcher.Start()  // ← PANIC here
       }
       // ...
   }
   ```
   
   **The Problem: Go Interface Nil Trap**
   
   When `internal.NewTraceDispatcher()` fails (e.g., due to ClientID conflict 
in `GetOrNewRocketMQClient`), it returns a **nil pointer of concrete type** 
`*traceDispatcher`:
   ```go
   // trace.go line 238
   func NewTraceDispatcher(traceCfg *primitive.TraceConfig) *traceDispatcher {
       cli := GetOrNewRocketMQClient(cliOp, nil)
       if cli == nil {
           return nil  // Returns (*traceDispatcher)(nil), not interface nil!
       }
       // ...
   }
   ```
   
   When this nil pointer is assigned to the `TraceDispatcher` interface 
parameter:
   ```go
   dispatcher := internal.NewTraceDispatcher(traceCfg)  // type: 
TraceDispatcher interface
   // Internal representation: (type=*traceDispatcher, value=nil)
   ```
   
   The interface itself is **not nil** (because it has type information), so 
`dispatcher != nil` evaluates to `true`, but calling `dispatcher.Start()` 
dereferences a nil pointer → **PANIC**.
   ```
   ┌─────────────────────────────────────────┐
   │     Interface variable: dispatcher      │
   ├───────────────────┬─────────────────────┤
   │  type: *traceDispatcher  │  value: nil  │
   └───────────────────┴─────────────────────┘
            ↓
     dispatcher != nil  →  TRUE (type is not nil)
     dispatcher.Start() →  PANIC (value is nil)
   ```
   
   ### 4. Suggested Fix
   
   **Option A: Fix the nil check using reflection**
   ```go
   import "reflect"
   
   func newTraceInterceptor(dispatcher internal.TraceDispatcher) 
primitive.Interceptor {
       // Proper nil check for interface wrapping nil pointer
       if dispatcher != nil && !reflect.ValueOf(dispatcher).IsNil() {
           dispatcher.Start()
       }
       // ...
   }
   ```
   
   **Option B (Recommended): Return error from NewTraceDispatcher**
   ```go
   // trace.go
   func NewTraceDispatcher(traceCfg *primitive.TraceConfig) (*traceDispatcher, 
error) {
       cli := GetOrNewRocketMQClient(cliOp, nil)
       if cli == nil {
           return nil, errors.New("failed to create RocketMQ client for trace 
dispatcher")
       }
       // ...
       return &traceDispatcher{...}, nil
   }
   
   // interceptor.go
   func WithTrace(traceCfg *primitive.TraceConfig) Option {
       return func(options *producerOptions) {
           dispatcher, err := internal.NewTraceDispatcher(traceCfg)
           if err != nil {
               rlog.Warning("trace dispatcher creation failed", 
map[string]interface{}{
                   "error": err,
               })
               return
           }
           // ...
       }
   }
   ```
   
   **Option C: Return interface nil directly**
   ```go
   // trace.go - change return type to interface
   func NewTraceDispatcher(traceCfg *primitive.TraceConfig) TraceDispatcher {
       cli := GetOrNewRocketMQClient(cliOp, nil)
       if cli == nil {
           return nil  // Now returns true interface nil
       }
       return &traceDispatcher{...}
   }
   ```
   
   ### 5. Importance
   
   **Blocker** - This causes unrecoverable panics in production when:
   - Running multiple producers/consumers in microservices connecting to 
different MQ clusters
   - Multi-tenant applications with environment isolation (prod/test/dev)
   
   ### 6. Current Workaround
   
   Wrap producer creation with `recover()`:
   ```go
   func createProducerSafe(opts ...producer.Option) (p rocketmq.Producer, err 
error) {
       defer func() {
           if r := recover(); r != nil {
               err = fmt.Errorf("producer init panic: %v", r)
           }
       }()
       return rocketmq.NewProducer(opts...)
   }
   ```
   
   ---
   
   **Related Code References:**
   - `producer/interceptor.go:44-48` - Ineffective nil check
   - `internal/trace.go:234-239` - Returns nil pointer on failure
   - `internal/client.go:209-213` - GetOrNewRocketMQClient returns nil on 
NameServer conflict


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