-
Notifications
You must be signed in to change notification settings - Fork 645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid race condition when creating port bindings #1447
Avoid race condition when creating port bindings #1447
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1447 +/- ##
============================================
+ Coverage 59.07% 59.25% +0.17%
- Complexity 1989 2004 +15
============================================
Files 162 163 +1
Lines 9032 9047 +15
Branches 1365 1366 +1
============================================
+ Hits 5336 5361 +25
+ Misses 3205 3198 -7
+ Partials 491 488 -3
|
mappedPorts.updateProperties(container.getPortBindings()); | ||
} else { | ||
log.warn("Container %s is not running anymore, can not extract dynamic ports",containerId); | ||
for (int attempt = 0; attempt < 5; attempt++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, Do you think we should add Thread.currentThread().isInterrupted()
in this loop terminating condition here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good shout. Added
bb884bf
to
8f0f009
Compare
@@ -71,7 +72,9 @@ | |||
* @since 16/06/15 | |||
*/ | |||
public class RunService { | |||
|
|||
private static final int UPDATE_MAPPED_PORTS_RETRIES = 5; | |||
private static final int UPDATE_MAPPED_PORTS_RETRY_DELAY_MS = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would recommend to go here to 250 and increase the UPDATE_MAPPED_PORTS_RETRIES
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i checked the logs with this parameters is see only one try per container. i will test it with a lower time
[WARNING] DOCKER> Failed to get port bindings for container 4d2b4ff6615c (attempt 0 of 5)
[WARNING] DOCKER> Failed to get port bindings for container 35efb753ef46 (attempt 0 of 5)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private static final int UPDATE_MAPPED_PORTS_RETRIES = 50;
private static final int UPDATE_MAPPED_PORTS_RETRY_DELAY_MS = 50;
i still see only one attemt
[WARNING] DOCKER> Failed to get port bindings for container 8c64d1409bef (attempt 0 of 50)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private static final int UPDATE_MAPPED_PORTS_RETRIES = 500;
private static final int UPDATE_MAPPED_PORTS_RETRY_DELAY_MS = 5;
[WARNING] DOCKER> Failed to get port bindings for container 0fbb04f25568 (attempt 0 of 500)
[WARNING] DOCKER> Failed to get port bindings for container 0fbb04f25568 (attempt 1 of 500)
note that a have a pretty new PC with a lot of power.
so finally i would recommend 10ms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, i think the issue is it only prints out when it fails (also it is 0 indexed) so it may fail once and then pass on the 2nd attempt but will not print anything out. Will tweak this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having the delay of 250ms and 10 attempts is reasonable- it really should not make a huge difference to container start times to wait 250ms between attempts, especially as it failing to get the port data from docker is an edge case and race condition. As container start times are usually in the order of seconds or tens of seconds i think this is probably reasonable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would change this
private static final int UPDATE_MAPPED_PORTS_RETRY_DELAY_MS = 500;
to
private static final int UPDATE_MAPPED_PORTS_RETRY_DELAY_MS = 10;
and also adjust the UPDATE_MAPPED_PORTS_RETRIES to match again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not convinced that hammering the docker api every 10ms for (potentially) up to 500 attempts is the best idea. By setting the delay to 250ms it will wait long enough that the port information should be available after 1 retry. 250ms should not be problematic to add to container start times.
Happy to be told otherwise by the maintainers of this codebase but IMO 250ms and 10 attempts should be sufficient.
return; | ||
} catch (PortBindingException e) { | ||
try { | ||
log.warn("Failed to get port bindings for container %s (attempt %d of %d)", containerId, attempt, UPDATE_MAPPED_PORTS_RETRIES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would change this to debug, but add at the end a error log if this fails. or even better throw an exception at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think thats sensible. Have also added a debug log message when it succeeds too.
1835d7e
to
64dee37
Compare
I've updated this PR to use the failsafe library for the retries. I think it makes the code clearer than the loop with if conditions that I had. Hope the maintainers of this repo are OK with introducing this dependency. |
As a Polling Framework i recommend to use https://github.com/awaitility/awaitility her you can specify a initial delay and a polling interval. here an example
IMO: for me build time is a very precious thing. Alone in my project i start 8 Docker-Images. I think we should put some effort in keep this time as short as possible. as we have seen on my PC, after10ms the container is ready, so 240ms are lost. |
I have not used Awaitility before but from a cursory look it appears to be less versatile than Failsafe while achieving the same goal. The suggestion to use Awaitility seems like a personal preference rather than having identified any deficiency with using Failsafe. In particular Awaitility appears to have several drawbacks that, while it would be possible to use, in my mind make it less desirable in this instance.
Since you feel strongly enough about reducing the retry interval I am happy to do that unless there is any objection from the maintainers. @rohanKanojia it would be good to have some input from maintainers on this PR whether it looks ok. |
Actually come to think of this a potential compromise could be to use a backoff policy so that initially it retries very frequently but if the machine is slower it will backoff to a longer interval. Have added that as a separate commit. This way it will start waiting 10ms between retries but double its interval every attempt up to 100ms. |
a54e0f9
to
b9708be
Compare
Hello, I don't have any problem with keeping failsafe 👍 . I'll give it a review again tonight and approve/merge if everything is fine. |
Could you please update doc/changelog.md regarding this change? I think we can proceed with merge |
On container startup, sometimes the port information retrieved from the docker API is incomplete with port details being empty arrays, e.g. Ports:{"2525/tcp":[]}. This causes an IndexOutOfBoundsException to be thrown as the createPortBindings method assumes there will be at least one entry in the mapping. In these cases we should retry the API call to get the more complete port information. Signed-off-by: Ewen Cluley <ewen@brandwatch.com>
b9708be
to
4b7c1ef
Compare
Thank you, done. |
@rohanKanojia if you're happy with the changelog can you merge it? I don't have permission to merge I don't think. |
See #1446
Welcome feedback to my approach, to be frank I am not well versed with this code base and may have got some terminology wrong (especially in the unit test). If you can think of a better way to handle this I would love to hear it and happy to change the PR to implement.
On container startup, sometimes the port information retrieved from the
docker API is incomplete with port details being empty arrays, e.g.
Ports:{"2525/tcp":[]}. This causes an IndexOutOfBoundsException to be thrown
as the createPortBindings method assumes there will be at least one entry in
the mapping. In these cases we should retry the API call to get the more
complete port information.
Fixes #1446