Skip to content

Commit f5a5a2d

Browse files
authored
feat: allow disabling global distributed locker per job (#811)
* chore: fix distributed locker tests * feat: allow disabling global dist locker per job
1 parent bf75107 commit f5a5a2d

File tree

4 files changed

+81
-19
lines changed

4 files changed

+81
-19
lines changed

executor.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ func (e *executor) runJob(j internalJob, jIn jobIn) {
370370
e.incrementJobCounter(j, Skip)
371371
return
372372
}
373-
} else if j.locker != nil {
373+
} else if !j.disabledLocker && j.locker != nil {
374374
lock, err := j.locker.Lock(j.ctx, j.name)
375375
if err != nil {
376376
_ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err)
@@ -379,7 +379,7 @@ func (e *executor) runJob(j internalJob, jIn jobIn) {
379379
return
380380
}
381381
defer func() { _ = lock.Unlock(j.ctx) }()
382-
} else if e.locker != nil {
382+
} else if !j.disabledLocker && e.locker != nil {
383383
lock, err := e.locker.Lock(j.ctx, j.name)
384384
if err != nil {
385385
_ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err)

job.go

+11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type internalJob struct {
4545
afterJobRunsWithError func(jobID uuid.UUID, jobName string, err error)
4646
afterJobRunsWithPanic func(jobID uuid.UUID, jobName string, recoverData any)
4747
afterLockError func(jobID uuid.UUID, jobName string, err error)
48+
disabledLocker bool
4849

4950
locker Locker
5051
}
@@ -556,6 +557,16 @@ func WithDistributedJobLocker(locker Locker) JobOption {
556557
}
557558
}
558559

