Skip to content

Add namespace statistics adjustment service

What does this MR do and why?

Adds a namespace level adjustment service that calculates vulnerability statistics based on the existing project level statistics using traversal_ids. It uses the existing scheduled worker to trigger the calculations.

Relevant issue

Add a scheduled job to calculate namespace leve... (#521325 - closed) • Gal Katz • 17.11

How to set up and validate locally

1. Make sure the new table exists
bin/rails db:migrate VERSION=20250224125821
2. Run the background job:
  1. Go to http://gdk.test:3000/admin/background_jobs
  2. Go to Cron tab.
  3. click Enqueue now for vulnerability_namespace_statistics_schedule_worker

OR use one of****:

Vulnerabilities::NamespaceStatistics::ScheduleWorker.new.perform
Vulnerabilities::NamespaceStatistics::AdjustmentService.execute([<YOUR_FAVORITE_NAMESPACE_ID>])

Query plans

NamespaceStatistics::AdjustmentService

Raw SQL
INSERT INTO vulnerability_namespace_statistics (total, info, unknown, low, medium, high, critical, traversal_ids, namespace_id, created_at, updated_at) ( WITH namespace_data (
        namespace_id, traversal_ids, next_traversal_id
) AS (
            VALUES (9970, ARRAY[9970]::bigint[], ARRAY[9971]::bigint[]))
            SELECT
                SUM(total) AS total,
                SUM(info) AS info,
                SUM(unknown) AS unknown,
                SUM(low) AS low,
                SUM(medium) AS medium,
                SUM(high) AS high,
                SUM(critical) AS critical,
                namespace_data.traversal_ids AS traversal_ids,
                namespace_data.namespace_id AS namespace_id,
                now() AS created_at,
                now() AS updated_at
            FROM
                vulnerability_statistics,
                namespace_data
            WHERE
                vulnerability_statistics.archived = FALSE
                AND vulnerability_statistics.traversal_ids >= namespace_data.traversal_ids
                AND vulnerability_statistics.traversal_ids < namespace_data.next_traversal_id
            GROUP BY
                namespace_data.traversal_ids,
                namespace_id)
    ON CONFLICT (namespace_id)
        DO UPDATE SET
            total = EXCLUDED.total,
            info = EXCLUDED.info,
            unknown = EXCLUDED.unknown,
            low = EXCLUDED.low,
            medium = EXCLUDED.medium,
            high = EXCLUDED.high,
            critical = EXCLUDED.critical,
            updated_at = EXCLUDED.updated_at,
            traversal_ids = EXCLUDED.traversal_ids
Query plan

See this explain.

ModifyTable on public.vulnerability_namespace_statistics  (cost=0.42..2265.99 rows=0 width=0) (actual time=681.751..681.753 rows=0 loops=1)
   Buffers: shared hit=150 read=1170 dirtied=166 written=4
   WAL: records=204 fpi=158 bytes=1049392
   I/O Timings: read=665.951 write=0.106
   ->  Subquery Scan on *SELECT*  (cost=0.42..2265.99 rows=1 width=92) (actual time=680.544..680.546 rows=1 loops=1)
         Buffers: shared hit=142 read=1167 dirtied=159
         WAL: records=196 fpi=158 bytes=1048688
         I/O Timings: read=665.319 write=0.000
         ->  Aggregate  (cost=0.42..2265.96 rows=1 width=108) (actual time=677.954..677.955 rows=1 loops=1)
               Buffers: shared hit=133 read=1162 dirtied=158
               WAL: records=195 fpi=158 bytes=1048589
               I/O Timings: read=662.883 write=0.000
               ->  Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade on public.vulnerability_statistics  (cost=0.42..2239.01 rows=1539 width=28) (actual time=5.024..677.059 rows=1349 loops=1)
                     Index Cond: ((vulnerability_statistics.traversal_ids >= '{9970}'::bigint[]) AND (vulnerability_statistics.traversal_ids < '{9971}'::bigint[]))
                     Buffers: shared hit=133 read=1162 dirtied=158
                     WAL: records=195 fpi=158 bytes=1048589
                     I/O Timings: read=662.883 write=0.000
Settings: seq_page_cost = '4', work_mem = '100MB', random_page_cost = '1.5', jit = 'off', effective_cache_size = '472585MB'

NamespaceStatistics::ScheduleWorker

Raw SQL
SELECT "vulnerability_statistics"."traversal_ids"
FROM "vulnerability_statistics"
WHERE "vulnerability_statistics"."archived" = FALSE
  AND (traversal_ids IN
       ('{9970}',
        '{6086725,6703050,63193220,64676474,89519966}',
        '{9970,96981785,97058478,98867842,100569068}',
        '{9970,11787569,60126506,60423513}',
        '{9970,11787569,60126506,60423513,68178409}',
        '{9970,11787569,60126506,60423513,67986786}',
        '{9970,11787569,60126506,60423513,58583601}',
        '{9970,11787569,60126506,60423513,59967840}',
        '{9970,11787569,60126506,60423513,44324145}',
        '{9970,11787569,60126506,60423513,44324654}',
        '{9970,11787569,60126506,60423513,62759333}',
        '{9970,11787569,60126506,60423513,54511349}',
        '{9970,11787569,60126506,60423513,68178409}',
        '{9970,11787569,60126506,60423513,67955190}',
        '{9970,11787569,60126506,60423513,67949308}',
        '{9970,11787569,60126506,60423513,66686979}',
        '{9970,11787569,60126506,60423513,54511349}',
        '{9970,11787569,60126506,60423513,52019572}'))
Query plan

See this explain.

 Bitmap Heap Scan on public.vulnerability_statistics  (cost=41.00..328.19 rows=157 width=47) (actual time=20.449..133.413 rows=177 loops=1)
   Buffers: shared hit=50 read=185 dirtied=12
   WAL: records=14 fpi=12 bytes=92450
   I/O Timings: read=22.432 write=0.000
   ->  BitmapOr  (cost=41.00..41.00 rows=157 width=0) (actual time=18.459..18.466 rows=0 loops=1)
         Buffers: shared hit=50 read=16
         I/O Timings: read=18.147 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=13.215..13.215 rows=35 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970}'::bigint[])
               Buffers: shared hit=3 read=9
               I/O Timings: read=13.060 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=1.579..1.579 rows=32 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{6086725,6703050,63193220,64676474,89519966}'::bigint[])
               Buffers: shared hit=1 read=2
               I/O Timings: read=1.535 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=1.290..1.290 rows=1 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,96981785,97058478,98867842,100569068}'::bigint[])
               Buffers: shared hit=2 read=1
               I/O Timings: read=1.266 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..7.23 rows=107 width=0) (actual time=2.328..2.328 rows=109 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513}'::bigint[])
               Buffers: shared hit=2 read=4
               I/O Timings: read=2.286 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.006..0.006 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,68178409}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,67986786}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,58583601}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,59967840}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,44324145}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,44324654}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,62759333}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,54511349}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.002..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,68178409}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,67955190}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,67949308}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,66686979}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,54511349}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
         ->  Bitmap Index Scan using idx_vulnerability_statistics_on_traversal_ids_and_letter_grade  (cost=0.00..1.95 rows=3 width=0) (actual time=0.003..0.003 rows=0 loops=1)
               Index Cond: (vulnerability_statistics.traversal_ids = '{9970,11787569,60126506,60423513,52019572}'::bigint[])
               Buffers: shared hit=3
               I/O Timings: read=0.000 write=0.000
Settings: jit = 'off', effective_cache_size = '472585MB', seq_page_cost = '4', work_mem = '100MB', random_page_cost = '1.5'
Edited by Gal Katz

Merge request reports

Loading