Skip to content

feat: serve dependencies from the serving knowledge base (v0.16.0) - #57

Merged
scanoss-qg merged 2 commits into
mainfrom
feat/serving-duckdb-migration
Oct 28, 2026
Merged

scanoss-qg merged 2 commits into
mainfrom
feat/serving-duckdb-migration

Conversation

@scanoss-qg

@scanoss-qg scanoss-qg commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Moves dependencies off the mining PostgreSQL database onto the serving DuckDB knowledge base, the same migration the other five decoration services (components, cryptography, geoprovenance, licenses, vulnerabilities) went through. Clean cut: no configuration selects the mining path. CHANGELOG versioned as [0.16.0].

Concern Mining Serving
Resolution go-component-helper → all_urls ⋈ mines ⋈ versions pkg/resolver → component, component_version
Licences all_urls ⋈ licenses, fallback projects component_version_license ⋈ license ⋈ "SPDX_license_data"
Golang golang_projects + pkg.go.dev (optionally written back) component + pkg.go.dev (read-only) + GitHub repository
Transitive <ecosystem>_dependencies.dep_data (JSON) component_version_dependency

Configuration: Database.Driver defaults to duckdb, and Database.Path / DB_PATH is new (opened read-only). COMP_COMMIT_MISSING is gone.

Behaviour changes

  • Releases with several licences report all of them (about 1% of releases carry two rows); mining reported one. Compound identifiers (MIT/Apache-2.0) are still split.
  • No component-level licence fallback: serving has no projects equivalent, so a release without a licence reports NO_INFO.
  • Scoped npm and composer now resolve: pkg:npm/@angular/core (was NO_INFO) and, in the transitive walk, scoped npm / composer entries (was 404, because the package identifier dropped the namespace for everything but maven).
  • Version spelling: the export drops the leading v mining stored (v5.4.5 → 5.4.5). An exactly requested version is echoed as the caller spelled it, so golang v1.9.1 stays v1.9.1; a bare purl reports the stored spelling.
  • Golang tags: the export carries golang modules at pseudo-versions only, so a requested tag goes to pkg.go.dev, then to the GitHub repository; answered that way it reports SUCCESS.
  • Crates: the export files Rust under the cargo purl type; the walk maps crates to it and still answers pkg:crates/....
  • The service embeds UnimplementedDependenciesServer instead of the papi interface.

Verification

  • go test ./... passes; golangci-lint v2.10.1: 0 issues.
  • Built in golang:1.25-bookworm (max GLIBC_2.34) and run against full_2026101605.duckdb on d3 with the same 14 requests captured from production v0.15.0:
Case Result vs production
echo, multi-file, maven, pypi, nuget, gem, npm (express ×3, lodash, unknown, invalid) same
pkg:npm/@angular/core now resolved (MIT); was NO_INFO
pkg:github/scanoss/engine 5.4.5 version 5.4.5 (export drops the v)
golang ×5 same versions, licences and status; grpc bare purl picks the export's latest pseudo-version
composer laravel VERSION_NOT_FOUND (the export has the component but no versions); was COMPONENT_NOT_FOUND
django >=4.2 508.0.0 with NO_INFO (bogus 508 release in both; its licence row is empty)
transitive npm depth 1, maven, gem identical sets (48 / 8 / 13)
transitive npm depth 3 and crates (limit 50) same size; members vary run to run because the limit cuts a concurrent walk (true before too); every production member is in the unlimited serving walk
transitive scoped npm 3 dependencies; was 404
transitive composer / unknown / pypi same 404 / 404 / 400

Not in scope

  • 43% of sampled component_version_license rows point at a license row with an empty name and no SPDX id; those releases report NO_INFO. That is in the export.

Licence fallback (28cdae8)

43% of component_version_license rows point at license.id = 523, which has neither a name nor an SPDX id. That is mining's "no licence" row: production answers these releases without a licence too (I checked 36 samples against v0.15.0). A release with no licence of its own now borrows one, in the order scanoss.api's licence lookup uses, and reports it with info_code: WARNING and scanoss.api's message:

  1. the source component (source_purl_id) at the same version: "Showing results from the source code purl (…)";
  2. the nearest version of the component that has a licence: "Showing results from version …";
  3. the nearest version of the source component that has one: "Showing results from the source code purl (…) version …".

