You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
version: every will skip versions if a parallel upstream job's latest build finishes before an older one #736
So this is a tricky one. The semantics for version: every coming from a job whose builds run in parallel are complicated by how the passed constraints work with version: every. See #358 (comment)
tl;dr: passed constraints allow it to skip versions, otherwise when a bad version enters the mix, all downstream jobs can no longer run.
The scheduling for the second job would have to know that a build is in-flight for the ideal next version, and know to wait for it to finish before determining the inputs.
Our team also makes heavy use of the pullrequest-resource, and reliably running all passed versions of a PR in downstream jobs is important. As I currently understand it, we need to set serial: true or max_in_flight: 1 to ensure all PRs make it through a pipeline?
@oppegard That's right. Until we figure out whether there's something better we could do. I'm a little wary of the changes this kind of implies; having scheduling so dependent on ephemeral job state feels wrong.
Have each job do a put to an otherwise-unused time resource. Then thread that through the subsequent jobs with trigger: true and a passed constraint. Do this for each job. This would make it so there's always a newer version once the build completes.
Is still an issue (and is the above workaround is still required) when using the pullrequest-resource, even though the mentioned problems with using passed and every together have been (I believe?) resolved by #358 (comment) ?
If so, could you please explain the details, and why the problem seems to be specific to pullrequest-resource?
My guess is that it is because the pullrequest-resource conflates two independent "versions" (PR numbers and SHAs) into a single "version", which breaks some of the concourse assumptions about how resources and versioning work. It would be great to have you confirm or deny that, and if so, possibly explain why from your perspective.
The problem is not specific to the PR resource. This issue is still open. The linked comment explains why this issue exists; it's a fundamental issue with passed and every together, where it's either broken this way or broken in the way that comment mentioned, and I think this way is the less broken of the two (as the alternative is a deadlocked pipeline).
EDIT I think understand now. Actually, I'm still confused. I thought when you said above "the second job", you meant a downstream job, but now I realize you probably meant a concurrently executing instance of the same job. So, I'm still trying to understand the crux of the problem, and why it only occurs with the combination of version: every, passed:, and non-serial or max-in-flight > 1 (and others on my team are also having trouble understanding it). So, it would be great if you could expand and clarify exactly what problem this causes, and why:
tl;dr: passed constraints allow it to skip versions, otherwise when a bad version enters the mix, all downstream jobs can no longer run.
The scheduling for the second job would have to know that a build is in-flight for the ideal next version, and know to wait for it to finish before determining the inputs.
END EDIT
But, with respect to the pullrequest-resource specifically:
The only reason we need to use "every" is because the pullrequest-resource conflates two independent "versions" (PR# and SHA) into one emitted "version". If we want the latest (not every) SHA to be built for every PR#.
If we only used the standard concourse git resource, which only emits one "version" (a SHA), in conjunction with a pipeline-per-pr# (i.e. pipeline-per-branch) approach as you discuss here, we would not need to use "every", because the separate pipeline-per-pr# (each with separate standard git resources defined for the PRs' branches) would ensure the latest SHA for the per-branch git resource would always be run (not every SHA, which is fine because we only care about running the latest commit on the PR's branch).
Thus, since we wouldn't need to use "every", we would not be exposed to this issue.
Correct?
If not, then please explain in more detail, because I'm still confused :)
It doesn't appear to work if you ever have more than one entry in a passed: array.
E.g., if you try this, job-3 hang and never start with the message "waiting for a suitable set of input versions\n dummy-time - no versions satisfy passed constraints"
Would ask that this issue and/or #1298 remain open as it's a common problem and the proposed fix (spaces) is a large and uncertain features (concourse/rfcs#24).
I'll leave this issue open as long as the issue still stands. The resolution will probably be deprecating/disallowing version: every + passed: in favor of spatial pipelines using the across step (concourse/rfcs#29). The problem with version: every + passed: is pretty fundamental as noted in #736 (comment), and supporting it adds a lot of complexity in our scheduling algorithm.
I'll keep the stale bot at bay by placing this in our Spatial Resources epic as a long-term goal to close out once the epic is complete.
We've been hitting this same issue. So, I've been trying to understand how the scheduling algorithm is currently implemented. I'd like to check my understanding, and see if it inspires any reasonable fixes.
tick() loads, among other things, a (possibly-cached) copy of the pipeline config, and a VersionDB containing the set of all resource versions, build inputs & outputs related to a pipeline.
This is passed to a atc/scheduler/Scheduler.Schedule()
Once we've found a version for this resource that IsNext(), we return it as a mapping, we collect all those mappings, ensure that we have a mapping for each input config, do a final group resolve to check compatibility, save the input mapping to the DB, and then determine whether we should trigger the build.
If my understanding is correct about this process, the cause of this bug is pretty straightforward - When
A) multiple versions of a resource exist, and
B) the newest version has a build that exists
then no other version of that resource is ever considered as an input to a potential job, regardless of version: every.
Given this, it seems like there might also be edge cases in which inputs that were version: every and didn't have a passed could also skip versions. I'd have to dig in to confirm or reproduce that, though.
A naiive suggestion might be, in cases where an input_config is version: every, to consider candidates in ascending order of check_order, skipping candidates that already have a build. I wouldn't be surprised if this is actually how it was implemented before, and that caused other subtle problems of "job gets stuck and never schedules any new builds". When i get some more time, I'll do some history spelunking.
@YenTheFirst Impressive sleuthing! We're actually near the end of a complete re-design and re-implementation of the scheduling algorithm, currently living on the algorithm-v3 and release/6.0.x branch. If you'd like to read a bit about it, I had a section on it in the v10 roadmap post. We're aiming to push it out as a 6.0 beta release sometime soon.
For inputs with version: every the new algorithm actually does exactly what you proposed: walk through versions in ascending order from whichever version was previously used, falling back on descending order if none of those are valid.
However, at least at some level, the problem with passed and version: every with parallel upstream jobs is still pretty fundamental. Both attributes demand two things that are naturally in conflict: "use every version" and "use only versions that passed through the job". "Correcting" the behavior with parallel upstream builds would likely mean changing the semantics of version: every to either a) wait for in-fight builds to complete before deciding which version to use or b) support walking "backwards" to execute older versions.
Neither of these options really feel compelling to me. Even version: everywithoutpassed constraints is actually really awkward to support, and I really want to replace it with something else in the future as it has been the root cause of a ton of complexity/confusion in both the old implementation and the new one.
I don't think the concepts of passed and version every are fundamentally at-odds, at least from a user's point of view. It's typically either "I want to use every version of a resource, as long as it passed an upstream job", or "I want to use every version of a resource, but wait until a sure-to-succeed upstream job has produced an accompanying artifact", or "I want to use every output of an upstream job (even if that's not every version of the resource)". It may make sense for those use-cases to get fulfilled by something other than version: every, but I do think those are use-cases that should be supported.
In our particular case, the pipeline is something like:
For every commit on every pr-branch, build a docker image
for every successfully built docker image, run unit tests
for every successfully built docker image, run integration tests
for every commit that was successfully built, run the linter
I suspect we could work around this by removing the initial "build a docker image", and instead having unit-tests, integration-tests do their own building, but that duplicates work and code.
[In this particular case, yes, we're using version: every as a hack around the fact that Concourse does not have spaces yet, but...Concourse doesn't have spaces yet. I suspect that there's reasonable variations on this pipeline where version: every isn't merely a hack]
I'll take a closer look at those algorithms-v3 and release/6.0.x branches. Where would be a reasonable place to leave feedback related to those branches, if applicable?
Based only on your description,
walk through versions in ascending order from whichever version was previously used
At first glance, this sounds like it would have the exact same problem. If a downstream build for D completes before an upstream build for C, the downstream build for C will never start, since "D" will be the version that was previously used.
b) support walking "backwards" to execute older versions.
I expect this is basically what's needed.
To be clear, I don't doubt the validity of your use case at all. I just don't see us improving this specific approach any time soon, because it will take a nontrivial amount of work, and it will just delay the real fix which we've recently gained a pretty clear picture of.
The problem is that, while version: every + passed may not seem to be at odds from a user-level, it's technically impossible to satisfy without significant changes to how Concourse works, which would mean other user-level changes. I just don't think these changes are the right thing to do.
b) support walking "backwards" to execute older versions.
I expect this is basically what's needed.
This is why I think it's a fundamental issue. Concourse pipelines go forwards, not backwards. Changing this would have a ton of implications, and it isn't a clean fix anyway because now there's a question of when does version: every stop going backwards. For example, if I apply version: every to a regular Git repo without passed constraints, it's not likely that I would want it to literally go all the way back to the initial commit (resources will eventually collect all versions, not just from initial configuration time).
We already know this feature is a pain in the butt to support, and doubling down on it to get it working correctly would require re-designing significant portions of the product around it, both internally and in the UI. So at this point I'm not on board with supporting version: every + passed, and I would like to even phase out version: every in the long run. It's a crutch that makes things "work" just enough for us to not find better solutions.
Your use case of building every commit of every PR is totally fine. I think a model that better fits how Concourse works would be to configure a pipeline for each commit. We just need to improve the automation and navigation of pipelines. This is all broken down into many small features, laid out in the v10 roadmap post. Implementing most of these features will be easier or at least more beneficial in the long-term than doubling down on version: every.
I'll take a closer look at those algorithms-v3 and release/6.0.x branches. Where would be a reasonable place to leave feedback related to those branches, if applicable?
Hmm good point, not yet. We should just open a PR for it. 🤔 I'll link to it here once we do (or just keep an eye out if I forget!)
Activity
joonas commentedon Nov 5, 2016
I'm also seeing this behavior on my end on the pullrequest-resource, currently running Concourse 2.4.0
vito commentedon Nov 7, 2016
So this is a tricky one. The semantics for
version: everycoming from a job whose builds run in parallel are complicated by how thepassedconstraints work withversion: every. See #358 (comment)tl;dr:
passedconstraints allow it to skip versions, otherwise when a bad version enters the mix, all downstream jobs can no longer run.The scheduling for the second job would have to know that a build is in-flight for the ideal next version, and know to wait for it to finish before determining the inputs.
oppegard commentedon Nov 11, 2016
Our team also makes heavy use of the pullrequest-resource, and reliably running all
passedversions of a PR in downstream jobs is important. As I currently understand it, we need to setserial: trueormax_in_flight: 1to ensure all PRs make it through a pipeline?vito commentedon Nov 14, 2016
@oppegard That's right. Until we figure out whether there's something better we could do. I'm a little wary of the changes this kind of implies; having scheduling so dependent on ephemeral job state feels wrong.
vito commentedon Dec 2, 2016
Possible workaround:
Have each job do a
putto an otherwise-unusedtimeresource. Then thread that through the subsequent jobs withtrigger: trueand apassedconstraint. Do this for each job. This would make it so there's always a newer version once the build completes.e.g.:
Note that
version: everyis configured on thedummy-timeresource in subsequent jobs.thewoolleyman commentedon Jan 19, 2017
Hi @vito,
Is still an issue (and is the above workaround is still required) when using the pullrequest-resource, even though the mentioned problems with using
passedandeverytogether have been (I believe?) resolved by #358 (comment) ?If so, could you please explain the details, and why the problem seems to be specific to pullrequest-resource?
My guess is that it is because the pullrequest-resource conflates two independent "versions" (PR numbers and SHAs) into a single "version", which breaks some of the concourse assumptions about how resources and versioning work. It would be great to have you confirm or deny that, and if so, possibly explain why from your perspective.
Thanks!
-- Chad
vito commentedon Jan 19, 2017
The problem is not specific to the PR resource. This issue is still open. The linked comment explains why this issue exists; it's a fundamental issue with
passedandeverytogether, where it's either broken this way or broken in the way that comment mentioned, and I think this way is the less broken of the two (as the alternative is a deadlocked pipeline).thewoolleyman commentedon Jan 19, 2017
@vito OK, thanks.
EDIT
I think understand now. Actually, I'm still confused. I thought when you said above "the second job", you meant a downstream job, but now I realize you probably meant a concurrently executing instance of the same job. So, I'm still trying to understand the crux of the problem, and why it only occurs with the combination ofversion: every,passed:, and non-serial or max-in-flight > 1 (and others on my team are also having trouble understanding it). So, it would be great if you could expand and clarify exactly what problem this causes, and why:END EDIT
But, with respect to the pullrequest-resource specifically:
Correct?
If not, then please explain in more detail, because I'm still confused :)
Thanks,
-- Chad
thewoolleyman commentedon Jan 20, 2017
@vito Also, I tried out the workaround above.
It doesn't appear to work if you ever have more than one entry in a
passed:array.E.g., if you try this,
job-3hang and never start with the message "waiting for a suitable set of input versions\n dummy-time - no versions satisfy passed constraints"Is this expected (and thus the workaround doesn't work in this situation), or do I have something wrong?
Thanks,
-- Chad
49 remaining items
phillbaker commentedon Jul 17, 2019
Would ask that this issue and/or #1298 remain open as it's a common problem and the proposed fix (spaces) is a large and uncertain features (concourse/rfcs#24).
vito commentedon Jul 30, 2019
I'll leave this issue open as long as the issue still stands. The resolution will probably be deprecating/disallowing
version: every+passed:in favor of spatial pipelines using theacrossstep (concourse/rfcs#29). The problem withversion: every+passed:is pretty fundamental as noted in #736 (comment), and supporting it adds a lot of complexity in our scheduling algorithm.I'll keep the stale bot at bay by placing this in our Spatial Resources epic as a long-term goal to close out once the epic is complete.
YenTheFirst commentedon Nov 2, 2019
We've been hitting this same issue. So, I've been trying to understand how the scheduling algorithm is currently implemented. I'd like to check my understanding, and see if it inspires any reasonable fixes.
My understanding is,
atc/scheduler/Runnerruns, and on a regular basis,tick()s. (https://github.com/concourse/concourse/blob/release/5.6.x/atc/scheduler/runner.go#L67)tick()loads, among other things, a (possibly-cached) copy of the pipeline config, and aVersionDBcontaining the set of all resource versions, build inputs & outputs related to a pipeline.atc/scheduler/Scheduler.Schedule()ensurePendingBuildExists. The purpose ofensurePendingBuildExistsseems to be to find-or-create the single next build that should start. (https://github.com/concourse/concourse/blob/release/5.6.x/atc/scheduler/scheduler.go#L62)ensurePendingBuildExistscallsatc/scheduler/InputMapper.SaveNextInputMapping().SaveNextInputMappingcalls, per-resource,atc/db/algorithm/InputConfigs.Resolve()(https://github.com/concourse/concourse/blob/release/5.6.x/atc/scheduler/inputmapper/inputmapper.go#L66) [there's an additional 'final resolve' afterward, which isn't relevant to the current thread]Resolve()fetches version candidates. In particular, it fetches exactly one of "all candidates", "the latest candidate", "a specific pinned candidate", or "the candidates which passed the configured jobs". (https://github.com/concourse/concourse/blob/release/5.6.x/atc/db/algorithm/input_configs.go#L19-L54)atc/db/algorithm/VersionDB.*callingatc/db/algorithm/Versions.With(), which finds-or-adds versions in sorted order. (https://github.com/concourse/concourse/blob/release/5.6.x/atc/db/algorithm/version.go#L36)atc/db/algorithm/InputCandidates.Reduce().Reduce()experimentally pins each version of a resource, in order, until it finds a version that "IsNext()" https://github.com/concourse/concourse/blob/release/5.6.x/atc/db/algorithm/input_candidates.go#L99IsNext()returns true if a build already exists for the given version of a resource (https://github.com/concourse/concourse/blob/release/5.6.x/atc/db/algorithm/input_candidates.go#L30)IsNext(), we return it as a mapping, we collect all those mappings, ensure that we have a mapping for each input config, do a final group resolve to check compatibility, save the input mapping to the DB, and then determine whether we should trigger the build.If my understanding is correct about this process, the cause of this bug is pretty straightforward - When
A) multiple versions of a resource exist, and
B) the newest version has a build that exists
then no other version of that resource is ever considered as an input to a potential job, regardless of
version: every.Given this, it seems like there might also be edge cases in which inputs that were
version: everyand didn't have apassedcould also skip versions. I'd have to dig in to confirm or reproduce that, though.A naiive suggestion might be, in cases where an
input_configisversion: every, to consider candidates in ascending order of check_order, skipping candidates that already have a build. I wouldn't be surprised if this is actually how it was implemented before, and that caused other subtle problems of "job gets stuck and never schedules any new builds". When i get some more time, I'll do some history spelunking.vito commentedon Nov 3, 2019
@YenTheFirst Impressive sleuthing! We're actually near the end of a complete re-design and re-implementation of the scheduling algorithm, currently living on the
algorithm-v3andrelease/6.0.xbranch. If you'd like to read a bit about it, I had a section on it in the v10 roadmap post. We're aiming to push it out as a 6.0 beta release sometime soon.For inputs with
version: everythe new algorithm actually does exactly what you proposed: walk through versions in ascending order from whichever version was previously used, falling back on descending order if none of those are valid.However, at least at some level, the problem with
passedandversion: everywith parallel upstream jobs is still pretty fundamental. Both attributes demand two things that are naturally in conflict: "use every version" and "use only versions that passed through the job". "Correcting" the behavior with parallel upstream builds would likely mean changing the semantics ofversion: everyto either a) wait for in-fight builds to complete before deciding which version to use or b) support walking "backwards" to execute older versions.Neither of these options really feel compelling to me. Even
version: everywithoutpassedconstraints is actually really awkward to support, and I really want to replace it with something else in the future as it has been the root cause of a ton of complexity/confusion in both the old implementation and the new one.YenTheFirst commentedon Nov 4, 2019
I don't think the concepts of
passedandversion everyare fundamentally at-odds, at least from a user's point of view. It's typically either "I want to use every version of a resource, as long as it passed an upstream job", or "I want to use every version of a resource, but wait until a sure-to-succeed upstream job has produced an accompanying artifact", or "I want to use every output of an upstream job (even if that's not every version of the resource)". It may make sense for those use-cases to get fulfilled by something other thanversion: every, but I do think those are use-cases that should be supported.In our particular case, the pipeline is something like:
I suspect we could work around this by removing the initial "build a docker image", and instead having unit-tests, integration-tests do their own building, but that duplicates work and code.
[In this particular case, yes, we're using
version: everyas a hack around the fact that Concourse does not have spaces yet, but...Concourse doesn't have spaces yet. I suspect that there's reasonable variations on this pipeline whereversion: everyisn't merely a hack]I'll take a closer look at those
algorithms-v3andrelease/6.0.xbranches. Where would be a reasonable place to leave feedback related to those branches, if applicable?Based only on your description,
At first glance, this sounds like it would have the exact same problem. If a downstream build for D completes before an upstream build for C, the downstream build for C will never start, since "D" will be the version that was previously used.
vito commentedon Nov 5, 2019
To be clear, I don't doubt the validity of your use case at all. I just don't see us improving this specific approach any time soon, because it will take a nontrivial amount of work, and it will just delay the real fix which we've recently gained a pretty clear picture of.
The problem is that, while
version: every+passedmay not seem to be at odds from a user-level, it's technically impossible to satisfy without significant changes to how Concourse works, which would mean other user-level changes. I just don't think these changes are the right thing to do.This is why I think it's a fundamental issue. Concourse pipelines go forwards, not backwards. Changing this would have a ton of implications, and it isn't a clean fix anyway because now there's a question of when does
version: everystop going backwards. For example, if I applyversion: everyto a regular Git repo withoutpassedconstraints, it's not likely that I would want it to literally go all the way back to the initial commit (resources will eventually collect all versions, not just from initial configuration time).We already know this feature is a pain in the butt to support, and doubling down on it to get it working correctly would require re-designing significant portions of the product around it, both internally and in the UI. So at this point I'm not on board with supporting
version: every+passed, and I would like to even phase outversion: everyin the long run. It's a crutch that makes things "work" just enough for us to not find better solutions.Your use case of building every commit of every PR is totally fine. I think a model that better fits how Concourse works would be to configure a pipeline for each commit. We just need to improve the automation and navigation of pipelines. This is all broken down into many small features, laid out in the v10 roadmap post. Implementing most of these features will be easier or at least more beneficial in the long-term than doubling down on
version: every.Hmm good point, not yet. We should just open a PR for it. 🤔 I'll link to it here once we do (or just keep an eye out if I forget!)
edit: PR is open #4721
Remove max in flight and add every in the concourse pipeline
Remove max in flight and add every in the concourse pipeline
Remove version: "every" from publish step artifacts
Remove "version: every" when "passed:" contraints are defined
taylorsilva commentedon Jan 6, 2025
Issue Bankruptcy
This issue is being closed as part of 2025's Issue Bankruptcy declaration. See here for details
If you think this issue still has merit then feel free to open a new issue and reference this one.