From dd5df613cd808909df7964b61bb5917ccff8b48c Mon Sep 17 00:00:00 2001 From: liuhy Date: Wed, 17 Jun 2026 14:44:33 +0800 Subject: [PATCH] fix(remoting): add sync.RWMutex protection for global codec map The global codec map in remoting/codec.go was accessed without any synchronization: RegistryCodec writes and GetCodec reads could race when called from multiple goroutines. While current production code only writes during init(), the public API allows runtime registration, which would cause a Go map concurrent read-write panic. Add sync.RWMutex to protect all map access: - RegistryCodec: Lock/Unlock for writes - GetCodec: RLock/RUnlock for reads This follows the same pattern used throughout the project (e.g., common/extension/registry_type.go, cluster/cluster/interceptor_invoker.go). Co-Authored-By: Claude --- remoting/codec.go | 10 +++++++++- remoting/codec_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/remoting/codec.go b/remoting/codec.go index 91126ebf24..e273a3d718 100644 --- a/remoting/codec.go +++ b/remoting/codec.go @@ -19,6 +19,7 @@ package remoting import ( "bytes" + "sync" ) // Codec is the interface that wrap EncodeRequest、 EncodeResponse and Decode method @@ -34,12 +35,19 @@ type DecodeResult struct { Result any } -var codec = make(map[string]Codec, 2) +var ( + codecMu sync.RWMutex + codec = make(map[string]Codec, 2) +) func RegistryCodec(protocol string, codecTmp Codec) { + codecMu.Lock() + defer codecMu.Unlock() codec[protocol] = codecTmp } func GetCodec(protocol string) Codec { + codecMu.RLock() + defer codecMu.RUnlock() return codec[protocol] } diff --git a/remoting/codec_test.go b/remoting/codec_test.go index ced9c6ca20..0fa14cb063 100644 --- a/remoting/codec_test.go +++ b/remoting/codec_test.go @@ -19,6 +19,7 @@ package remoting import ( "bytes" + "sync" "testing" ) @@ -101,3 +102,28 @@ func TestCodecInterface(t *testing.T) { assert.NotNil(t, result) assert.Equal(t, 9, length) } + +func TestConcurrentCodecAccess(t *testing.T) { + const goroutines = 100 + var wg sync.WaitGroup + wg.Add(goroutines * 2) + + // Concurrent writers + for i := 0; i < goroutines; i++ { + go func(i int) { + defer wg.Done() + RegistryCodec("concurrent-"+string(rune('a'+i%26)), &mockCodec{}) + }(i) + } + + // Concurrent readers + for i := 0; i < goroutines; i++ { + go func(i int) { + defer wg.Done() + GetCodec("concurrent-" + string(rune('a'+i%26))) + }(i) + } + + wg.Wait() + // If we reach here without a panic, the concurrent access is safe. +}