bocharov commented on code in PR #400:
URL: 
https://github.com/apache/kvrocks-controller/pull/400#discussion_r3716660704


##########
store/cluster_shard.go:
##########
@@ -420,6 +420,12 @@ func (shard *Shard) ToSlotsString() (string, error) {
        }
 
        for i, node := range shard.Nodes {
+               // A blank host (":port") would emit a malformed line and 
register a phantom, unreachable node.
+               // Fail loudly here rather than push corruption to every 
kvrocks node.
+               host, port, err := net.SplitHostPort(node.Addr())
+               if err != nil || host == "" || port == "" {
+                       return "", fmt.Errorf("node %s has invalid address %q", 
node.ID(), node.Addr())
+               }

Review Comment:
   Done — added a shared `validateAddr(addr string) error` in 
`store/cluster_shard.go`, now used by both `Shard.ToSlotsString` and 
`ClusterStore.CheckNewNodes`.



##########
store/cluster_node.go:
##########
@@ -315,17 +330,52 @@ func (n *ClusterNode) GetClusterNodesString(ctx 
context.Context) (string, error)
        return strings.TrimRight(clusterNodesStr, "\n"), nil
 }
 
-func (n *ClusterNode) SyncClusterInfo(ctx context.Context, cluster *Cluster) 
error {
+// syncMaxRetries and syncRetryBaseDelay bound the per-node push retry. 
kvrocks nodes do not gossip,
+// so the controller is the sole topology source; a push dropped by a 
transient blip (node restarting,
+// network hiccup) is never re-tried by any other path until the next probe 
tick, leaving that node on a
+// stale/divergent view (apache/kvrocks-controller#395). A short bounded 
backoff closes that window.
+const (
+       syncMaxRetries     = 3
+       syncRetryBaseDelay = 150 * time.Millisecond
+)
+
+// SyncClusterInfo pushes the authoritative topology to this node via CLUSTERX 
SETNODEID + SETNODES.
+//
+// When force is true the SETNODES carries the `force` flag, which makes 
kvrocks apply the topology
+// unconditionally — bypassing its version gate (reject-if-lower, 
no-op-if-equal). This is what lets the
+// controller REPAIR a node that has drifted at an equal epoch (the divergence 
#395 describes): a normal
+// equal-version push is silently no-op'd by the server, so it can never fix a 
bad-but-same-version view.
+// Because SETNODES replaces the node's entire topology, a forced push also 
removes stale/phantom node
+// entries (e.g. empty-address ghosts) without the data-destroying CLUSTER 
RESET the alternative requires.
+// Safe because the controller is the single writer of topology; callers must 
not force a version older
+// than a node legitimately ahead of the store (the probe loop keeps the `node 
ahead` branch for that).
+func (n *ClusterNode) SyncClusterInfo(ctx context.Context, cluster *Cluster, 
force bool) error {

Review Comment:
   Done — replaced the `force bool` + package consts with a 
`SyncPolicy{MaxRetries, RetryDelay, Force}` struct. `SyncClusterInfo` now takes 
the policy; added `DefaultSyncPolicy()` and `ForceSyncPolicy()` helpers, and 
callers use the latter.



##########
controller/cluster.go:
##########
@@ -242,12 +243,38 @@ func (c *ClusterChecker) syncClusterToNodes(ctx 
context.Context) error {
        return nil
 }
 
+// topologyDiverged reports whether a probed node's applied topology disagrees 
with the desired one:
+// a wrong peer count or wrong slot coverage. It returns false when the node 
did not report these
+// fields (KnownNodes/SlotsOk < 0), so an older kvrocks that omits them falls 
back to epoch-only
+// reconcile instead of being force-pushed on every tick.
+func topologyDiverged(info *store.ClusterInfo, wantNodes, wantSlots int64) 
bool {
+       if info == nil || info.KnownNodes < 0 || info.SlotsOk < 0 {
+               return false
+       }
+       return info.KnownNodes != wantNodes || info.SlotsOk != wantSlots
+}
+

Review Comment:
   Done — moved it onto `store.ClusterInfo` as 
`(*ClusterInfo).TopologyDiverged(wantNodes, wantSlots)`; the reconcile loop now 
calls `info.TopologyDiverged(...)` and the test moved to the store package.



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