Skip to content

Conversation

@iliana
Copy link
Contributor

@iliana iliana commented Sep 27, 2025

Part of #7135. Related to (and should probably be rebased on or merged into) #9107.

No explicit tests yet but seems to work as expected for existing tests.

Also modifies the artifacts_for_repo function to be paginated, as the number of artifacts per repo has grown and is expected to continue to grow.

Copy link
Collaborator

@davepacheco davepacheco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got some minor suggestions but this looks good!

I think I'd probably rebase this one on the other one rather than combining them, at least for the purposes of review and approval. I don't feel strongly between those two options. For what it's worth, I already made a branch that starts from this one and merges in the branch from the other one. There was a tiny conflict to resolve. I could push that here if you want (and then you'd want to actually change the base branch in GitHub). (My branch is called dap/testing/pruning.) see my next comment

);
};

dbg!(&last_result_completed);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if you meant to leave these here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debatable! I'd love some nicer error handling here because when there's an {"error":"blah blah blah"} it panics without showing the message at all. This is halfway to that, and only is printed to the console if a test calls these replication-related helpers and the background tasks fail.

opctx: &OpContext,
repo_id: TufRepoUuid,
) -> ListResultVec<TufArtifact> {
opctx.authorize(authz::Action::Read, &authz::FLEET).await?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add do two things to try to avoid people accidentally using this in API endpoints (since we're making multiple queries here):

  • add a call to opctx.check_complex_operations_allowed()?;
  • add _batched() to the name to convey that (we do this in a few other places in the datastore)

Really, I'd be tempted to apply this to artifacts_for_repo, but that may currently break some callers that might be using it from the API. Those should probably be made paginated but we can do that when the dust settles on these APIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After #9106 lands we may be able to refactor things a little and apply this to artifacts_for_repo, since it removes the list of artifacts from the public APIs.

Comment on lines 73 to 75
.inner_join(tuf_artifact_dsl::tuf_artifact.on(
tuf_artifact_dsl::id.eq(tuf_repo_artifact_dsl::tuf_artifact_id),
))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For our use case, I think we don't need this join. We only need the artifact ids. It might be nice to have a version of this that just does that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm going to revert artifacts_for_repo and wait to possibly paginate it during a refactor, particularly if we add check_complex_operations_allowed there. I'll write the query that simply returns the artifact IDs for a repo in the function we add instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No wait we do need the join here, because we ultimately use the artifact sha256sums in the replication task.

@davepacheco

This comment was marked as outdated.

@iliana iliana force-pushed the iliana/artifact-pruning branch from 9f685b4 to fdbed8b Compare October 1, 2025 00:47
@iliana iliana marked this pull request as ready for review October 1, 2025 00:47
Comment on lines +596 to +604
{
let generation_now =
self.datastore.tuf_get_generation(opctx).await?;
ensure!(
generation == generation_now,
"generation changed from {generation} \
to {generation_now}, bailing"
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making sure I understand: we have to do this check because if the generation changed, the config we'd build from repos would be inconsistent with other Nexuses building a config at generation_now, right? This seems pretty critical and maybe easy to miss - maybe worth a comment? (My very first reaction reading this was "why do we need to check this? if a new repo has been pruned or added that's fine and we'll just pick it up the next time we run")

Alternatively: should tuf_list_repos_unpruned_batched() take a generation argument and fail if it changes at any point during the listing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I did have this implemented where we read generation and then paginated through the repositories during a single transaction, but I replaced it with the datastore methods from #9107. The comment for tuf_list_repos_unpruned_batched() reads:

    /// Since this involves pagination, this is not a consistent snapshot.
    /// Consider using `tuf_get_generation()` before calling this function and
    /// then making any subsequent queries conditional on the generation not
    /// having changed.

So the second check is my interpretation of making subsequent queries conditional on the generation not having changed. I will add a comment to this effect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah, yeah, one conditional check after does seem better than reasserting the condition as we page through a table. Thanks.

Comment on lines 110 to 112
let mut watcher = self.storage.delete_done_rx.clone();
watcher.mark_unchanged();
watcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit - I think we could return self.storage.delete_done_tx.subscribe() instead? Then we wouldn't need the mark_unchanged(), and maybe could also drop the delete_done_rx field entirely?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes, we could do that. I forgot about the subscribe() method.

@iliana iliana enabled auto-merge (squash) October 1, 2025 20:16
@iliana iliana merged commit 6731ce2 into main Oct 1, 2025
16 checks passed
@iliana iliana deleted the iliana/artifact-pruning branch October 1, 2025 22:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants