func gatherTasks(ds appwrap.Datastore, job JobInfo) ([]JobTask, error) { taskKeys := makeTaskKeys(ds, job.FirstTaskId, job.TaskCount) tasks := make([]JobTask, len(taskKeys)) i := 0 for i < len(taskKeys) { last := i + 100 if last > len(taskKeys) { last = len(taskKeys) } if err := ds.GetMulti(taskKeys[i:last], tasks[i:last]); err != nil { return nil, err } i = last } return tasks, nil }
// check if the specified job has completed. it should currently be at expectedStage, and if it's been completed // we advance it to next stage. if it's already at nextStage another process has beaten us to it so we're done // // caller needs to check the stage in the final job; if stageChanged is true it will be either nextStage or StageFailed. // If StageFailed then at least one of the underlying tasks failed and the reason will appear as a taskError{} in err func jobStageComplete(c context.Context, ds appwrap.Datastore, jobKey *datastore.Key, taskKeys []*datastore.Key, expectedStage, nextStage JobStage) (stageChanged bool, job JobInfo, finalErr error) { last := len(taskKeys) tasks := make([]JobTask, 100) for last > 0 { first := last - 100 if first < 0 { first = 0 } taskCount := last - first if err := ds.GetMulti(taskKeys[first:last], tasks[0:taskCount]); err != nil { finalErr = err return } else { for i := 0; i < taskCount; i++ { if tasks[i].Status == TaskStatusFailed { logInfo(c, "failed tasks found") nextStage = StageFailed last = -1 finalErr = taskError{tasks[i].Info} break } else if tasks[i].Status != TaskStatusDone { return } } if last >= 0 { last = first } } } // running this in a transaction ensures only one process advances the stage if transErr := runInTransaction(ds, func(ds appwrap.Datastore) error { job = JobInfo{} if err := ds.Get(jobKey, &job); err != nil { return err } if job.Stage != expectedStage { // we're not where we expected, so advancing this isn't our responsibility stageChanged = false return errMonitorJobConflict } job.Stage = nextStage job.UpdatedAt = time.Now() _, err := ds.Put(jobKey, &job) stageChanged = (err == nil) return err }); transErr != nil { finalErr = transErr } if finalErr != nil { logCritical(c, "taskComplete failed: %s", finalErr) } else { logInfo(c, "task is complete") } return }