Skip to content

feat/labs migration#1968

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

feat/labs migration#1968
alanpeixinho wants to merge 10 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

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.

It is a valid concern. If we plain to move some operations to ingestion time, we certainly are going to end up increasing "ingestion complexity". But I believe that, as long as we keep the ingester fast enough, simplifying the analysis step should be the goal.

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

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.

Done

Comment thread backend/kernelCI_app/queries/build.py Outdated
)
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?

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 because lab is now the column name, but thinking again. I might change the column name for lab_id, this way we keep the lab name free to use, and follow more closely the the foreign key nomenclature.

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.

Changed mind, to really avoid rename hacking on the ORM, it was easier to just add a proper sql query instead.

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?

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.

Done

Comment thread backend/kernelCI_app/models.py Outdated
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

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.

The point is, this is the original implementation (pre-refactor)

    misc = sanitize_dict(build.misc) or {}
    lab = misc.get("lab", UNKNOWN_STRING)
    if lab:
        build_lab_summary = builds_summary.labs.get(lab)
        if not build_lab_summary:
            build_lab_summary = StatusCount()
            builds_summary.labs[lab] = build_lab_summary
        setattr(
            builds_summary.labs[lab],
            status_key,
            getattr(builds_summary.labs[lab], status_key) + 1,
        )

The conditional for lab is never false, because we are assigning UNKNOWN_STRING for lab.
However, it seems that this function is no longer used, and might be a legacy function used before performance improvements on HardwareDetails endpoints.
I will confirm it, and if this is the case, remove it.
We might as well plan to perform some dead code elimination, and check for similar cases.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Rename Builds/Tests FK field from lab to lab_id so lab is free for
annotated lab names without conflicting with the ORM relation.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Keep lab as the standard Django FK field and resolve build test lab names
via raw SQL, matching the hardware query pattern.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Enforce the lab FK db_constraint on builds and tests, drop the unused
lab_id from the build tests response, and fix the tree query lab TODO.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
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