Skip to content

Commit 938f64b

Browse files
elekjtolio
authored andcommitted
sync2: ignore cycle when inverval is -1 (including initial run)
This would make it possible to easily disable the chores in the Storj micro-services, even if there is no dedicated flag for this. Setting -1 interval for any flag would mean disabling the cycle all together. Change-Id: I531f0bef5e208787b7643018a257a076a3385e31
1 parent 0a22f62 commit 938f64b

File tree

2 files changed

+56
-10
lines changed

2 files changed

+56
-10
lines changed

sync2/cycle.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"sync/atomic"
1010
"time"
1111

12-
monkit "github.com/spacemonkeygo/monkit/v3"
12+
"github.com/spacemonkeygo/monkit/v3"
1313
"golang.org/x/sync/errgroup"
1414
)
1515

@@ -89,6 +89,9 @@ func (cycle *Cycle) Run(ctx context.Context, fn func(ctx context.Context) error)
8989
cycle.initialize()
9090
defer close(cycle.stopped)
9191

92+
if cycle.Disabled() {
93+
return nil
94+
}
9295
currentInterval := cycle.interval
9396
cycle.ticker = time.NewTicker(currentInterval)
9497
defer cycle.ticker.Stop()
@@ -199,29 +202,55 @@ func (cycle *Cycle) Stop() {
199202
}
200203

201204
// ChangeInterval allows to change the ticker interval after it has started.
205+
// interval=-1 (disabled loop) is not allowed to be changed (it will panic).
202206
func (cycle *Cycle) ChangeInterval(interval time.Duration) {
207+
if cycle.Disabled() {
208+
if interval != -1 {
209+
panic("Change interval on a disabled cycle is not supported")
210+
}
211+
return
212+
}
213+
if interval == -1 {
214+
panic("Cannot disable an already initialized cycle.")
215+
}
203216
cycle.sendControl(cycleChangeInterval{interval})
204217
}
205218

206219
// Pause pauses the cycle.
207220
func (cycle *Cycle) Pause() {
221+
if cycle.Disabled() {
222+
return
223+
}
208224
cycle.sendControl(cyclePause{})
209225
}
210226

211227
// Restart restarts the ticker from 0.
212228
func (cycle *Cycle) Restart() {
229+
if cycle.Disabled() {
230+
return
231+
}
213232
cycle.sendControl(cycleContinue{})
214233
}
215234

216-
// Trigger ensures that the loop is done at least once.
235+
// Trigger ensures that the loop is done at least once. Note that it will not run if
236+
// the cycle is disabled.
237+
// TODO: Trigger should probably run the cycle even if the cycle interval is disabled.
217238
// If it's currently running it waits for the previous to complete and then runs.
218239
func (cycle *Cycle) Trigger() {
240+
if cycle.Disabled() {
241+
return
242+
}
219243
cycle.sendControl(cycleTrigger{})
220244
}
221245

222246
// TriggerWait ensures that the loop is done at least once and waits for completion.
247+
// Note that it will not run if the cycle is disabled.
248+
// TODO: Trigger should probably run the cycle even if the cycle interval is disabled.
223249
// If it's currently running it waits for the previous to complete and then runs.
224250
func (cycle *Cycle) TriggerWait() {
251+
if cycle.Disabled() {
252+
return
253+
}
225254
done := make(chan struct{})
226255

227256
cycle.sendControl(cycleTrigger{done})
@@ -231,6 +260,11 @@ func (cycle *Cycle) TriggerWait() {
231260
}
232261
}
233262

263+
// Disabled means the cycle is not intended to run (interval is -1).
264+
func (cycle *Cycle) Disabled() bool {
265+
return cycle.interval == -1
266+
}
267+
234268
type cycleManualTriggerTag struct{}
235269

236270
func withManualTrigger(ctx context.Context) context.Context {

sync2/cycle_test.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"golang.org/x/sync/errgroup"
1616

1717
"storj.io/common/sync2"
18+
"storj.io/common/testcontext"
1819
)
1920

2021
func TestCycle_Basic(t *testing.T) {
@@ -134,20 +135,31 @@ func TestCycle_StopCancelled(t *testing.T) {
134135
require.True(t, errors.Is(err, context.Canceled))
135136
}
136137

137-
func TestCycle_Run_NoInterval(t *testing.T) {
138+
func TestCycle_Disable(t *testing.T) {
138139
t.Parallel()
139140

140-
cycle := &sync2.Cycle{}
141+
cycle := sync2.NewCycle(-1)
142+
cycle.ChangeInterval(-1)
141143
require.Panics(t,
142144
func() {
143-
err := cycle.Run(context.Background(), func(_ context.Context) error {
144-
return nil
145-
})
146-
147-
require.NoError(t, err)
145+
cycle.ChangeInterval(5 * time.Minute)
148146
},
149-
"Run without setting an interval should panic",
147+
"changing interval of a disabled cycle should panic",
150148
)
149+
150+
executed := false
151+
cycle = sync2.NewCycle(-1)
152+
err := cycle.Run(testcontext.New(t), func(ctx context.Context) error {
153+
executed = true
154+
return nil
155+
})
156+
require.NoError(t, err)
157+
require.False(t, executed)
158+
159+
// no op for disabled cycle
160+
cycle.Pause()
161+
cycle.Trigger()
162+
cycle.Restart()
151163
}
152164

153165
func TestCycle_Stop_NotStarted(t *testing.T) {

0 commit comments

Comments
 (0)