Skip to content

Investigation.total_jobs runs an extra DB query for every pivoted job (N+1) #3955

Description

@Aditya30ag

What's happening

Investigation.total_jobs looks like this:

@property
def total_jobs(self) -> int:
    return sum(job.get_descendant_count() for job in self.jobs.all()) + self.jobs.count()

get_descendant_count() comes from django-treebeard. For a job with no children it's free (checked in memory). But the moment a job has spawned a pivot (a child job), this fires a real COUNT(*) query one per pivoted job, every single time.

This property is exposed directly on InvestigationSerializer, which is used by GET /api/investigation/ the investigation list endpoint. So every time someone loads that page, the number of extra queries grows with how many jobs in the results have pivots.

How I confirmed it

Ran this locally and watched the query count:

Setup Extra COUNT queries
1 job, no pivots 0
Same job gets 1 pivot child 1
Same job gets 2 more children (still 1 pivoted job) 1
A second job also gets a pivot 2

So it's not "one query per child" it's one query per pivoted job, and it adds up linearly as more jobs in the list have pivots.

Why it matters

Pivots are a core IntelOwl feature. Any real instance using them will pay this cost on every investigation list load it just doesn't show up with small/simple test data, which is probably why it's gone unnoticed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions