Skip to content

feat/labs migration#1968

Open
alanpeixinho wants to merge 3 commits into
kernelci:mainfrom
profusion:feat/labs-migration
Open

feat/labs migration#1968
alanpeixinho wants to merge 3 commits into
kernelci:mainfrom
profusion:feat/labs-migration

Conversation

@alanpeixinho

@alanpeixinho alanpeixinho commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates all read paths for lab information from JSONB misc fields (builds.misc->>'lab',
tests.misc->>'runtime') to the new labs table via lab_id foreign key, with JSONB fallback for rows not yet backfilled.
Every SQL query and Python helper that previously extracted lab names by parsing JSONB now uses COALESCE(labs.name, misc_fallback) through a LEFT JOIN on the labs table. This ensures:

  • New data (with lab_id populated by the ingester) reads from the structured FK
  • Historical data (without lab_id) still works via the JSONB fallback
  • After a full backfill, the JSONB fallbacks can be removed (all marked with TODO comments)

How to test

legacy database

  • Use a database where lab_id is NULL on existing rows
  • Verify all pages display lab names as before:
    • Tree details page (boots/tests tabs) — lab column in test history
    • Tree commits history page — build lab and test lab in filters and rows
    • Hardware details page — lab in test/build summaries, history, and filters
    • Build details page — lab column in test list
    • Issue details page — lab column in test list
    • Notifications/metrics endpoint — lab summary counts

Migrated database (test in a local database)

  • Run the migrations available
  • Run the ingester (which already fills lab_id)
    • Verify all pages display lab names as before:
    • Tree details page (boots/tests tabs) — lab column in test history
    • Tree commits history page — build lab and test lab in filters and rows
    • Hardware details page — lab in test/build summaries, history, and filters
    • Build details page — lab column in test list
    • Issue details page — lab column in test list
    • Notifications/metrics endpoint — lab summary counts

Part of kernelci#1948

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Part of kernelci#1948

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
@alanpeixinho alanpeixinho marked this pull request as draft July 1, 2026 20:59
@alanpeixinho alanpeixinho changed the title feat/labs migration {feat/labs migration Jul 1, 2026
@alanpeixinho alanpeixinho changed the title {feat/labs migration [WIP] feat/labs migration Jul 1, 2026
@alanpeixinho alanpeixinho force-pushed the feat/labs-migration branch from 7ee4011 to 80d7ce6 Compare July 2, 2026 20:51
@alanpeixinho alanpeixinho marked this pull request as ready for review July 2, 2026 20:53
    * For analysis queries we are going for lab column information
      first, and coalescing to json misc information.
    *  All COALESCE expressions are marked with TODO comments for removal
      after the lab_id backfill is complete.

    Closes kernelci#1948

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
@alanpeixinho alanpeixinho force-pushed the feat/labs-migration branch from 80d7ce6 to 3404f84 Compare July 2, 2026 21:16
@alanpeixinho alanpeixinho changed the title [WIP] feat/labs migration feat/labs migration Jul 2, 2026


def assign_lab_ids(builds_buf: list[Builds], tests_buf: list[Tests]) -> None:
"""Resolve each instance's real lab name to a labs.id and set its lab_id FK.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the lab name is unique in the database, why not use that as a primary key? In that case you'd make a first query to check which labs are not in the database yet, a second query to update the table if needed, but you wouldn't need the third query to get the id of the newly added labs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Mostly to avoid string keys, since this is an internal key, we might benefit more from integer indices.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Makes sense. I am worried that we are adding one query here, one query there and soon we will have a bloat of unnecessary queries in the ingester, but it's fine for now

tests.number_value AS tests_number_value,
tests.misc AS tests_misc,
tests.environment_compatible AS tests_environment_compatible,
COALESCE(test_labs.name, tests.misc ->> 'runtime') AS tests_lab,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think you forgot a TODO here

)
return list(result)
tests = []
for test in result:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not assign directly to lab like before?

result = []
for row in rows:
build_misc = row[11]
sanitized_build_misc = sanitize_dict(build_misc)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why does this not use the fallback?

log_excerpt = models.CharField(max_length=16384, blank=True, null=True)
misc = models.JSONField(blank=True, null=True)
lab = models.ForeignKey(
Labs, db_constraint=False, null=True, on_delete=models.DO_NOTHING

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't we use db_constraint=True?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ditto for Tests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see all other tables are using db_constraint=False, so there must be a good reason for this, but I do not see it documented

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For the kci keys, we unfortunately have some missing keys in a few rows. But since lab has only internal keys, I believe we can safely enforce constraints on them. Good point.

Comment on lines +454 to +457
build_lab_summary = builds_summary.labs.get(lab)
if not build_lab_summary:
build_lab_summary = StatusCount()
builds_summary.labs[lab] = build_lab_summary

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy pattern from previous chunk for consistence and readability

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.

3 participants