Details:

  • Status: a status that already reports a problem (REQUIREMENT_NOT_MET, VERSION_NOT_FOUND) keeps its code, and the fallback message is appended.
  • Placeholders: the rows that only say there is no licence (523, "None detected", "UNKNOWN", "unknown", all without SPDX) trigger the fallback and are never borrowed. If nothing can be borrowed, the release keeps them, as before.
  • Cost: the resolver now passes on the source link it already read, so it is not read again (that read is a scan of component by purl_id). One query per component reads every licensed release.
  • Nearest version: NearestVersions is ported from scanoss.api with one addition. Versions that semver ranks equal are ordered by their build metadata, so golang pseudo-versions pick the nearest in time.
  • Coverage: on samples of the export, this recovers a licence for 17–21% of such releases, and 36% for components with 1,000+ stars.
  • On d3: 13 of 36 sample releases now get a licence, and pkg:composer/laravel/framework gets MIT from pkg:github/laravel/framework.
  • Caveat: pkg:pypi/django resolves to 508.0.0, a bogus release in the export on both sides, and now borrows MIT from the equally bogus 503.0.0.

pkg.go.dev timeout

The live lookup of a golang module is given up after 5 s, covering both its attempts (with the version, then without), and the GitHub repository and the knowledge base answer instead. The HTTP client also carries the 5 s timeout, so an abandoned call does not linger. A test pins it with a pkg.go.dev that never answers.

Moves the service off the mining PostgreSQL database onto the serving DuckDB
file, as the other five decoration services already did. Clean cut: there is
no configuration that selects the mining path.

- pkg/db opens the serving file read-only (duckdb, sqlite or postgres);
  Database.Path / DB_PATH is new and duckdb is the default driver.
- pkg/resolver replaces go-component-helper's GetComponentsVersion, which
  resolves against all_urls, mines and versions. It tries every spelling the
  export stores, so scoped npm purls resolve however they are written, and it
  hands back each component's purl_id.
- Licences come from component_version_license through license and
  "SPDX_license_data". Every licence row of a release is reported; compound
  identifiers are still split. There is no component-level licence table, so
  the projects fallback is gone.
- Transitive dependencies come from component_version_dependency instead of
  the per-ecosystem JSON tables, every scope as before; crates map to the
  export's cargo purl type. The package identifier keeps the namespace for
  every ecosystem, which fixes scoped npm and composer entries.
- Golang tags the export lacks (it carries pseudo-versions only) are answered
  live from pkg.go.dev, then from the GitHub repository; nothing is written
  back, and COMP_COMMIT_MISSING is removed.
- cgo builds: release in golang:1.25-bookworm with the arm64 cross toolchain,
  Docker on bookworm with libstdc++6 and ca-certificates.
- The service embeds UnimplementedDependenciesServer.
- Tests run on SQLite against a transcription of the serving schema.
43% of component_version_license rows point at license 523, which has neither
a name nor an identifier: mining's "no licence" row, so mining answered those
releases without a licence too. A release with no licence of its own now
borrows one, in the order scanoss.api's licence lookup uses, and reports it
with info_code WARNING and scanoss.api's info_message:

  1. the source component at the same version;
  2. the nearest version of the component that has a licence;
  3. the nearest version of the source component that has one.

A status that already reports a problem keeps its code and gains the message.
The placeholder rows (523, "None detected", "UNKNOWN", "unknown") trigger
the fallback and are never borrowed. The resolver now hands over the source
link it already read, so it is not read again (a scan of component by
purl_id), and one query per component reads every licensed release.
NearestVersions is ported from scanoss.api, ordering versions semver ranks
equal by their build metadata so golang pseudo-versions pick the nearest in
time.

The live pkg.go.dev lookup of a module is given up after 5 s, both attempts
together, and the GitHub repository and the knowledge base answer instead; a
slow pkg.go.dev held the whole request (one call took 19 s).
@scanoss-qg
scanoss-qg merged commit a379660 into main Oct 28, 2026
2 checks passed
@scanoss-qg
scanoss-qg deleted the feat/serving-duckdb-migration branch October 28, 2026 18:52
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.

2 participants