Repository navigation
feat(coordinator)!: plan parents through an ordered list of sources - #658
Conversation
3e8998f to
4b047e9
Compare
4b047e9 to
b430716
Compare
3c83246 to
b64f9a0
Compare
49f127a to
45e1d83
Compare
45e1d83 to
a54370c
Compare
a54370c to
5d1ef6e
Compare
TimoStoff
left a comment
There was a problem hiding this comment.
Boy that was a big one! I think I have some helpful comments, looks mostly good though I have to admit this was a bit at the limit of what I have time to review really properly….
1112233 to
c10efe0
Compare
c10efe0 to
7dce03e
Compare
TimoStoff
left a comment
There was a problem hiding this comment.
on second thoughs….lgtm
89068d6 to
bf854e7
Compare
bf854e7 to
9cab11e
Compare
There was a problem hiding this comment.
I reviewed this PR and found no bugs. It is a large breaking redesign of the build planner, and new commits landed after the last approval, so a human should still look at it.
What was reviewed:
plan_release_buildandrun_planin coordinator/build.py. EveryCopyStepnow copies only throughcopy_releasewithwith_payload_sources, and everyBuildStepbuilds with theInstalledBuilderthe lookup stored and its ownforce.- The glue in client.py (
_prepare,_obtain), api.py and cli.py that turnsfetch_from/build_parents/auto_buildintoparents_from, including the new hints that name only the options the build did not use. - The
_Graphwalk in plan.py. This covers source ordering, damaged local copies, payload-source steps, and the pin check, which runs once before any step.
Extended reasoning...
The change rewrites the build coordinator (plan.py, build.py), adds ask= to sources.py, changes the missing-parent message in registry/_copy.py, and updates the client, the connectors API and the CLI. It is about 1,570 changed lines across 11 files. It touches no auth, crypto or injection surface. I deferred because it is large and breaking, and it makes design decisions a maintainer should weigh: source ordering, the store never being its own source, damaged copies stopping the plan, and the error type change from TimeFValidationError to TimeNetMissingParentError. There were also commits after the reviewer's approval.
9cab11e to
2cf6965
Compare
2cf6965 to
7449854
Compare
There was a problem hiding this comment.
An organization admin can review the limit in your organization's usage and billing settings.
Once the limit resets or is raised, someone with write access to this repository can comment @claude review on this pull request to trigger a review.
A build gets each parent that the store lacks from one ordered list of sources: registries to copy from, and InstalledConnectors() to build with. Before this change, the planner took one registry (fetch_from) and one flag (build_parents). The planner now takes parents_from. For each release that the store lacks or holds damaged, resolve_release() picks the first source that provides its exact version. A registry gives a CopyStep from that registry, and InstalledConnectors() gives a BuildStep with the InstalledBuilder of the lookup. Step is the union of the two types. A registry that lists a release but does not serve its files does not count, so a later source can provide the release. A release needs exactly the releases that its manifest lists as payload sources, never the payload sources of a payload source. The copy of a parent brings each of its payload sources that the registry serves. The planner finds them before any step runs, with find_payload_sources(), the walk and access check that copy_release() uses too. A payload source that both the registry and the store lack gets its own step, from its own source, and the copy waits for it. So a copy that cannot be completed fails before any download. A copy of a payload source alone takes only its own files (with_payload_sources=False). After the walk, the planner checks the pins of each pinned release: a copied release against its registry, a held release against the store, and for a release to build, that its readers pin each file alike. So these errors stop the plan before any step runs. In one plan, the planner asks each source at most once for each release, and it keeps each answer. It takes the manifests from the answers, which a registry that is not local fills. It reads a manifest itself only from the store and from a local registry. So the hosted registry gets one request for each manifest. A BuildStep holds the InstalledBuilder that the lookup read, and run_plan() builds with it. So a plan and its run read the card of each connector once. plan_steps() takes the builder of the requested release. The planner now makes the build step of the requested release too. A step waits for the nearest steps that it depends on, also through a release without a step. For example, if the store holds a parent but not its payload source, the build waits for the copy of that payload source. The store is never a source for itself. The planner drops it from the list, also as another LocalRegistry over the same directory, so it never copies the store onto itself. A damaged copy of a parent stops the plan if no other registry provides it. A damaged copy of the requested release gets a forced build, so the plan never ends with "Nothing to do" for it. build_release() and plan_parents() go away. plan_release_build() plans a build, and run_plan() runs the plan with one arm for each step type. plan_release_build() checks the connector and the version, and then it plans. Each BuildStep has a force flag. plan_release_build(force=True) sets it on the build of the requested release, and run_plan() passes it to the backend. run_plan() copies only through copy_release(). The CLI, timenet_connectors.build() and the client keep their keywords for now. Each maps them onto parents_from: the registry of --fetch-from first, then InstalledConnectors() if --build-parents is set. The client always adds InstalledConnectors(). The hint of a missing parent names only the options that the build did not use. If a payload source is missing, copy_release() now names both registries. BREAKING CHANGE: A parent that no installed connector builds at its exact version raises TimeNetMissingParentError, not TimeFValidationError. A parent that a registry lists but does not serve now builds when the build may build parents: with --build-parents, build_parents=True, or the auto_build of the client. Part of TNET-157.
The comments in the connectors API and CLI that map fetch_from and build_parents onto a list of sources now start with "Glue:". Part of TNET-157.
The glue comments of this change start with TODO(adrian), so a search finds each one that is left. Part of TNET-157.
Say only what a caller of the build plan and of its list of sources needs to know. Part of TNET-157.
Shorter docstrings for the code that this change adds or changes. Part of TNET-157.
Drop tests that cannot fail or repeat other tests, and sharpen the rest. Part of TNET-157.
7449854 to
fef6ac7
Compare
The Stack
This stack covers refactoring the internal logic of how releases are resolved during read, download & build, in particular also in the context of parent releases / parent datasets. On top of that it refactors the public TimeNet APIs and CLI interfaces around these actions, to streamline the behavior across them.
This PR
Moves the build planner onto an ordered list of sources.
Details
A build gets each parent that the store lacks from one registry (
fetch_from) or from a build (build_parents) (TNET-157). The design replaces both with one ordered list of sources, which #657 adds. This PR moves the build planner onto that list.plan_steps()replacesplan_parents(). It takes theInstalledBuilderof the requested release andparents_from, an ordered list of sources. For each release that the store lacks or holds damaged,resolve_release()picks the first source that provides its exact version. A registry gives a copy step, andInstalledConnectors()gives a build step.CopyStep(release, registry, depends_on, with_payload_sources)andBuildStep(release, builder, depends_on, pinned_by, force).Stepis their union. Each type has a read-onlyaction("copy" or "build") for the plan tree.run_plan()matches on the step type, with one arm for each type.BuildStepkeeps theInstalledBuilderthat the lookup read, and the run builds with it. So a plan and its run read the card of each connector once.resolve_release()takesask=, a function that asks one source. The planner passes one that keeps each answer.Roledocstring states this rule. The planner applies it before any step runs, with the walker ofcopy_release(),find_payload_sources()(feat(registry)!: copy a release between registries #649). The copy of a dataset brings the payload sources that its registry serves. A payload source that both the registry and the store lack gets its own step, and the copy waits for it. That copy haswith_payload_sources=False. So a copy that cannot be completed fails before any download.build_release()is nowplan_release_build()and thenrun_plan().run_plan()copies only throughcopy_release().BuildStephasforce. A forced build step builds its release again, over any copy in the store. Two cases force the build step of the requested release: the caller asks forforce, or the store holds a damaged copy of that release. Sorun_plan()takes noforce.timenet_connectors.build()and the client keep their keywords for now and map them ontoparents_from. The hint for a missing parent names only the options that the build did not use. If a payload source is missing,copy_release()names both registries.packages/timenet/src/timenet/coordinator/plan.pyandbuild.py. They hold the planner core, about 770 of the changed lines. The rest is the callers,ask=insources.pyand the migrated tests.Design decisions
LocalRegistryover the same directory.Breaking changes
TimeNetMissingParentError, notTimeFValidationError.InstalledConnectors()is inparents_from, a parent that a registry lists but does not serve now builds.Stack
Changes later in the stack:
--fetch-from,--build-parentsandtimenet_connectors.build(fetch_from=, build_parents=)ontoparents_fromis thin glue. "Defaults and the producer API" replaces it withtimenet.build()and its CLI flags._obtain()and_prepare()is thin glue. The next PR replaces both functions with the read coordinator.Merge message