mirror of
https://github.com/hibiken/asynq.git
synced 2024-12-27 00:02:19 +08:00
Add the scheduler option HeartbeatInterval (#956)
* Add the scheduler option HeartbeatInterval * Fix possible premature expiration of scheduler entries
This commit is contained in:
parent
580d69e88f
commit
4f00f52c1d
@ -1437,7 +1437,7 @@ func (r *RDB) ClearServerState(host string, pid int, serverID string) error {
|
|||||||
|
|
||||||
// KEYS[1] -> asynq:schedulers:{<schedulerID>}
|
// KEYS[1] -> asynq:schedulers:{<schedulerID>}
|
||||||
// ARGV[1] -> TTL in seconds
|
// ARGV[1] -> TTL in seconds
|
||||||
// ARGV[2:] -> schedler entries
|
// ARGV[2:] -> scheduler entries
|
||||||
var writeSchedulerEntriesCmd = redis.NewScript(`
|
var writeSchedulerEntriesCmd = redis.NewScript(`
|
||||||
redis.call("DEL", KEYS[1])
|
redis.call("DEL", KEYS[1])
|
||||||
for i = 2, #ARGV do
|
for i = 2, #ARGV do
|
||||||
@ -1468,10 +1468,10 @@ func (r *RDB) WriteSchedulerEntries(schedulerID string, entries []*base.Schedule
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ClearSchedulerEntries deletes scheduler entries data from redis.
|
// ClearSchedulerEntries deletes scheduler entries data from redis.
|
||||||
func (r *RDB) ClearSchedulerEntries(scheduelrID string) error {
|
func (r *RDB) ClearSchedulerEntries(schedulerID string) error {
|
||||||
var op errors.Op = "rdb.ClearSchedulerEntries"
|
var op errors.Op = "rdb.ClearSchedulerEntries"
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
key := base.SchedulerEntriesKey(scheduelrID)
|
key := base.SchedulerEntriesKey(schedulerID)
|
||||||
if err := r.client.ZRem(ctx, base.AllSchedulers, key).Err(); err != nil {
|
if err := r.client.ZRem(ctx, base.AllSchedulers, key).Err(); err != nil {
|
||||||
return errors.E(op, errors.Unknown, &errors.RedisCommandError{Command: "zrem", Err: err})
|
return errors.E(op, errors.Unknown, &errors.RedisCommandError{Command: "zrem", Err: err})
|
||||||
}
|
}
|
||||||
|
66
scheduler.go
66
scheduler.go
@ -26,16 +26,17 @@ type Scheduler struct {
|
|||||||
|
|
||||||
state *serverState
|
state *serverState
|
||||||
|
|
||||||
logger *log.Logger
|
heartbeatInterval time.Duration
|
||||||
client *Client
|
logger *log.Logger
|
||||||
rdb *rdb.RDB
|
client *Client
|
||||||
cron *cron.Cron
|
rdb *rdb.RDB
|
||||||
location *time.Location
|
cron *cron.Cron
|
||||||
done chan struct{}
|
location *time.Location
|
||||||
wg sync.WaitGroup
|
done chan struct{}
|
||||||
preEnqueueFunc func(task *Task, opts []Option)
|
wg sync.WaitGroup
|
||||||
postEnqueueFunc func(info *TaskInfo, err error)
|
preEnqueueFunc func(task *Task, opts []Option)
|
||||||
errHandler func(task *Task, opts []Option, err error)
|
postEnqueueFunc func(info *TaskInfo, err error)
|
||||||
|
errHandler func(task *Task, opts []Option, err error)
|
||||||
|
|
||||||
// guards idmap
|
// guards idmap
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@ -48,6 +49,8 @@ type Scheduler struct {
|
|||||||
sharedConnection bool
|
sharedConnection bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultHeartbeatInterval = 10 * time.Second
|
||||||
|
|
||||||
// NewScheduler returns a new Scheduler instance given the redis connection option.
|
// NewScheduler returns a new Scheduler instance given the redis connection option.
|
||||||
// The parameter opts is optional, defaults will be used if opts is set to nil
|
// The parameter opts is optional, defaults will be used if opts is set to nil
|
||||||
func NewScheduler(r RedisConnOpt, opts *SchedulerOpts) *Scheduler {
|
func NewScheduler(r RedisConnOpt, opts *SchedulerOpts) *Scheduler {
|
||||||
@ -68,6 +71,11 @@ func NewSchedulerFromRedisClient(c redis.UniversalClient, opts *SchedulerOpts) *
|
|||||||
opts = &SchedulerOpts{}
|
opts = &SchedulerOpts{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heartbeatInterval := opts.HeartbeatInterval
|
||||||
|
if heartbeatInterval <= 0 {
|
||||||
|
heartbeatInterval = defaultHeartbeatInterval
|
||||||
|
}
|
||||||
|
|
||||||
logger := log.NewLogger(opts.Logger)
|
logger := log.NewLogger(opts.Logger)
|
||||||
loglevel := opts.LogLevel
|
loglevel := opts.LogLevel
|
||||||
if loglevel == level_unspecified {
|
if loglevel == level_unspecified {
|
||||||
@ -81,18 +89,19 @@ func NewSchedulerFromRedisClient(c redis.UniversalClient, opts *SchedulerOpts) *
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Scheduler{
|
return &Scheduler{
|
||||||
id: generateSchedulerID(),
|
id: generateSchedulerID(),
|
||||||
state: &serverState{value: srvStateNew},
|
state: &serverState{value: srvStateNew},
|
||||||
logger: logger,
|
heartbeatInterval: heartbeatInterval,
|
||||||
client: NewClientFromRedisClient(c),
|
logger: logger,
|
||||||
rdb: rdb.NewRDB(c),
|
client: NewClientFromRedisClient(c),
|
||||||
cron: cron.New(cron.WithLocation(loc)),
|
rdb: rdb.NewRDB(c),
|
||||||
location: loc,
|
cron: cron.New(cron.WithLocation(loc)),
|
||||||
done: make(chan struct{}),
|
location: loc,
|
||||||
preEnqueueFunc: opts.PreEnqueueFunc,
|
done: make(chan struct{}),
|
||||||
postEnqueueFunc: opts.PostEnqueueFunc,
|
preEnqueueFunc: opts.PreEnqueueFunc,
|
||||||
errHandler: opts.EnqueueErrorHandler,
|
postEnqueueFunc: opts.PostEnqueueFunc,
|
||||||
idmap: make(map[string]cron.EntryID),
|
errHandler: opts.EnqueueErrorHandler,
|
||||||
|
idmap: make(map[string]cron.EntryID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +115,15 @@ func generateSchedulerID() string {
|
|||||||
|
|
||||||
// SchedulerOpts specifies scheduler options.
|
// SchedulerOpts specifies scheduler options.
|
||||||
type SchedulerOpts struct {
|
type SchedulerOpts struct {
|
||||||
|
// HeartbeatInterval specifies the interval between scheduler heartbeats.
|
||||||
|
//
|
||||||
|
// If unset, zero or a negative value, the interval is set to 10 second.
|
||||||
|
//
|
||||||
|
// Note: Setting this value too low may add significant load to redis.
|
||||||
|
//
|
||||||
|
// By default, HeartbeatInterval is set to 10 seconds.
|
||||||
|
HeartbeatInterval time.Duration
|
||||||
|
|
||||||
// Logger specifies the logger used by the scheduler instance.
|
// Logger specifies the logger used by the scheduler instance.
|
||||||
//
|
//
|
||||||
// If unset, the default logger is used.
|
// If unset, the default logger is used.
|
||||||
@ -284,7 +302,7 @@ func (s *Scheduler) Shutdown() {
|
|||||||
|
|
||||||
func (s *Scheduler) runHeartbeater() {
|
func (s *Scheduler) runHeartbeater() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(s.heartbeatInterval)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.done:
|
case <-s.done:
|
||||||
@ -317,7 +335,7 @@ func (s *Scheduler) beat() {
|
|||||||
entries = append(entries, e)
|
entries = append(entries, e)
|
||||||
}
|
}
|
||||||
s.logger.Debugf("Writing entries %v", entries)
|
s.logger.Debugf("Writing entries %v", entries)
|
||||||
if err := s.rdb.WriteSchedulerEntries(s.id, entries, 5*time.Second); err != nil {
|
if err := s.rdb.WriteSchedulerEntries(s.id, entries, s.heartbeatInterval*2); err != nil {
|
||||||
s.logger.Warnf("Scheduler could not write heartbeat data: %v", err)
|
s.logger.Warnf("Scheduler could not write heartbeat data: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user