From b70215f92b34ec3139f762c39e484df5c3a21390 Mon Sep 17 00:00:00 2001 From: Shivaji Kharse Date: Wed, 7 Oct 2026 14:56:17 +0530 Subject: [PATCH] feat(zero): allow --rebalance_interval=0 to disable automatic tablet rebalancing A zero interval now disables the rebalancer entirely instead of failing startup; negative values remain a startup error. The loop returns before creating the ticker (time.Tick panics on non-positive durations) and logs that rebalancing is disabled. Manual /moveTablet is unaffected. --- dgraph/cmd/zero/run.go | 9 +++++---- dgraph/cmd/zero/tablet.go | 10 ++++++++-- dgraph/cmd/zero/tablet_test.go | 17 +++++++++++++++++ dgraph/cmd/zero/zero.go | 2 +- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/dgraph/cmd/zero/run.go b/dgraph/cmd/zero/run.go index f870120b94c..13f2ebb5065 100644 --- a/dgraph/cmd/zero/run.go +++ b/dgraph/cmd/zero/run.go @@ -83,7 +83,8 @@ instances to achieve high-availability. " The count includes the original shard.") flag.String("peer", "", "Address of another dgraphzero server.") flag.StringP("wal", "w", "zw", "Directory storing WAL.") - flag.Duration("rebalance_interval", 8*time.Minute, "Interval for trying a predicate move.") + flag.Duration("rebalance_interval", 8*time.Minute, "Interval for trying a predicate move. "+ + "Set to 0 to disable automatic tablet rebalancing.") flag.String("enterprise_license", "", "(deprecated) Path to the enterprise license file.") flag.String("cid", "", "Cluster ID") @@ -282,9 +283,9 @@ func run() { "WAL directory and Audit output cannot be the same ('%s').", opts.audit.Output) } - if opts.rebalanceInterval <= 0 { - log.Fatalf("ERROR: Rebalance interval must be greater than zero. Found: %d", - opts.rebalanceInterval) + if opts.rebalanceInterval < 0 { + log.Fatalf("ERROR: Rebalance interval must be zero (to disable automatic tablet "+ + "rebalancing) or positive. Found: %d", opts.rebalanceInterval) } addr := "localhost" diff --git a/dgraph/cmd/zero/tablet.go b/dgraph/cmd/zero/tablet.go index eb550aea9de..7796e286cdd 100644 --- a/dgraph/cmd/zero/tablet.go +++ b/dgraph/cmd/zero/tablet.go @@ -75,8 +75,14 @@ This would trigger G1 to get latest state. Wait for it. */ // TODO: Have a event log for everything. -func (s *Server) rebalanceTablets() { - ticker := time.Tick(opts.rebalanceInterval) +func (s *Server) rebalanceTablets(interval time.Duration) { + // A zero interval disables automatic rebalancing; it must not reach time.Tick, + // which panics on non-positive durations. + if interval <= 0 { + glog.Infof("Automatic tablet rebalancing is disabled (rebalance_interval=%v).", interval) + return + } + ticker := time.Tick(interval) for range ticker { predicate, srcGroup, dstGroup := s.chooseTablet() if len(predicate) == 0 { diff --git a/dgraph/cmd/zero/tablet_test.go b/dgraph/cmd/zero/tablet_test.go index d0cd2885592..f843f8cfaa4 100644 --- a/dgraph/cmd/zero/tablet_test.go +++ b/dgraph/cmd/zero/tablet_test.go @@ -66,3 +66,20 @@ func TestMoveBackoff(t *testing.T) { s.recordMoveResult(pred, time.Minute, nil) require.False(t, s.skipMove(pred)) } + +func TestRebalanceTabletsDisabled(t *testing.T) { + // A non-positive interval disables automatic rebalancing: the loop must return + // immediately instead of ticking (time.Tick would panic on these values). + for _, interval := range []time.Duration{0, -time.Minute} { + done := make(chan struct{}) + go func() { + (&Server{}).rebalanceTablets(interval) + close(done) + }() + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatalf("rebalanceTablets(%v) did not return; rebalancing not disabled", interval) + } + } +} diff --git a/dgraph/cmd/zero/zero.go b/dgraph/cmd/zero/zero.go index cb76d79c1fd..8977e36ec81 100644 --- a/dgraph/cmd/zero/zero.go +++ b/dgraph/cmd/zero/zero.go @@ -99,7 +99,7 @@ func (s *Server) Init() { opts.limiterConfig.RefillAfter, s.closer) } - go s.rebalanceTablets() + go s.rebalanceTablets(opts.rebalanceInterval) } func (s *Server) periodicallyPostTelemetry() {