Skip to content

Indexes 3: Adds flatbuffers schema and SolverPackageSpec for indexes to spk#1338

Open
dcookspi wants to merge 2 commits intoindex-2-new-unchecked-additionsfrom
index-3-flatbuffer-and-solver-package-spec
Open

Indexes 3: Adds flatbuffers schema and SolverPackageSpec for indexes to spk#1338
dcookspi wants to merge 2 commits intoindex-2-new-unchecked-additionsfrom
index-3-flatbuffer-and-solver-package-spec

Conversation

@dcookspi
Copy link
Copy Markdown
Collaborator

@dcookspi dcookspi commented Mar 19, 2026

Note: for info on benefits of indexing for spk solves see #1340 (5 of 5). Maybe start there and work back down to this PR if you prefer to review PRs top down.

This adds a new spk-proto crate to spk, and a new v0 package implementation: SolverPackageSpec. spk-proto contains a flatbuffers schema for index package, version, build, options, and global variables data. The flatbuffers schema is designed for solver use. It only stores data needed for solving with the packages. It is not a complete replacement for all package operations. It does not have enough data to build or test packages.

SolverPackageSpec is a package implementation backed by a flatbuffer. Those packages will be produced from an index by changes added in a subsequent PR (#1339, 4 of 5). The functions for converting spk schema objects into and out of flatbuffer objects are contained in fb_converter.rs.

This is one of the changes that supports adding indexes and index based packages to Spk repositories.

This is 3 of 5 chained PRs for adding indexes to spk solves:

  1. Indexes 1: Change Package and related traits to not return references to fields #1336
  2. Indexes 2: Add new_unchecked() constructors to spk schema objects #1337
  3. this PR
  4. Indexes 4: Adds Indexes for SPK repositories #1339
  5. Indexes 5: Adds spk repo index subcommand for index generation and updates #1340

@dcookspi dcookspi self-assigned this Mar 19, 2026
@dcookspi dcookspi added enhancement New feature or request SPI AOI Area of interest for SPI pr-chain This PR doesn't target the main branch, don't merge! labels Mar 19, 2026
@@ -0,0 +1,327 @@
// Copyright (c) Contributors to the SPK project.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This flatbuffer schema does not match the rust spk schema objects one-to-one. The focus is on in making an index that is useful for spk solvers. There is not enough data in the index schema to build or run tests on any of the packages. There is only enough to run solves on them.

Some of the tables have the same name as their rust counterparts, others do not. Some are flatbuffer specific. Some do not store all the fields of their rust counter part. The ones not stored are noted as 'ignored' in comments. Some of the objects have been flattened down to include pieces of intermediate or more deeply nested rust objects. Those should also be noted in comments.

Comment on lines +60 to +64
// TODO: should this be IndexedPackageSpec or something else? It comes
// from an index and requires one to function, but it is used/usable
// only by the solver.
#[derive(Debug, Serialize)]
pub struct SolverPackageSpec {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Does this name seem sensible?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

First I'm not sure we even want to use "spec" in the name here, since to me that word makes me think more about content a human would write and/or yaml-formatted content, based on all the existing things we refer to as a "spec".

I'll propose IndexedPackage as a name for this.

I don't think it is true that it is only used by the solver. We already have plans to use the index for other kinds of reporting.

Comment on lines +104 to +105
// TODO: skip this? It could be quite large?
self.buf.hash(state);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure how meaningful the underlying bytes are here. The offset might be enough.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It would presumably be way cheaper to only hash the offset. That should be enough to cover uniqueness, I'm assuming no two packages in the index are going to have the same offset.

Comment on lines +382 to +384
// TODO: should this change the return value, update all the
// caller's handling, and return an error for this implementation?
unreachable!("{err}");
Copy link
Copy Markdown
Collaborator Author

@dcookspi dcookspi Mar 19, 2026

Choose a reason for hiding this comment

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

This question comes up in all the unimplemented parts of the SolverPackageSpec. The same decision can be applied to all of them. I went with unreachable for now to avoid the resulting Result<...> checking cascade throughout the rest of the codebase. But I don't know how large a change it would be.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's more work of course but I would like to refactor traits such that metadata isn't a required method on Package and then also the solver doesn't require an implementation of whatever [new] trait metadata moves to. Therefore you know for a fact this is unreachable; the code doesn't have to exist.

Comment on lines +62 to +67
// TODO: all these functions are here to keep the flatbuffer
// conversions together while deciding on whether to replace the
// backing of current rust objects with flatbuffer ones, like
// SolverPackageSpec does. Or whether to split these functions up into
// a couple of traits (e.g. IntoFlatbuffer, FromFlatbuffer) across
// each of the existing rust types.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Or, another alternative is to leave them together in this file for now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like the idea of leaving them all here in one place as a starting point.

Comment on lines +225 to +228
// TODO: this will be the next thing to get a
// proper flatbuffer representation because it
// is now showing up as the next significant
// chunk on the large solve flamegraphs
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I want to highlight this as one of the next likely improvements to the overall indexing. It's not part of this PR-chain at the moment.

Comment on lines +384 to +408
// Note: fb_compats in packages are not optional in the rust structs,
// but fb_compat's stored in var opts are optional in the rust struct.
#[inline]
fn var_opt_fb_compat_to_var_opt_compat(fb_compat: Option<&str>) -> Option<Compat> {
// None stored as an fb_compat represents no compat specified at
// all for a var opt. Some compat stored means a compat was
// specified, and it may even be the same as the default compat.
fb_compat.map(|fc| {
Compat::new_unchecked(fc)
.expect("A Compat in flatbuffer data shold be a valid Compat when parsed")
})
}

#[inline]
pub fn fb_compat_to_compat(fb_compat: Option<&str>) -> Compat {
if let Some(compat) = fb_compat {
Compat::new_unchecked(compat)
.expect("A Compat in flatbuffer data should be a valid Compat when parsed")
} else {
// In this case, None, so nothing, stored as an fb_compat
// represents the default compat. This is different to the var
// opt compat method above.
Compat::default()
}
}
Copy link
Copy Markdown
Collaborator Author

@dcookspi dcookspi Mar 19, 2026

Choose a reason for hiding this comment

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

Compats and the 2 uses of CompatRules described here, across the comments in these functions, bit me more than once during this work.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 19, 2026

…kage data.

Adds v0 SolverPackageSpec as a package implementation backed by a flatbuffer.

Adds fb_converter functions for converting spk schema objects into and
out of flatbuffer objects.

Signed-off-by: David Gilligan-Cook <dcook@imageworks.com>
Signed-off-by: David Gilligan-Cook <dcook@imageworks.com>
@dcookspi dcookspi force-pushed the index-3-flatbuffer-and-solver-package-spec branch from 53f24d1 to cb733cd Compare March 27, 2026 19:09
// The pieces of a pkg/version/build Ident, but not combined as a
// BuildIdent. This is used for embedded packages.
table IdentPartsBuf {
repository_name: string;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I can't think of a reason why this field would ever [need to be] populated in the index. But if there's zero cost to having it here...


// A Package/Version/Build containing what is needed when solving
table BuildIndex {
// Older packages may have been published with non-normalized
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This isn't really about "older packages" but that this preserves the version number as written in the recipe (to please users) versus the normalized version number which is used for comparisons.

table VersionIndex {
// The version number only
version: Version (required);
builds: [BuildIndex];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are builds sorted?

}

// The index for a repository
table RepositoryIndex {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's at least add some field that represents a high level version number that governs the practices outlined in comments but aren't represented in the types. If we change validation rules, expectations, etc., then we would bump this number up.

thiserror = { workspace = true }

[build-dependencies]
flatc-rust = "0.2"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please move this version up to workspace and update spfs-proto to use the workspace version too.

true
}

fn sources(&self) -> &Vec<SourceSpec> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Similar to the idea above about moving metadata out of Package, I'm thinking Package shouldn't have a sources either -- wouldn't that be a property for Recipe to have? In the future with #1187 we won't have this property in a "package".

Cow::Owned((**self.cached_requirements.load()).clone())
}

fn get_all_tests(&self) -> Vec<SpecTest> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same idea here of something that shouldn't be in Package.

It does leave me wondering if we want to have a separate SolverPackage trait with a reduced set of requirements that the solver expects. We either need that or some combination of sub-dividing Package to cater to the different needs of different subsystems.

Comment on lines +62 to +67
// TODO: all these functions are here to keep the flatbuffer
// conversions together while deciding on whether to replace the
// backing of current rust objects with flatbuffer ones, like
// SolverPackageSpec does. Or whether to split these functions up into
// a couple of traits (e.g. IntoFlatbuffer, FromFlatbuffer) across
// each of the existing rust types.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like the idea of leaving them all here in one place as a starting point.

spk_proto::PreReleasePolicy::None => None,
spk_proto::PreReleasePolicy::ExcludeAll => Some(PreReleasePolicy::ExcludeAll),
spk_proto::PreReleasePolicy::IncludeAll => Some(PreReleasePolicy::IncludeAll),
_ => None,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

are these enums generated with non-exhaustive?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess based on some code comments below that these are plain numbers.

It could be a good idea to add debug_assert!()s on each of these catch all cases to detect things that were missed after any schema changes.

ComponentEmbeddedPackage::new_unchecked(ident, components)
})
.collect()
// TODO: This does not set fabricated, not sure if that is a problem or not?
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It should be set true if the list was populated from default values, the point being that if it was fabricated then the list is not serialized. So what's most important is to skip serializing it when creating the flatbuffer instance (if fabricated is true). Then, this would go through the None case when deserializing.

Perhaps we can cook up some tests around the expected behavior.

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

Labels

enhancement New feature or request pr-chain This PR doesn't target the main branch, don't merge! SPI AOI Area of interest for SPI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants