@@ -93,7 +93,8 @@ type Lifecycler struct {
93
93
flushTransferer FlushTransferer
94
94
KVStore kv.Client
95
95
96
- actorChan chan func ()
96
+ actorChan chan func ()
97
+ autojoinChan chan struct {}
97
98
98
99
// These values are initialised at startup, and never change
99
100
ID string
@@ -106,6 +107,9 @@ type Lifecycler struct {
106
107
flushOnShutdown * atomic.Bool
107
108
unregisterOnShutdown * atomic.Bool
108
109
110
+ // Whether to auto join on ring on startup.
111
+ autoJoinOnStartup bool
112
+
109
113
// We need to remember the ingester state, tokens and registered timestamp just in case the KV store
110
114
// goes away and comes back empty. The state changes during lifecycle of instance.
111
115
stateMtx sync.RWMutex
@@ -128,7 +132,14 @@ type Lifecycler struct {
128
132
}
129
133
130
134
// NewLifecycler creates new Lifecycler. It must be started via StartAsync.
131
- func NewLifecycler (cfg LifecyclerConfig , flushTransferer FlushTransferer , ringName , ringKey string , flushOnShutdown bool , logger log.Logger , reg prometheus.Registerer ) (* Lifecycler , error ) {
135
+ func NewLifecycler (
136
+ cfg LifecyclerConfig ,
137
+ flushTransferer FlushTransferer ,
138
+ ringName , ringKey string ,
139
+ autoJoinOnStartup , flushOnShutdown bool ,
140
+ logger log.Logger ,
141
+ reg prometheus.Registerer ,
142
+ ) (* Lifecycler , error ) {
132
143
addr , err := GetInstanceAddr (cfg .Addr , cfg .InfNames , logger )
133
144
if err != nil {
134
145
return nil , err
@@ -165,10 +176,12 @@ func NewLifecycler(cfg LifecyclerConfig, flushTransferer FlushTransferer, ringNa
165
176
ID : cfg .ID ,
166
177
RingName : ringName ,
167
178
RingKey : ringKey ,
179
+ autoJoinOnStartup : autoJoinOnStartup ,
168
180
flushOnShutdown : atomic .NewBool (flushOnShutdown ),
169
181
unregisterOnShutdown : atomic .NewBool (cfg .UnregisterOnShutdown ),
170
182
Zone : zone ,
171
183
actorChan : make (chan func ()),
184
+ autojoinChan : make (chan struct {}),
172
185
state : PENDING ,
173
186
lifecyclerMetrics : NewLifecyclerMetrics (ringName , reg ),
174
187
logger : logger ,
@@ -391,23 +404,38 @@ func (i *Lifecycler) ZonesCount() int {
391
404
return i .zonesCount
392
405
}
393
406
407
+ func (i * Lifecycler ) Join () {
408
+ i .autojoinChan <- struct {}{}
409
+ }
410
+
394
411
func (i * Lifecycler ) loop (ctx context.Context ) error {
412
+ joined := false
395
413
// First, see if we exist in the cluster, update our state to match if we do,
396
414
// and add ourselves (without tokens) if we don't.
397
415
if err := i .initRing (context .Background ()); err != nil {
398
416
return perrors .Wrapf (err , "failed to join the ring %s" , i .RingName )
399
417
}
400
418
401
419
// We do various period tasks
402
- autoJoinAfter := time .After ( i . cfg . JoinAfter )
420
+ var autoJoinAfter <- chan time.Time
403
421
var observeChan <- chan time.Time
404
422
423
+ if i .autoJoinOnStartup {
424
+ i .Join ()
425
+ }
426
+
405
427
heartbeatTickerStop , heartbeatTickerChan := newDisableableTicker (i .cfg .HeartbeatPeriod )
406
428
defer heartbeatTickerStop ()
407
429
408
430
for {
409
431
select {
432
+ case <- i .autojoinChan :
433
+ autoJoinAfter = time .After (i .cfg .JoinAfter )
410
434
case <- autoJoinAfter :
435
+ if joined {
436
+ continue
437
+ }
438
+ joined = true
411
439
level .Debug (i .logger ).Log ("msg" , "JoinAfter expired" , "ring" , i .RingName )
412
440
// Will only fire once, after auto join timeout. If we haven't entered "JOINING" state,
413
441
// then pick some tokens and enter ACTIVE state.
@@ -556,7 +584,7 @@ func (i *Lifecycler) initRing(ctx context.Context) error {
556
584
// We use the tokens from the file only if it does not exist in the ring yet.
557
585
if len (tokensFromFile ) > 0 {
558
586
level .Info (i .logger ).Log ("msg" , "adding tokens from file" , "num_tokens" , len (tokensFromFile ))
559
- if len (tokensFromFile ) >= i .cfg .NumTokens {
587
+ if len (tokensFromFile ) >= i .cfg .NumTokens && i . autoJoinOnStartup {
560
588
i .setState (ACTIVE )
561
589
}
562
590
ringDesc .AddIngester (i .ID , i .Addr , i .Zone , tokensFromFile , i .GetState (), registeredAt )
@@ -587,9 +615,14 @@ func (i *Lifecycler) initRing(ctx context.Context) error {
587
615
588
616
// If the ingester failed to clean its ring entry up in can leave its state in LEAVING
589
617
// OR unregister_on_shutdown=false
590
- // Move it into ACTIVE to ensure the ingester joins the ring.
618
+ // if autoJoinOnStartup, move it into ACTIVE to ensure the ingester joins the ring.
619
+ // else set to PENDING
591
620
if instanceDesc .State == LEAVING && len (instanceDesc .Tokens ) == i .cfg .NumTokens {
592
- instanceDesc .State = ACTIVE
621
+ if i .autoJoinOnStartup {
622
+ instanceDesc .State = ACTIVE
623
+ } else {
624
+ instanceDesc .State = PENDING
625
+ }
593
626
}
594
627
595
628
// We exist in the ring, so assume the ring is right and copy out tokens & state out of there.
@@ -699,9 +732,6 @@ func (i *Lifecycler) autoJoin(ctx context.Context, targetState InstanceState) er
699
732
700
733
// At this point, we should not have any tokens, and we should be in PENDING state.
701
734
myTokens , takenTokens := ringDesc .TokensFor (i .ID )
702
- if len (myTokens ) > 0 {
703
- level .Error (i .logger ).Log ("msg" , "tokens already exist for this instance - wasn't expecting any!" , "num_tokens" , len (myTokens ), "ring" , i .RingName )
704
- }
705
735
706
736
newTokens := GenerateTokens (i .cfg .NumTokens - len (myTokens ), takenTokens )
707
737
i .setState (targetState )
0 commit comments