Skip to content

feat/prune age#1970

Open
alanpeixinho wants to merge 7 commits into
kernelci:mainfrom
profusion:feat/prune-age
Open

feat/prune age#1970
alanpeixinho wants to merge 7 commits into
kernelci:mainfrom
profusion:feat/prune-age

Conversation

@alanpeixinho

@alanpeixinho alanpeixinho commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Add prune_db, a manual management command to delete old data and reclaim space.

  • Deletes checkouts, builds and tests older than a given age (e.g. --older-than "30 days").
  • Manual cascade (models use DO_NOTHING): pruning a checkout removes its builds, pruning a build removes its tests — even when children are newer.
  • --origins scopes the age filter; cascade stays origin-agnostic so no orphans are left.
  • Batched deletes (--batch-size) that each commit on their own to keep locks short.
  • --dry-run and a per-table confirmation prompt showing exactly how many rows will be affected (skip with --yes).
  • Extracts parse_interval from update_db into a shared helper.

Usage

python manage.py prune_db --older-than "30 days" --dry-run                                                                                                                                                         
python manage.py prune_db --older-than "30 days" --origins maestro                                                                                                                                          

How to Test

  • In a local database create newly seeded data poetry run python3 manage.py seed_data --clear --yes
  • Run command poetry run python3 manage.py prune_db --dry-run with different arguments for older-than parameter.
  • Verify the number of selected rows matches the database.
  • Run command poetry run python3 manage.py prune_db with different arguments for older-than parameter.
  • Verify the rows are properly deleted.

@alanpeixinho alanpeixinho force-pushed the feat/prune-age branch 2 times, most recently from e5c35c3 to 552bcc4 Compare July 3, 2026 20:04

# Values are always passed as query parameters; only static SQL fragments are
# interpolated. Named parameters let us reuse the same dict across every query.
params = {"cutoff": cutoff, "batch_size": options["batch_size"]}

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.

a minor but I think that would be nice to validate the batch_size. Currently we are able to pass a negative value to the database.

django.db.utils.DataError: LIMIT must not be negative

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 on lines +1 to +8
"""
Management command to prune old builds, tests and checkouts.

Removes checkouts, builds and tests older than a given age. To keep referential
integrity, deletion cascades manually (models use DO_NOTHING): a removed
checkout drags its builds, and a removed build drags its tests, even when those
children are newer than the cutoff.
"""

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.

Worth noting in docs that aggregate tables are not touched by this command.

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

@tales-aparecida

Copy link
Copy Markdown

@nuclearcat how long/far-back do we keep the raw KCIDB json payloads?

@@ -0,0 +1,25 @@
from datetime import datetime, timedelta

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.

Django has a built-in parser django.utils.dateparse.parse_duration that could be used 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.

I took a look on the built-in parser. Unfortunately it has different standards from what we are following.

  • standard: "30 00:00:00" (%d %H:%M:%S)
  • ISO 8601: "P30D"
  • postgres: "3 days 04:05:06" (only works for days)
    I believe we might benefit more of keeping our custom parser, we could even extend it to include weeks or months, especially in a scenario where we are working with longer ranges.
    What you think?

@nuclearcat

Copy link
Copy Markdown
Member

Each batch re-evaluates the full nested predicate. Add on top batch might be too small (we need to check how many tests per day we have, how long take deletion of 10k).
Consider materializing the ids once into a CREATE TEMP TABLE tobedeleted_... AS SELECT id .., and then batch-deleting by joining against it. Temp tables survive across the per-batch commits within the same session, so this fits the existing design.

  * Delete rows older than a given age with manual cascade (checkout -> builds -> tests)
  * --origins scoping, --dry-run and a --yes confirmation.
  Extract parse_interval into a shared helper and add integration tests.

  Closes kernelci#1969

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>
…tests. * Delete rows older than a given age with manual cascade (checkout -> builds -> tests) * --origins scoping, --dry-run and a --yes confirmation. Extract parse_interval into a shared helper and add integration tests.

    * Add tables parameter.
    * Protect issue related rows by default, and add parameter to
      bypass.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
…tests. * Delete rows older than a given age with manual cascade (checkout -> builds -> tests) * --origins scoping, --dry-run and a --yes confirmation. Extract parse_interval into a shared helper and add integration tests.

    * Add tables parameter.
    * Protect issue related rows by default, and add parameter to
      bypass.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
…tests. * Delete rows older than a given age with manual cascade (checkout -> builds -> tests) * --origins scoping, --dry-run and a --yes confirmation. Extract parse_interval into a shared helper and add integration tests.

    * Add tables parameter.
    * Protect issue related rows by default, and add parameter to
      bypass.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
@alanpeixinho

Copy link
Copy Markdown
Contributor Author

Each batch re-evaluates the full nested predicate. Add on top batch might be too small (we need to check how many tests per day we have, how long take deletion of 10k). Consider materializing the ids once into a CREATE TEMP TABLE tobedeleted_... AS SELECT id .., and then batch-deleting by joining against it. Temp tables survive across the per-batch commits within the same session, so this fits the existing design.

Nice catch @nuclearcat . I have included a temp table to store ids before batch deleting.

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.

5 participants