fix(worker): fixed scheduling bugs

This commit is contained in:
bigeagle 2016-05-02 18:22:23 +08:00
parent 51fa12900d
commit 65984053eb
No known key found for this signature in database
GPG Key ID: 9171A4571C27920A
3 changed files with 40 additions and 7 deletions

View File

@ -12,6 +12,7 @@ import (
type scheduleQueue struct { type scheduleQueue struct {
sync.Mutex sync.Mutex
list *skiplist.SkipList list *skiplist.SkipList
jobs map[string]bool
} }
func timeLessThan(l, r interface{}) bool { func timeLessThan(l, r interface{}) bool {
@ -23,12 +24,18 @@ func timeLessThan(l, r interface{}) bool {
func newScheduleQueue() *scheduleQueue { func newScheduleQueue() *scheduleQueue {
queue := new(scheduleQueue) queue := new(scheduleQueue)
queue.list = skiplist.NewCustomMap(timeLessThan) queue.list = skiplist.NewCustomMap(timeLessThan)
queue.jobs = make(map[string]bool)
return queue return queue
} }
func (q *scheduleQueue) AddJob(schedTime time.Time, job *mirrorJob) { func (q *scheduleQueue) AddJob(schedTime time.Time, job *mirrorJob) {
q.Lock() q.Lock()
defer q.Unlock() defer q.Unlock()
if _, ok := q.jobs[job.Name()]; ok {
logger.Warningf("Job %s already scheduled, removing the existing one", job.Name())
q.unsafeRemove(job.Name())
}
q.jobs[job.Name()] = true
q.list.Set(schedTime, job) q.list.Set(schedTime, job)
logger.Debugf("Added job %s @ %v", job.Name(), schedTime) logger.Debugf("Added job %s @ %v", job.Name(), schedTime)
} }
@ -45,10 +52,11 @@ func (q *scheduleQueue) Pop() *mirrorJob {
defer first.Close() defer first.Close()
t := first.Key().(time.Time) t := first.Key().(time.Time)
// logger.Debug("First job should run @%v", t)
if t.Before(time.Now()) { if t.Before(time.Now()) {
job := first.Value().(*mirrorJob) job := first.Value().(*mirrorJob)
q.list.Delete(first.Key()) q.list.Delete(first.Key())
delete(q.jobs, job.Name())
logger.Debug("Popped out job %s @%v", job.Name(), t)
return job return job
} }
return nil return nil
@ -58,7 +66,11 @@ func (q *scheduleQueue) Pop() *mirrorJob {
func (q *scheduleQueue) Remove(name string) bool { func (q *scheduleQueue) Remove(name string) bool {
q.Lock() q.Lock()
defer q.Unlock() defer q.Unlock()
return q.unsafeRemove(name)
}
// remove job
func (q *scheduleQueue) unsafeRemove(name string) bool {
cur := q.list.Iterator() cur := q.list.Iterator()
defer cur.Close() defer cur.Close()
@ -66,6 +78,7 @@ func (q *scheduleQueue) Remove(name string) bool {
cj := cur.Value().(*mirrorJob) cj := cur.Value().(*mirrorJob)
if cj.Name() == name { if cj.Name() == name {
q.list.Delete(cur.Key()) q.list.Delete(cur.Key())
delete(q.jobs, name)
return true return true
} }
} }

View File

@ -30,6 +30,24 @@ func TestSchedule(t *testing.T) {
time.Sleep(1200 * time.Millisecond) time.Sleep(1200 * time.Millisecond)
So(schedule.Pop(), ShouldEqual, job) So(schedule.Pop(), ShouldEqual, job)
})
Convey("When adding one job twice", func() {
c := cmdConfig{
name: "schedule_test",
}
provider, _ := newCmdProvider(c)
job := newMirrorJob(provider)
sched := time.Now().Add(1 * time.Second)
schedule.AddJob(sched, job)
schedule.AddJob(sched.Add(1*time.Second), job)
So(schedule.Pop(), ShouldBeNil)
time.Sleep(1200 * time.Millisecond)
So(schedule.Pop(), ShouldBeNil)
time.Sleep(1200 * time.Millisecond)
So(schedule.Pop(), ShouldEqual, job)
}) })
Convey("When removing jobs", func() { Convey("When removing jobs", func() {
c := cmdConfig{ c := cmdConfig{

View File

@ -109,7 +109,7 @@ func (w *Worker) ReloadMirrorConfig(newMirrors []mirrorConfig) {
job.SetState(statePaused) job.SetState(statePaused)
go job.Run(w.managerChan, w.semaphore) go job.Run(w.managerChan, w.semaphore)
} else { } else {
job.SetState(stateReady) job.SetState(stateNone)
go job.Run(w.managerChan, w.semaphore) go job.Run(w.managerChan, w.semaphore)
w.schedule.AddJob(time.Now(), job) w.schedule.AddJob(time.Now(), job)
} }
@ -125,7 +125,7 @@ func (w *Worker) ReloadMirrorConfig(newMirrors []mirrorConfig) {
job := newMirrorJob(provider) job := newMirrorJob(provider)
w.jobs[provider.Name()] = job w.jobs[provider.Name()] = job
job.SetState(stateReady) job.SetState(stateNone)
go job.Run(w.managerChan, w.semaphore) go job.Run(w.managerChan, w.semaphore)
w.schedule.AddJob(time.Now(), job) w.schedule.AddJob(time.Now(), job)
logger.Noticef("New job %s", job.Name()) logger.Noticef("New job %s", job.Name())
@ -166,6 +166,9 @@ func (w *Worker) makeHTTPServer() {
} }
logger.Noticef("Received command: %v", cmd) logger.Noticef("Received command: %v", cmd)
// No matter what command, the existing job
// schedule should be flushed
w.schedule.Remove(job.Name())
// if job disabled, start them first // if job disabled, start them first
switch cmd.Cmd { switch cmd.Cmd {
case CmdStart, CmdRestart: case CmdStart, CmdRestart:
@ -181,14 +184,13 @@ func (w *Worker) makeHTTPServer() {
case CmdStop: case CmdStop:
// if job is disabled, no goroutine would be there // if job is disabled, no goroutine would be there
// receiving this signal // receiving this signal
w.schedule.Remove(job.Name())
if job.State() != stateDisabled { if job.State() != stateDisabled {
job.ctrlChan <- jobStop job.ctrlChan <- jobStop
} }
case CmdDisable: case CmdDisable:
w.disableJob(job) w.disableJob(job)
case CmdPing: case CmdPing:
job.ctrlChan <- jobStart // empty
default: default:
c.JSON(http.StatusNotAcceptable, gin.H{"msg": "Invalid Command"}) c.JSON(http.StatusNotAcceptable, gin.H{"msg": "Invalid Command"})
return return
@ -250,7 +252,7 @@ func (w *Worker) runSchedule() {
go job.Run(w.managerChan, w.semaphore) go job.Run(w.managerChan, w.semaphore)
continue continue
default: default:
job.SetState(stateReady) job.SetState(stateNone)
go job.Run(w.managerChan, w.semaphore) go job.Run(w.managerChan, w.semaphore)
stime := m.LastUpdate.Add(job.provider.Interval()) stime := m.LastUpdate.Add(job.provider.Interval())
logger.Debugf("Scheduling job %s @%s", job.Name(), stime.Format("2006-01-02 15:04:05")) logger.Debugf("Scheduling job %s @%s", job.Name(), stime.Format("2006-01-02 15:04:05"))
@ -263,7 +265,7 @@ func (w *Worker) runSchedule() {
// manager's mirror list // manager's mirror list
for name := range unset { for name := range unset {
job := w.jobs[name] job := w.jobs[name]
job.SetState(stateReady) job.SetState(stateNone)
go job.Run(w.managerChan, w.semaphore) go job.Run(w.managerChan, w.semaphore)
w.schedule.AddJob(time.Now(), job) w.schedule.AddJob(time.Now(), job)
} }