Copilot commented on code in PR #3441:
URL: https://github.com/apache/dubbo-go/pull/3441#discussion_r3426355743


##########
config_center/nacos/listener.go:
##########
@@ -70,17 +68,35 @@ func (n *nacosDynamicConfiguration) addListener(key string, 
listener config_cent
                        return
                }
        }
-       _, cancel := context.WithCancel(context.Background())
        listenersMap := rawListenersMap.(*sync.Map)
-       listenersMap.Store(listener, cancel)
+       listenersMap.Store(listener, struct{}{})
 }
 
 func (n *nacosDynamicConfiguration) removeListener(key string, listener 
config_center.ConfigurationListener) {
        rawListenersMap, loaded := n.keyListeners.Load(key)
        if !loaded {
                logger.Errorf("[ConfigCenter][Nacos] key is not be listened, 
key=%s", key)
-       } else {
-               listenersMap := rawListenersMap.(*sync.Map)
-               listenersMap.Delete(listener)
+               return
+       }
+       listenersMap := rawListenersMap.(*sync.Map)
+       listenersMap.Delete(listener)
+
+       // If no listeners remain for this key, cancel the nacos config 
subscription
+       isEmpty := true
+       listenersMap.Range(func(_, _ any) bool {
+               isEmpty = false
+               return false
+       })
+       if isEmpty {
+               n.keyListeners.Delete(key)
+               if n.client != nil {
+                       err := 
n.client.Client().CancelListenConfig(vo.ConfigParam{
+                               DataId: key,
+                               Group:  
n.resolvedGroup(n.url.GetParam(constant.NacosGroupKey, 
constant2.DEFAULT_GROUP)),
+                       })

Review Comment:
   `listener.go` now references `vo`, `constant`, and `constant2`, but the 
shown import block only imports `sync`. As written, this hunk will not compile 
unless those packages are imported elsewhere in this file; add the required 
imports in this file’s import block (and run gofmt) so the new 
`CancelListenConfig` call builds.



##########
config_center/nacos/listener.go:
##########
@@ -70,17 +68,35 @@ func (n *nacosDynamicConfiguration) addListener(key string, 
listener config_cent
                        return
                }
        }
-       _, cancel := context.WithCancel(context.Background())
        listenersMap := rawListenersMap.(*sync.Map)
-       listenersMap.Store(listener, cancel)
+       listenersMap.Store(listener, struct{}{})
 }
 
 func (n *nacosDynamicConfiguration) removeListener(key string, listener 
config_center.ConfigurationListener) {
        rawListenersMap, loaded := n.keyListeners.Load(key)
        if !loaded {
                logger.Errorf("[ConfigCenter][Nacos] key is not be listened, 
key=%s", key)
-       } else {
-               listenersMap := rawListenersMap.(*sync.Map)
-               listenersMap.Delete(listener)
+               return
+       }
+       listenersMap := rawListenersMap.(*sync.Map)
+       listenersMap.Delete(listener)
+
+       // If no listeners remain for this key, cancel the nacos config 
subscription
+       isEmpty := true
+       listenersMap.Range(func(_, _ any) bool {
+               isEmpty = false
+               return false
+       })
+       if isEmpty {
+               n.keyListeners.Delete(key)
+               if n.client != nil {
+                       err := 
n.client.Client().CancelListenConfig(vo.ConfigParam{
+                               DataId: key,
+                               Group:  
n.resolvedGroup(n.url.GetParam(constant.NacosGroupKey, 
constant2.DEFAULT_GROUP)),
+                       })

Review Comment:
   `CancelListenConfig` requires the same `(DataId, Group)` pair that was used 
to register the listener. Passing `DataId: key` while deriving `Group` from URL 
defaults can be incorrect if `key` is a composite “listenKey” (common in Nacos 
integrations) or if the subscription group varies per key. To avoid failing to 
cancel (leaking subscriptions) or canceling the wrong subscription, store the 
actual `vo.ConfigParam` (or at least `dataId` and `group`) alongside `key` when 
registering, and use that exact pair when canceling.



##########
config_center/nacos/listener.go:
##########
@@ -18,7 +18,6 @@
 package nacos
 
 import (
-       "context"
        "sync"
 )

Review Comment:
   `listener.go` now references `vo`, `constant`, and `constant2`, but the 
shown import block only imports `sync`. As written, this hunk will not compile 
unless those packages are imported elsewhere in this file; add the required 
imports in this file’s import block (and run gofmt) so the new 
`CancelListenConfig` call builds.



##########
config_center/nacos/listener.go:
##########
@@ -70,17 +68,35 @@ func (n *nacosDynamicConfiguration) addListener(key string, 
listener config_cent
                        return
                }
        }
-       _, cancel := context.WithCancel(context.Background())
        listenersMap := rawListenersMap.(*sync.Map)
-       listenersMap.Store(listener, cancel)
+       listenersMap.Store(listener, struct{}{})
 }
 
 func (n *nacosDynamicConfiguration) removeListener(key string, listener 
config_center.ConfigurationListener) {
        rawListenersMap, loaded := n.keyListeners.Load(key)
        if !loaded {
                logger.Errorf("[ConfigCenter][Nacos] key is not be listened, 
key=%s", key)
-       } else {
-               listenersMap := rawListenersMap.(*sync.Map)
-               listenersMap.Delete(listener)
+               return
+       }
+       listenersMap := rawListenersMap.(*sync.Map)
+       listenersMap.Delete(listener)
+
+       // If no listeners remain for this key, cancel the nacos config 
subscription
+       isEmpty := true
+       listenersMap.Range(func(_, _ any) bool {
+               isEmpty = false
+               return false
+       })
+       if isEmpty {
+               n.keyListeners.Delete(key)
+               if n.client != nil {
+                       err := 
n.client.Client().CancelListenConfig(vo.ConfigParam{
+                               DataId: key,
+                               Group:  
n.resolvedGroup(n.url.GetParam(constant.NacosGroupKey, 
constant2.DEFAULT_GROUP)),
+                       })
+                       if err != nil {
+                               logger.Errorf("[ConfigCenter][Nacos] cancel 
listen config fail, key=%s, err=%v", key, err)
+                       }
+               }
        }

Review Comment:
   The “delete → range to check empty → delete key → cancel” sequence is not 
atomic. A concurrent `addListener` can add a listener after the emptiness check 
(or during it), yet this code may still delete `keyListeners[key]` and call 
`CancelListenConfig`, potentially dropping active listeners or canceling Nacos 
pushes while someone is re-subscribing. Consider introducing per-key 
synchronization (e.g., store a per-key struct containing a `sync.Mutex` + 
listener set/refcount, or guard add/remove with a shared lock) so that checking 
emptiness and canceling/unregistering happens atomically with respect to 
adds/removes.



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