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:
- Go to
http://gdk.test:3000/admin/background_jobs - Go to
Crontab. - click
Enqueue nowforvulnerability_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