RockteMQ-AI commented on code in PR #56:
URL: https://github.com/apache/rocketmq-operator/pull/56#discussion_r3909363540


##########
pkg/controller/broker/broker_controller.go:
##########
@@ -131,31 +131,43 @@ func (r *ReconcileBroker) Reconcile(request 
reconcile.Request) (reconcile.Result
                return reconcile.Result{}, err
        }
 
+       actualKey := broker.Namespace + "-" + broker.Spec.RocketMQName
+       actual, _ := share.GetInstance().LoadOrStore(actualKey, 
share.ShareItem{})
+       defer func() {
+               reqLogger.Info("Broker actualKey:" + actualKey + " 
actual.NameServerStr:" + actual.NameServersStr +
+                       " actual.BrokerClusterName:" + actual.BrokerClusterName 
+ " IsNameServersStrInitialized:" + 
strconv.FormatBool(actual.IsNameServersStrInitialized))
+               share.GetInstance().Store(actualKey, actual)
+       }()
+
+       var groupNum int
        if broker.Status.Size == 0 {
-               share.GroupNum = broker.Spec.Size
+               groupNum = broker.Spec.Size
        } else {
-               share.GroupNum = broker.Status.Size
+               groupNum = broker.Status.Size
        }
-
        if broker.Spec.NameServers == "" {
                // wait for name server ready when create broker cluster if 
nameServers is omitted
                for {
-                       if share.IsNameServersStrInitialized {
+                       actual, _ = share.GetInstance().Load(actualKey)
+                       if actual.IsNameServersStrInitialized {
                                break
                        } else {
-                               log.Info("Broker Waiting for name server 
ready...")
+                               log.Info("Broker Waiting for name server 
ready..., actualKey:" +
+                                       actualKey + " actual.NameServersStr:" + 
actual.NameServersStr + " IsNameServersStrInitialized:" +
+                                       
strconv.FormatBool(actual.IsNameServersStrInitialized))
                                
time.Sleep(time.Duration(cons.WaitForNameServerReadyInSecond) * time.Second)
                        }
                }
        } else {
-               share.NameServersStr = broker.Spec.NameServers
+               actual.NameServersStr = broker.Spec.NameServers

Review Comment:
   actual.NameServersStr is only assigned on the local copy fetched at the top 
of Reconcile; it is written back to the sync.Map in the deferred Store (line 
139), i.e. after getBrokerStatefulSet() has already run. getENV() (line 484) 
re-loads the item from the map, so on the first reconcile of a Broker with 
spec.nameServers set explicitly, the StatefulSet is created with an empty 
NAMESRV_ADDR env var and the brokers cannot start. The old global-variable code 
made the mutation immediately visible to getENV; the copy-in/copy-out semantics 
break that. Store the item immediately after mutating it, or pass the 
name-server string down into getBrokerStatefulSet/getENV as a parameter.



##########
pkg/controller/broker/broker_controller.go:
##########
@@ -131,31 +131,43 @@ func (r *ReconcileBroker) Reconcile(request 
reconcile.Request) (reconcile.Result
                return reconcile.Result{}, err
        }
 
+       actualKey := broker.Namespace + "-" + broker.Spec.RocketMQName
+       actual, _ := share.GetInstance().LoadOrStore(actualKey, 
share.ShareItem{})
+       defer func() {
+               reqLogger.Info("Broker actualKey:" + actualKey + " 
actual.NameServerStr:" + actual.NameServersStr +
+                       " actual.BrokerClusterName:" + actual.BrokerClusterName 
+ " IsNameServersStrInitialized:" + 
strconv.FormatBool(actual.IsNameServersStrInitialized))
+               share.GetInstance().Store(actualKey, actual)
+       }()
+
+       var groupNum int
        if broker.Status.Size == 0 {
-               share.GroupNum = broker.Spec.Size
+               groupNum = broker.Spec.Size
        } else {
-               share.GroupNum = broker.Status.Size
+               groupNum = broker.Status.Size
        }
-
        if broker.Spec.NameServers == "" {
                // wait for name server ready when create broker cluster if 
nameServers is omitted
                for {

Review Comment:
   This busy-wait loop blocks the reconcile worker indefinitely. With per-key 
state, a mismatch between broker.spec.rocketMQName and the NameService CR's 
rocketMQName (a plain string with no cross-CRD validation — a single typo) 
means IsNameServersStrInitialized is never set and this goroutine sleeps 
forever. With the default MaxConcurrentReconciles=1, one misconfigured cluster 
starves every broker CR in every other cluster, defeating the multi-cluster 
goal of this PR. Prefer returning reconcile.Result{RequeueAfter: ...} like the 
pod-not-ready handling already added further down in this function.



##########
deploy/crds/rocketmq_v1alpha1_broker_crd.yaml:
##########
@@ -93,6 +96,7 @@ spec:
           - volumes
           - volumeClaimTemplates
           - scalePodName
+          - rocketMQName

Review Comment:
   rocketMQName is added to `required` (also in the nameservice and 
topictransfer CRDs). Existing Broker/NameService/TopicTransfer CRs created by 
older operator versions lack this field, so after the CRD update any subsequent 
write to those objects (kubectl apply/edit, and potentially the operator's own 
status updates) fails OpenAPI validation — there is no migration path 
documented. Also note that CRs patched with an empty value silently collide on 
the key "<namespace>-". Consider making the field optional with a documented 
default, or document a mandatory pre-upgrade migration for all existing CRs.



##########
cmd/manager/main.go:
##########
@@ -100,7 +93,6 @@ func main() {
 
        // Create a new Cmd to provide shared dependencies and start components

Review Comment:
   Removing GetWatchNamespace()/the Namespace option silently ignores the 
WATCH_NAMESPACE env var that existing operator deployments set. After 
upgrading, the operator expands from namespaced to cluster-wide watching with 
no notice; deployments that deliberately scoped it for least privilege will 
break at startup because the caches now need list/watch across all namespaces 
(and old namespaced Role manifests no longer suffice). Keep honoring 
WATCH_NAMESPACE, or at minimum log a loud warning when it is set.



##########
deploy/clusterrole_binding.yaml:
##########
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: rocketmq-operator
+subjects:
+  - kind: ServiceAccount

Review Comment:
   The ClusterRoleBinding subject hardcodes namespace: default. If the operator 
is installed in any other namespace, its ServiceAccount never receives the 
ClusterRole and — since the manager now watches all namespaces — informer cache 
sync fails and the operator cannot start. The binding should be parameterized 
by the install namespace, and deploy/operator.yaml needs to be updated to 
reference this cluster-scoped RBAC (the diff does not touch it, so existing 
deployments still ship the namespaced Role and WATCH_NAMESPACE).



##########
pkg/controller/broker/broker_controller.go:
##########
@@ -131,31 +131,43 @@ func (r *ReconcileBroker) Reconcile(request 
reconcile.Request) (reconcile.Result
                return reconcile.Result{}, err
        }
 
+       actualKey := broker.Namespace + "-" + broker.Spec.RocketMQName
+       actual, _ := share.GetInstance().LoadOrStore(actualKey, 
share.ShareItem{})
+       defer func() {
+               reqLogger.Info("Broker actualKey:" + actualKey + " 
actual.NameServerStr:" + actual.NameServersStr +
+                       " actual.BrokerClusterName:" + actual.BrokerClusterName 
+ " IsNameServersStrInitialized:" + 
strconv.FormatBool(actual.IsNameServersStrInitialized))
+               share.GetInstance().Store(actualKey, actual)

Review Comment:
   ShareItem is copied out, mutated, and stored back as a whole by different 
controllers (broker, nameservice, topictransfer) with no per-key locking. 
Concurrent reconciles for the same key perform read-modify-write on independent 
struct copies, so the last Store wins and the other controller's changes are 
silently lost — e.g. a broker reconcile finishing here can revert a 
NameServersStr / IsNameServersStrUpdated value just written by the nameservice 
controller. sync.Map only protects the map itself, not lost updates on the 
values. Guard each key with a mutex, or have each controller store only the 
fields it owns.



##########
pkg/controller/broker/broker_controller.go:
##########
@@ -189,8 +201,8 @@ func (r *ReconcileBroker) Reconcile(request 
reconcile.Request) (reconcile.Result
        // Check for name server scaling
        if broker.Spec.AllowRestart {
                // The following code will restart all brokers to update 
NAMESRV_ADDR env
-               if share.IsNameServersStrUpdated {
-                       for brokerGroupIndex := 0; brokerGroupIndex < 
broker.Spec.Size; brokerGroupIndex++ {
+               if actual.IsNameServersStrUpdated {
+                               for brokerGroupIndex := 0; brokerGroupIndex < 
broker.Spec.Size; brokerGroupIndex++ {

Review Comment:
   The for loop under `if actual.IsNameServersStrUpdated` is indented one level 
too deep — a gofmt violation introduced by this change; please run 
gofmt/goimports on the touched files.



##########
pkg/share/share.go:
##########
@@ -18,19 +18,45 @@
 // Package share defines some variables shared by different packages
 package share
 
+import "sync"
+
 var (
-       // GroupNum is the number of broker group
-       GroupNum = 0
+       once sync.Once
+       instance *Singleton
+)
 
+type ShareItem struct {

Review Comment:
   No unit tests accompany the new share package or the reworked reconcile 
logic, although both are easily testable (LoadOrStore/Store round-trips, 
concurrent updates to one key, and the Reconcile path where spec.nameServers is 
set explicitly). A test asserting that a newly created broker StatefulSet 
carries the expected NAMESRV_ADDR would have caught the stale-copy bug in 
getENV.



##########
pkg/controller/nameservice/nameservice_controller.go:
##########
@@ -206,25 +222,25 @@ func (r *ReconcileNameService) 
updateNameServiceStatus(instance *rocketmqv1alpha
                }
 
                // use admin tool to update broker config
-               if share.IsNameServersStrUpdated && (len(oldNameServerListStr) 
> cons.MinIpListLength) && (len(share.NameServersStr) > cons.MinIpListLength) {
+               if actual.IsNameServersStrUpdated && (len(oldNameServerListStr) 
> cons.MinIpListLength) && (len(actual.NameServersStr) > cons.MinIpListLength) {
                        mqAdmin := cons.AdminToolDir
                        subCmd := cons.UpdateBrokerConfig
                        key := cons.ParamNameServiceAddress
 
-                       reqLogger.Info("share.GroupNum=broker.Spec.Size=" + 
strconv.Itoa(share.GroupNum))
+                       reqLogger.Info("share.GroupNum=broker.Spec.Size=" + 
strconv.Itoa(actual.GroupNum))
+
 
-                       clusterName := share.BrokerClusterName
+                       clusterName := actual.BrokerClusterName

Review Comment:
   clusterName (and GroupNum) come from in-memory cross-controller state that 
is only populated after a Broker reconcile for the same rocketMQName has run. 
After an operator restart the map is empty, so a name-server list change 
triggers the mqAdmin updateBrokerConfig command with `-c ""`, which fails and 
silently leaves brokers configured with stale name-server addresses. Consider 
looking the broker cluster name up from the Broker CR (label/owner lookup) 
instead of relying on shared memory. This weakness is inherited from the old 
globals, but the restart-loss scenario is worth fixing while this state is 
being reworked.



##########
pkg/apis/rocketmq/v1alpha1/topictransfer_types.go:
##########
@@ -37,6 +37,8 @@ type TopicTransferSpec struct {
        SourceCluster string `json:"sourceCluster,omitempty"`
        // The cluster where the topic will be transferred to
        TargetCluster string `json:"targetCluster,omitempty"`
+       //      // RocketMQ Name, the broker and nameserver in the same cluster 
must be filled with the same name

Review Comment:
   The comment is doubled (`// \t// RocketMQ Name...`) — leftover from editing. 
Similar "and and" duplications appear in the new README and example YAML 
comments; worth a quick copy pass.



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