Skip to content

Commit

Permalink
Fix update out of sequence
Browse files Browse the repository at this point in the history
A simple but old error has recently become evident. Due to the fact that
we read an object and then write it back across the boundaries of a
transaction, it is possible for the task object to have changed in
between transactions. This would cause the attempt to write out the old
task to suffer an "Update out of sequence" error.

This fix simply reads the latest version of the task back out within the
boundary of a transaction to avoid the race.

Signed-off-by: Drew Erny <drew.erny@docker.com>
(cherry picked from commit d68ac46)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
  • Loading branch information
dperny authored and thaJeztah committed Oct 1, 2019
1 parent 177caa5 commit bfce89e
Showing 1 changed file with 9 additions and 4 deletions.
13 changes: 9 additions & 4 deletions manager/orchestrator/service.go
Expand Up @@ -47,22 +47,27 @@ func SetServiceTasksRemove(ctx context.Context, s *store.MemoryStore, service *a
err = s.Batch(func(batch *store.Batch) error {
for _, t := range tasks {
err := batch.Update(func(tx store.Tx) error {
// the task may have changed for some reason in the meantime
// since we read it out, so we need to get from the store again
// within the boundaries of a transaction
latestTask := store.GetTask(tx, t.ID)

// time travel is not allowed. if the current desired state is
// above the one we're trying to go to we can't go backwards.
// we have nothing to do and we should skip to the next task
if t.DesiredState > api.TaskStateRemove {
if latestTask.DesiredState > api.TaskStateRemove {
// log a warning, though. we shouln't be trying to rewrite
// a state to an earlier state
log.G(ctx).Warnf(
"cannot update task %v in desired state %v to an earlier desired state %v",
t.ID, t.DesiredState, api.TaskStateRemove,
latestTask.ID, latestTask.DesiredState, api.TaskStateRemove,
)
return nil
}
// update desired state to REMOVE
t.DesiredState = api.TaskStateRemove
latestTask.DesiredState = api.TaskStateRemove

if err := store.UpdateTask(tx, t); err != nil {
if err := store.UpdateTask(tx, latestTask); err != nil {
log.G(ctx).WithError(err).Errorf("failed transaction: update task desired state to REMOVE")
}
return nil
Expand Down

0 comments on commit bfce89e

Please sign in to comment.