@@ -50,7 +50,7 @@ const (
5050 rebStageFin
5151 rebStageFinStreams
5252 rebStageDone
53- rebStageAbort // one of targets aborts the rebalancing ( never set, only sent)
53+ rebStageAbort // one target aborts the rebalancing and notifies all (wire-only stage: never set, only sent)
5454)
5555
5656type (
@@ -140,7 +140,7 @@ const (
140140 preemptRetries = 16 // poll <= 16 seconds
141141)
142142
143- func (* Reb ) _preempt (logHdr , oxid string ) error {
143+ func (reb * Reb ) _preempt (logHdr , oxid string ) error {
144144 const (
145145 tag = "preempt"
146146 )
@@ -149,19 +149,35 @@ func (*Reb) _preempt(logHdr, oxid string) error {
149149 debug .AssertNoErr (err ) // (can only be invalid xid)
150150 return err
151151 }
152- if oxreb == nil || ! oxreb . EndTime (). IsZero () {
152+ if oxreb == nil {
153153 return nil
154154 }
155- oxreb .Abort (cmn .ErrXactRenewAbort )
156- nlog .Warningln (logHdr , "[" , cmn .ErrXactRenewAbort , oxreb .String (), "]" )
157155
158- // reb.fini() always performs full cleanup, that's why we can just poll here
159- // for valid end-time (not a sentinel indicating "almost done")
156+ if ! oxreb .EndTime ().IsZero () {
157+ xreb := reb .xctn ()
158+ if xreb == nil || xreb .ID () != oxreb .ID () {
159+ return nil
160+ }
161+ }
162+
163+ s := oxreb .String ()
164+ if oxreb .Abort (cmn .ErrXactRenewAbort ) {
165+ nlog .Warningln (logHdr , "[" , cmn .ErrXactRenewAbort , s , "]" )
166+ } else {
167+ nlog .Warningln (logHdr , "[ previously already aborted or finished:" , s , "]" )
168+ }
169+
170+ // reb.fini() always performs full cleanup, that's why we can poll here until:
171+ // - xreb.Finish() publishes valid end-time
172+ // - reb.setXact(nil) uninstalls the old local rebalance
160173 smap1 := core .T .Sowner ().Get ()
161174 for i := range preemptRetries {
162175 time .Sleep (time .Second )
163176 if ! oxreb .EndTime ().IsZero () {
164- return nil
177+ xreb := reb .xctn ()
178+ if xreb == nil || xreb .ID () != oxreb .ID () {
179+ return nil
180+ }
165181 }
166182
167183 smap2 := core .T .Sowner ().Get ()
@@ -173,7 +189,8 @@ func (*Reb) _preempt(logHdr, oxid string) error {
173189 nlog .Warningf ("%s: %s polling for %s ..." , tag , logHdr , oxreb .String ())
174190 }
175191 }
176- return fmt .Errorf ("%s: previous rebalance %q takes more than %v to finish" , tag , oxreb .String (), preemptRetries * time .Second )
192+
193+ return fmt .Errorf ("%s: previous rebalance %q takes more than %v to finish/cleanup" , tag , oxreb .String (), preemptRetries * time .Second )
177194}
178195
179196// Run() is the main method: serialized to execute one at a time (while possibly _preempting_
@@ -196,10 +213,9 @@ func (*Reb) _preempt(logHdr, oxid string) error {
196213//
197214// See also: README.md in this package.
198215func (reb * Reb ) Run (smap * meta.Smap , extArgs * ExtArgs ) {
199- if reb .rebID () == extArgs .NID {
216+ if reb .skip ( extArgs .NID , true /*allow equal*/ ) {
200217 return
201218 }
202-
203219 logHdr := reb .logHdr (extArgs .NID , smap , true /*initializing*/ )
204220 // preempt
205221 if xact .IsValidRebID (extArgs .Oxid ) {
@@ -234,7 +250,7 @@ func (reb *Reb) Run(smap *meta.Smap, extArgs *ExtArgs) {
234250 if ! rargs .pingall () {
235251 return
236252 }
237- if reb .rebID () == extArgs .NID {
253+ if reb .skip ( extArgs .NID , true /*allow equal*/ ) {
238254 return
239255 }
240256
@@ -256,7 +272,13 @@ func (reb *Reb) Run(smap *meta.Smap, extArgs *ExtArgs) {
256272 reb .stages .stage .Store (rebStageDone )
257273 fs .RemoveMarker (fname .RebalanceMarker , extArgs .Tstats , false /*stopping*/ )
258274 fs .RemoveMarker (fname .NodeRestartedPrev , extArgs .Tstats , false )
275+ reb .mu .Lock ()
259276 rargs .xreb .Finish ()
277+ if reb .xctn () == rargs .xreb {
278+ reb .setXact (nil ) // (compare w/ fini())
279+ reb .smap .Store (nil )
280+ }
281+ reb .mu .Unlock ()
260282 return
261283 }
262284
@@ -303,6 +325,7 @@ func (reb *Reb) Run(smap *meta.Smap, extArgs *ExtArgs) {
303325 } else {
304326 nlog .Errorln (logHdr , "fail => stage-fin:" , err )
305327 }
328+
306329 reb .changeStage (rargs , rebStageFin )
307330
308331 reb .fini (rargs , err , extArgs .Tstats )
@@ -317,6 +340,21 @@ func (reb *Reb) Run(smap *meta.Smap, extArgs *ExtArgs) {
317340 }
318341}
319342
343+ func (reb * Reb ) skip (nid int64 , allowEq bool ) bool {
344+ // before _renew() commits successfully, reb.rebID() still reflects the previous generation
345+ cur := reb .rebID ()
346+ switch {
347+ case cur < nid :
348+ return false
349+ case cur > nid :
350+ nlog .Warningf ("skip rebalance g[%d] - already superseded by newer g[%d]" , nid , cur )
351+ return true
352+ default :
353+ debug .Assert (allowEq ) // see rns.IsRunning() check
354+ return true
355+ }
356+ }
357+
320358// To optimize goroutine creation:
321359// 1. One bucket case just calls a single rebalance worker depending on
322360// whether a bucket is erasure coded (goroutine is not used).
@@ -353,14 +391,29 @@ func (reb *Reb) initRenew(rargs *rargs, extArgs *ExtArgs, ctlMsg func(sb *cos.SB
353391 if rns .Err != nil {
354392 return false
355393 }
356- if rns .IsRunning () {
394+ if rns .IsRunning () { // this rargs.id is already running, nothing to do
357395 return false
358396 }
359397 xctn := rns .Entry .Get ()
360398 xreb , ok := xctn .(* xs.Rebalance )
361399 debug .Assert (ok )
400+ if xreb .RebID () != rargs .id {
401+ err := fmt .Errorf ("reb-id mismatch: g[%d] != g[%d]" , xreb .RebID (), rargs .id )
402+ debug .AssertNoErr (err )
403+ return false
404+ }
362405
363406 reb .mu .Lock () // ----------------------------
407+
408+ if reb .skip (rargs .id , false /*allow equal*/ ) {
409+ reb .mu .Unlock ()
410+ if xreb .RebID () < reb .rebID () {
411+ xreb .Abort (cmn .ErrXactRenewAbort )
412+ xreb .Finish ()
413+ }
414+ return false
415+ }
416+
364417 origSmap , origStage := reb .smap .Load (), reb .stages .stage .Load ()
365418 err := reb ._renew (rargs , xreb , haveStreams )
366419
@@ -373,15 +426,9 @@ func (reb *Reb) initRenew(rargs *rargs, extArgs *ExtArgs, ctlMsg func(sb *cos.SB
373426 }
374427
375428 debug .Assert (reb .dm == nil )
376- if reb .dm != nil { // (defensive; remove later)
377- reb .dm .Close (err )
378- reb .dm .UnregRecv ()
379- reb .dm = nil
380- }
381- if rargs .xreb == xreb {
382- xreb .Abort (err )
383- reb .setXact (nil )
384- }
429+ debug .Assert (reb .xctn () != xreb )
430+ xreb .Abort (err )
431+ xreb .Finish ()
385432 reb .smap .Store (origSmap )
386433 reb .stages .stage .Store (origStage )
387434 reb .mu .Unlock () // fail ------
@@ -391,10 +438,6 @@ func (reb *Reb) initRenew(rargs *rargs, extArgs *ExtArgs, ctlMsg func(sb *cos.SB
391438}
392439
393440func (reb * Reb ) _renew (rargs * rargs , xreb * xs.Rebalance , haveStreams bool ) error {
394- if xreb .RebID () != rargs .id {
395- return fmt .Errorf ("reb-id mismatch: g[%d] != g[%d]" , xreb .RebID (), rargs .id )
396- }
397-
398441 rargs .xreb = xreb
399442
400443 // prior to opening streams:
0 commit comments