[PATCH v2] Fix race condition between task creation and removal#1154
[PATCH v2] Fix race condition between task creation and removal#1154jinuxstyle wants to merge 1 commit into
Conversation
This commit fixes a race condition between task creation and removal
which could causes orphan containers. It happens when a agent task is
handling creation event and is creating a container, a removal event
comes in and cancels the task's context, causing both the creation and
removal fail, but the container is still created by the docker daemon.
The removal fails because when the DELETE request arrives the docker
daemon, the container is still being created.
goroutine (creation) task (removal)
--------- ----
... ...
ctlr.Prepare(ctx)
r.adapter.createNetworks(ctx)
... case <-shutdown
r.adapter.create(ctx) cancel()
//failed due to context cancelled tm.ctlr.Remove(ctx)
//container still createdby daemon //failed due to "No such
//container" error because
//the container not created
//yet
...
//the container created by docker
//daemon but becomes orphan
This patch fixes it by delaying the cancel option until the ongoing
operation is finished if the task is in the preparing state, in
which state a container will be created for the task. This way,
the removal operation would never race with the creation operation.
Signed-off-by: Jin Xu <jinuxstyle@hotmail.com>
Current coverage is 55.20%@@ master #1154 diff @@
==========================================
Files 77 77
Lines 12079 12083 +4
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
- Hits 6678 6670 -8
- Misses 4491 4501 +10
- Partials 910 912 +2
|
|
LGTM |
|
/cc @aluzzardi @stevvooe |
| // is in preparing state. This avoid the race | ||
| // between Prepare and Remove operations, | ||
| // which could cause orphan containers. | ||
| if tm.task.Status.State == api.TaskStatePreparing { |
There was a problem hiding this comment.
This method should never be looking at task state to make decisions about what to do. State is only inspected or mutated in exec.Do.
There was a problem hiding this comment.
I agree it's better doing it in exec.Do so that it can go through the state machine. I thought about it but I didn't figure out how. It looks the shutdown is handled as an special event which can abort other ongoing activities for the same task.
|
Unfortunately, this fix will reintroduce bugs that this code prevents. Please do not merge this. The actual bug is that In general, no two methods on We may have to make |
|
Thanks for your review. @stevvooe
Based on my debugging, the Remove did fail and it's due to the latter reason as you pointed out. The docker daemon reported "No such container" error but it already received the creation request and was creating the container.
I agree. But currently, the shutdown is handled concurrently in a separate branch against other operations.
I couldn't understand why we can't wait for Prepare to exit except the preparing may takes some time if it's pulling a image. Could you explain what's your concern? By the way, the #1152 proposed a solution which fixes the race in the exec.Controller level. Could you please take a look if you think that's a potentially better solution?
As I observed, the Remove is called only once. Do you mean making Remove and Create operations concurrent safe?
I read the comment you mentioned and it looks like the reaper is a long term solution. But I don't know how to implement it. I would be interested in the reaper feature. |
BTW, I am curious about what bug do you mean which could be reintroduced by this change? |
|
To debug the issue, I added some prints in Prepare and Remove functions. Following swarmkit log: daemon log: |
We aren't implementing this now because it will hide these kinds of bugs.
This fix is too narrow and blocks the manager on a potentially unbounded operation. What if the container is in any other method? The race condition still exists. This needs to be handled by the
#1152 suffers from being too narrow, as well. And it adds state, which it doesn't need at all. Careful removal of the "does not exist" error and granular locking (and maybe a closed channel) is all that is needed to resolve this. Could you file a bug with the description of the problem? We need to be careful with a solution here. |
Based on current design, the removal operation is disruptive and will race not only with Prepare but also Start and potentially other methods. I think the design itself is not bad because it can remove the task without waiting too long by cancelling ongoing activities. So following the current design, we need to make sure it doesn't bring harmful results. As I noticed so far, only the race between remove and prepare will result in annoying result, orphan containers. Of course, we would need a more general approach if we have larger range of problems.
No problem. |
|
Filed issue #1159. |
|
Superseded by #1192. Closing it. |
This commit fixes a race condition between task creation and removal
which could causes orphan containers. It happens when a agent task is
handling creation event and is creating a container, a removal event
comes in and cancels the task's context, causing both the creation and
removal fail, but the container is still created by the docker daemon.
The removal fails because when the DELETE request arrives the docker
daemon, the container is still being created.
This patch fixes it by delaying the cancel option until the ongoing
operation is finished if the task is in the preparing state, in
which state a container will be created for the task. This way,
the removal operation would never race with the creation operation.
Signed-off-by: Jin Xu jinuxstyle@hotmail.com