-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Proposal: Provide feedback on push progress. #1262
Conversation
|
||
### Alternative considerations | ||
|
||
- display byte-completion (eg. `10MB/100MB`) |
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 like this. Some layers are tiny and others huge. You get the feeling of how much it will take when you know the entire size.
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.
What do you think about including time elapsed and/or a progress bar as well?
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.
Adding time sounds fine, but I think the process bar won't help much unless you are doing in-place ncurses-like line replacement. I think we can start simple.
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.
Don't we need to do in-place replacement anyway if we're showing byte completion?
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.
It'll be just like the percentage completion except rather than a percent, it will show the absolute byte counts.
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.
Elapsed time and throughput (average and instantaneous) are helpful when diagnosing sloooow connections.
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.
Let's take a poll:
Place your name after the option(s) you would like to vote for
- byte completion | votes: chanseokoh, TadCordle
- time elapsed | votes: chanseokoh, TadCordle
- progress bar | votes:
- throughput | votes: chanseokoh (average only)
|
|
|
One way is to do something our internal build tool does: start with known total bytes to process, and as we discover more bytes to process, re-scale the whole progress workload. For example, at first it might look "10MB/100MB", but as you start processing more layers, it may become "11MB/254MB", "12MB/318MB", and so on. |
|
Yea, a variable progress bar would work, though it would still mean that there would have to be synchronization of some running total across the tasks though, whereas currently we don't have any explicit shared mutable memory between the concurrent tasks. |
They all just send their progress over to the progress handler? And it does the math? They don't have to know about eachother. |
The tasks won't need to know about each other, but the progress handler would need to have synchronized memory to handle the concurrent mutations of its progress/total counters, which would lock the tasks against each other during progress updates. |
Oh I see, so the event manager isn't dispatching events on a single thread? A UI Thread, like swing? |
The event manager dispatches to a method call on the thread it is dispatched from, although that method could be implemented to execute logic on another thread if needed. However, we probably should avoid having another thread purely for progress monitoring since that could incur a significant cost in context switching for all the progress updates. |
For updating a global counter, I think From the Javadoc,
I think nowadays with most users using contemporary processors, it will work efficiently without blocking. Even if it blocks, it should be transient (which I don't think will actually happen in practice on modern machines). And given the very low update frequency (10-second interval or so?), the chance of contention seems extremely low. |
AFAIK, Gradle uses a separate dedicated thread (via |
There's also the issue that we would not be able to log anything else after the progress bar starts and before it finishes, so this would not be compatible with other messages like retrying with auth token. The original "Building..." messages would all be replaced too. |
Yeah, so I'm actually skeptical with the |
Yeah using #!/bin/bash
printf "progress [---- ]\r"
sleep 1
printf "some log statement here\n"
printf "progress [---- ]\r"
sleep 1
printf "some other log statment\n"
printf "progress [---- ]\r" |
I think I'm with @loosebazooka on this one, |
Updated with design towards the progress bar approach. See Proposal section. If looks good, I will start implementing the DAT-based progress events first. |
|
||
These issues can be resolved with a *decentralized allocation tree*. | ||
|
||
#### Decentralized allocation tree (DAT) |
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 read through it, and still not clear if this will work as intended, if what I understood is correct, which makes me think probably I'm missing something.
So, what I can only think of for the intention of a tree is that, basically, the root node corresponds to the top-level entity that kicks off multiple second-level async "steps". For example, if you look at BuildSteps.forBuildToDockerRegistry
, it spawns 12 steps:
public static BuildSteps forBuildToDockerRegistry(BuildConfiguration buildConfiguration) {
return new BuildSteps(
DESCRIPTION_FOR_DOCKER_REGISTRY,
buildConfiguration,
() ->
new StepsRunner(buildConfiguration)
.runRetrieveTargetRegistryCredentialsStep()
.runAuthenticatePushStep()
.runPullBaseImageStep()
.runPullAndCacheBaseImageLayersStep()
.runPushBaseImageLayersStep()
.runBuildAndCacheApplicationLayerSteps()
.runBuildImageStep()
.runPushContainerConfigurationStep()
.runPushApplicationLayersStep()
.runFinalizingPushStep()
.runPushImageStep()
.waitOnPushImageStep());
}
So you intend that you create a root node here with 12 allocation units, pass the root node to the 12 Step
instances, and each step will create a single child node (with some allocation units) soon, resulting in 13 nodes (1 root + 12 children) at some point? And each Step
further passe down its child node to methods and instances it own in the similar manner?
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.
Yes, that is how it is intended to work. The only thing that will need to known prior to an allocation node being created is the number of allocation units that node should have.
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.
Oh one thing to note is that the root node with 12 allocation units does not necessarily need to have 12 children - allocation units can be "occupied" by non-node-based updates (like received bytes in the download progress case).
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.
If so (passing down the root node to multiple async step instances), and if the root and the children are physically connected (I assume so, because otherwise, it isn't a tree at all), isn't it that you are sharing the tree (at least the root node) concurrently? It sounds like basically you are sharing a single progress state. I thought it'd be cool if anyone could just fire progress events in an isolated manner, not having to establish any connection in relation to others. With no communications with others, it may be impossible to avoid the "backward-moving" progress bar, but I think it's worth the sacrifice for simplicity.
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.
The node(s) are passed down, but they are all immutable with no progress state. The state is only kept at the receiving end (the progress monitor that receives the progress events). The DAT is essentially just a representation of what fraction of the total progress a progress event actually accounts for. This allows for progress events to be emitted asynchronously and in a decentralized manner, only to be collected by the progress monitor and summed into an overall current progress.
proposals/progress_output.md
Outdated
### Example | ||
|
||
``` | ||
Executing tasks: Pushing classes layer, pulling base image layer 50501d3b88f7, pushing dependencies layer, pushing base image layer 8b106a18283f |
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.
It may end up being difficult reading these log messages if we put them on one line/constantly update them; it seems like this layout would be great for serial operations, but when we have a bunch of steps executing at once, showing the log messages like this may not add a lot of value.
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.
Update: working on prototyping having these on multiple lines - it makes things look a lot clearer
|
||
## Proposal | ||
|
||
Display an overall progress bar along with the tasks currently being executed. |
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.
How will this work at different logging levels?
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 we can disable the progress bar for logging levels debug and higher since there will be many log messages to indicate progress anyways, but the progress bar itself should be implemented such that it does not interfere with the other log messages Jib emits.
A recording of the WIP POC: https://asciinema.org/a/H4BIcjEYsSa8Rtyy0ayZt01Xh There are still a few artifacts to clean up, most notably Gradle's own progress logs being left over. Also, the old log messages would be disabled since they don't add anything of value anymore. |
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.
The prototype looks good to me, I think you just need to update the proposal with your findings from prototyping.
Updated the example and made a note about the things that need to be handled for Gradle logging. |
Regards issues: #1251, #806
Project tracker at #1297
Preview at: https://github.com/GoogleContainerTools/jib/blob/proposal-progress-output/proposals/progress_output.md
Brief recording of the current state of the POC: https://asciinema.org/a/H4BIcjEYsSa8Rtyy0ayZt01Xh