560+
// WithDisabledDistributedJobLocker disables the distributed job locker.
561+
// This is useful when a global distributed locker has been set on the scheduler
562+
// level using WithDistributedLocker and need to be disabled for specific jobs.
563+
func WithDisabledDistributedJobLocker(disabled bool) JobOption {
564+
return func(j *internalJob, _ time.Time) error {
565+
j.disabledLocker = disabled
566+
return nil
567+
}
568+
}
569+
559570
// WithEventListeners sets the event listeners that should be
560571
// run for the job.
561572
func WithEventListeners(eventListeners ...EventListener) JobOption {

scheduler.go

+2
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,8 @@ func WithDistributedElector(elector Elector) SchedulerOption {
818818
// WithDistributedLocker sets the locker to be used by multiple
819819
// Scheduler instances to ensure that only one instance of each
820820
// job is run.
821+
// To disable this global locker for specific jobs, see
822+
// WithDisabledDistributedJobLocker.
821823
func WithDistributedLocker(locker Locker) SchedulerOption {
822824
return func(s *scheduler) error {
823825
if locker == nil {

scheduler_test.go

+66-17
Original file line numberDiff line numberDiff line change
@@ -1452,13 +1452,15 @@ func TestScheduler_WithDistributed(t *testing.T) {
14521452
tests := []struct {
14531453
name string
14541454
count int
1455+
runCount int
14551456
schedulerOpts []SchedulerOption
14561457
jobOpts []JobOption
14571458
assertions func(*testing.T)
14581459
}{
14591460
{
14601461
"3 schedulers with elector",
14611462
3,
1463+
1,
14621464
[]SchedulerOption{
14631465
WithDistributedElector(&testElector{notLeader: notLeader}),
14641466
},
@@ -1482,6 +1484,7 @@ func TestScheduler_WithDistributed(t *testing.T) {
14821484
{
14831485
"3 schedulers with locker",
14841486
3,
1487+
1,
14851488
[]SchedulerOption{
14861489
WithDistributedLocker(&testLocker{notLocked: notLocked}),
14871490
},
@@ -1499,11 +1502,14 @@ func TestScheduler_WithDistributed(t *testing.T) {
14991502
default:
15001503
}
15011504
}
1505+
1506+
assert.Equal(t, 2, notLockedCount)
15021507
},
15031508
},
15041509
{
15051510
"3 schedulers and job with Distributed locker",
15061511
3,
1512+
1,
15071513
nil,
15081514
[]JobOption{
15091515
WithDistributedJobLocker(&testLocker{notLocked: notLocked}),
@@ -1521,6 +1527,35 @@ func TestScheduler_WithDistributed(t *testing.T) {
15211527
default:
15221528
}
15231529
}
1530+
1531+
assert.Equal(t, 2, notLockedCount)
1532+
},
1533+
},
1534+
{
1535+
"3 schedulers and job with disabled Distributed locker",
1536+
3,
1537+
3,
1538+
[]SchedulerOption{
1539+
WithDistributedLocker(&testLocker{notLocked: notLocked}),
1540+
},
1541+
[]JobOption{
1542+
WithDisabledDistributedJobLocker(true),
1543+
},
1544+
func(_ *testing.T) {
1545+
timeout := time.Now().Add(1 * time.Second)
1546+
var notLockedCount int
1547+
for {
1548+
if time.Now().After(timeout) {
1549+
break
1550+
}
1551+
select {
1552+
case <-notLocked:
1553+
notLockedCount++
1554+
default:
1555+
}
1556+
}
1557+
1558+
assert.Equal(t, 0, notLockedCount)
15241559
},
15251560
},
15261561
}
@@ -1531,6 +1566,11 @@ func TestScheduler_WithDistributed(t *testing.T) {
15311566
ctx, cancel := context.WithCancel(context.Background())
15321567
schedulersDone := make(chan struct{}, tt.count)
15331568

1569+
var (
1570+
runCount int
1571+
doneCount int
1572+
)
1573+
15341574
for i := tt.count; i > 0; i-- {
15351575
s := newTestScheduler(t,
15361576
tt.schedulerOpts...,
@@ -1539,6 +1579,7 @@ func TestScheduler_WithDistributed(t *testing.T) {
15391579
WithStartAt(
15401580
WithStartImmediately(),
15411581
),
1582+
WithLimitedRuns(1),
15421583
}
15431584
jobOpts = append(jobOpts, tt.jobOpts...)
15441585

@@ -1565,31 +1606,39 @@ func TestScheduler_WithDistributed(t *testing.T) {
15651606
}()
15661607
}
15671608

1568-
var runCount int
1569-
select {
1570-
case <-jobsRan:
1571-
cancel()
1572-
runCount++
1573-
case <-time.After(time.Second):
1574-
cancel()
1575-
t.Error("timed out waiting for job to run")
1609+
RunCountLoop:
1610+
for {
1611+
select {
1612+
case <-jobsRan:
1613+
runCount++
1614+
if runCount >= tt.runCount {
1615+
break RunCountLoop
1616+
}
1617+
case <-time.After(time.Second):
1618+
t.Error("timed out waiting for job to run")
1619+
break RunCountLoop
1620+
}
15761621
}
15771622

1578-
var doneCount int
1579-
timeout := time.Now().Add(3 * time.Second)
1580-
for doneCount < tt.count && time.Now().After(timeout) {
1623+
cancel()
1624+
assert.Equal(t, tt.runCount, runCount)
1625+
1626+
DoneCountLoop:
1627+
for {
15811628
select {
15821629
case <-schedulersDone:
15831630
doneCount++
1584-
default:
1631+
if doneCount >= tt.count {
1632+
break DoneCountLoop
1633+
}
1634+
case <-time.After(3 * time.Second):
1635+
t.Error("timed out waiting for schedulers to shutdown")
1636+
break DoneCountLoop
15851637
}
15861638
}
1587-
close(jobsRan)
1588-
for range jobsRan {
1589-
runCount++
1590-
}
15911639

1592-
assert.Equal(t, 1, runCount)
1640+
assert.Equal(t, tt.count, doneCount)
1641+
15931642
time.Sleep(time.Second)
15941643
tt.assertions(t)
15951644
})

0 commit comments

Comments
 (0)