Skip to content

Blueprints: Record collection ID used in generation #5010

Open

Description

@jgallagher

In the update huddle today, we discussed keeping either (a) the collection used to generate the current target blueprint or (b) a higher number of historical collections. We're on the fence about (a); (b) seems fine but it's not entirely clear how high we'd have to bump it to be useful in practice.

At a bare minimum, we can easily add the collection ID that was used when a blueprint was generated to the serialized blueprint. This may not be very useful if the collection it references has already been pruned, but it's cheap and doing so might end up giving us more insight into (a) or (b) above.

Activity

  1. self-assigned this
    on Feb 6, 2024
  2. davepacheco commented on Oct 22, 2026

    @davepacheco
    Collaborator

    Most of this is not too relevant any more. Since #10306, we emit Reconfigurator state files for each blueprint that becomes the target blueprint. That file contains the inventory collection used to plan it. I think it's still a good idea to include the collection id in the blueprint.

  3. davepacheco commented on Nov 4, 2026

    @davepacheco
    Collaborator

    Sorry in advance that this is kind of raw. I'll put up a PR with a simpler, specific proposal but I wanted to record some thinking here.


    I thought I'd take a look at this, thinking it would be trivial. But now I've run into some tricky bits. This makes a lot of sense when we think about blueprints generated by the planner, since it's making decisions based on what it finds in inventory. But there are other sources of blueprints:

    /// Description of the source of a blueprint.
    #[derive(
    Clone, Debug, Eq, PartialEq, JsonSchema, Deserialize, Serialize, Diffable,
    )]
    #[serde(tag = "source", rename_all = "snake_case")]
    pub enum BlueprintSource {
    /// The initial blueprint created by the rack setup service.
    Rss,
    /// A blueprint created by the planner, and we still have the associated
    /// planning report.
    Planner(Arc<PlanningReport>),
    /// A blueprint created by the planner but loaded from the database, so we
    /// no longer have the associated planning report.
    PlannerLoadedFromDatabase,
    /// This blueprint was created by one of `reconfigurator-cli`'s blueprint
    /// editing subcommands.
    ReconfiguratorCliEdit,
    /// This blueprint was constructed by hand by an automated test.
    Test,
    }

    The collection id is not applicable for a source of RSS, ReconfiguratorCliEdit, or Test since those aren't necessarily making decisions based on a collection.

    One option is simply an Option<CollectionId> on the Blueprint. It's simple, but it's not at all clear when it's present. I don't really like it.

    Another option would be to put the collection id into the two planner-related BlueprintSource variants. BlueprintSource::Planner would have a CollectionId. BlueprintSource::PlannerFromDb would probably have to have an Option<CollectionId> because we can't backfill a collection id for blueprints that were created in releases before we make this change.

    If this is really just for debugging, an even simpler option would be to just put the collection id into the planning report. This will be available to people using omdb or reconfigurator-cli, but it won't be available to Nexus itself. That gets to the real purpose behind this. I think we've discussed two purposes:

    1. for debugging
    2. to be able to say something like: Nexus should not plan a blueprint based on a collection older than the one that the previous blueprint was based on

    Putting it into the planning report is fine for 1. For 2, Nexus needs programmatic access, not to the collection id (which might be gone) but to some timestamp or sequence number related to inventory collection. This doesn't make sense for the Rss blueprint and probably not the Test one. It's unclear what should happen for a ReconfiguratorCliEdit one, or even a Planner one that was imported from elsewhere. If we decide to do (2), we'd first need to firm up exactly what we mean and what we want each of these things to do. (They might reset the field, or they might inherit it from the parent, or something else. You might think it was always safe to inherit it from the parent, but I don't think that's true for an "edit"-type change that ignored something from that collection -- you might actually want the next planner to run to re-consider that collection.)


    Yet a different option would be to add a new top-level field that's like:

    enum ReflectsCollection {
        Collection {
            collection_id: CollectionUuid,
            time_started: DateTime<Utc>,
            time_done: DateTime<Utc>,
        },
        Indeterminate,
    }

    The planner could always provide BasedOnCollection::Collection. Other things could decide which variant they want to provide here. It wouldn't have to be strictly tied to one source or another. In some future where we decide to implement a rule like the one above, if we have a specific collection, we can honor that, and if we don't, we can ignore it. I'm leaning towards this and will see if it's easy to throw together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions