Skip to content

Enable design and wiki events on contribution calendar

Alex Kalderimis requested to merge ajk-236005-design-activity-in-heatmap into master

What does this MR do?

Issues #236005 and #237855.

Known changes in behaviour

So previously there was a single query for commented actions (i.e. notes added), which was guarded by the :merge_requests feature.

This appears to me to be a bug - it has the consequence that if I make a comment on an issue, then my contribution is not counted if the project does not have merge-requests enabled.

To preserve the MR gating aspect, I split this into two queries - one for all comments on MRs, and one for all comments on other things, applying the :merge_requests feature gating only the the former. This now means that if I comment on an issue, and the project does not have issues enabled, then my contribution will be counted. But a) how did I comment on the issue anyway? and b) it would have in previous code, if merge requests were enabled and issues were not.

SQL queries

The Event.contributions query is used in ContributionsCalendar.events_by_date where it produces the following SQL (see https://explain.depesz.com/s/DYmV):

-- using my user_id so that we hit gitlab-org/gitlab as one of the projects
SELECT "events".* FROM "events" WHERE (action = 5
OR (action = 1 AND target_type IN ('MergeRequest','Issue','DesignManagement::Design','WikiPage::Meta'))
OR (action = 3 AND target_type IN ('MergeRequest','Issue'))
OR (action = 7 AND target_type IN ('MergeRequest'))
OR (action = 2 AND target_type IN ('DesignManagement::Design','WikiPage::Meta'))
OR (action = 6 AND target_type IN ('Note'))
) AND "events"."author_id" = 3614858 AND "events"."created_at" BETWEEN '2020-08-16 00:00:00' AND '2020-08-16 23:59:59.999999' AND "events"."project_id" IN (SELECT "projects"."id" FROM ((SELECT "projects".* FROM "projects" WHERE "projects"."id" IN (SELECT DISTINCT "events"."project_id" FROM "events" WHERE (action = 5
OR (action = 1 AND target_type IN ('MergeRequest','Issue','DesignManagement::Design','WikiPage::Meta'))
OR (action = 3 AND target_type IN ('MergeRequest','Issue'))
OR (action = 7 AND target_type IN ('MergeRequest'))
OR (action = 2 AND target_type IN ('DesignManagement::Design','WikiPage::Meta'))
OR (action = 6 AND target_type IN ('Note'))
) AND "events"."author_id" = 3614858 AND (created_at > '2019-08-26 10:09:35.999128')) AND "projects"."id" IN (SELECT "projects"."id" FROM "projects" INNER JOIN "project_authorizations" ON "projects"."id" = "project_authorizations"."project_id" WHERE "project_authorizations"."user_id" = 86))
UNION
(SELECT "projects".* FROM "projects" WHERE "projects"."id" IN (SELECT DISTINCT "events"."project_id" FROM "events" WHERE (action = 5
OR (action = 1 AND target_type IN ('MergeRequest','Issue','DesignManagement::Design','WikiPage::Meta'))
OR (action = 3 AND target_type IN ('MergeRequest','Issue'))
OR (action = 7 AND target_type IN ('MergeRequest'))
OR (action = 2 AND target_type IN ('DesignManagement::Design','WikiPage::Meta'))
OR (action = 6 AND target_type IN ('Note'))
) AND "events"."author_id" = 3614858 AND (created_at > '2019-08-26 10:09:36.014385')) AND "projects"."visibility_level" IN (0, 10, 20))) projects ORDER BY "projects"."id" DESC)

which explains as follows:

 Hash Join  (cost=8029.52..8032.28 rows=1 width=72) (actual time=11.898..11.899 rows=0 loops=1)
   Hash Cond: (projects.id = events.project_id)
   Buffers: shared hit=1 read=3
   I/O Timings: read=11.795
   ->  HashAggregate  (cost=8025.88..8027.88 rows=200 width=4) (actual time=0.000..0.000 rows=0 loops=0)
         Group Key: projects.id
         ->  Sort  (cost=8017.13..8018.59 rows=583 width=4) (actual time=0.000..0.000 rows=0 loops=0)
               Sort Key: projects.id DESC
               ->  Subquery Scan on projects  (cost=7978.69..7990.35 rows=583 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                     ->  HashAggregate  (cost=7978.69..7984.52 rows=583 width=4316) (actual time=0.000..0.000 rows=0 loops=0)
                           Group Key: projects_1.id, projects_1.name, projects_1.path, projects_1.description, projects_1.created_at, projects_1.updated_at, projects_1.creator_id, projects_1.namespace_id, projects_1.last_activity_at, projects_1.import_url, projects_1.visibility_level, projects_1.archived, projects_1.merge_requests_template, projects_1.star_count, projects_1.merge_requests_rebase_enabled, projects_1.import_type, projects_1.import_source, projects_1.avatar, projects_1.approvals_before_merge, projects_1.reset_approvals_on_push, projects_1.merge_requests_ff_only_enabled, projects_1.issues_template, projects_1.mirror, projects_1.mirror_last_update_at, projects_1.mirror_last_successful_update_at, projects_1.mirror_user_id, projects_1.shared_runners_enabled, projects_1.runners_token, projects_1.build_coverage_regex, projects_1.build_allow_git_fetch, projects_1.build_timeout, projects_1.mirror_trigger_builds, projects_1.public_builds, projects_1.pending_delete, projects_1.last_repository_check_failed, projects_1.last_repository_check_at, projects_1.container_registry_enabled, projects_1.only_allow_merge_if_pipeline_succeeds, projects_1.has_external_issue_tracker, projects_1.repository_storage, projects_1.request_access_enabled, projects_1.has_external_wiki, projects_1.repository_read_only, projects_1.lfs_enabled, projects_1.description_html, projects_1.only_allow_merge_if_all_discussions_are_resolved, projects_1.repository_size_limit, projects_1.service_desk_enabled, projects_1.printing_merge_request_link_enabled, projects_1.auto_cancel_pending_pipelines, projects_1.cached_markdown_version, projects_1.last_repository_updated_at, projects_1.ci_config_path, projects_1.disable_overriding_approvers_per_merge_request, projects_1.delete_error, projects_1.storage_version, projects_1.resolve_outdated_diff_discussions, projects_1.remote_mirror_available_overridden, projects_1.only_mirror_protected_branches, projects_1.pull_mirror_available_overridden, projects_1.jobs_cache_index, projects_1.external_authorization_classification_label, projects_1.mirror_overwrites_diverged_branches, projects_1.external_webhook_token, projects_1.pages_https_only, projects_1.packages_enabled, projects_1.merge_requests_author_approval, projects_1.pool_repository_id, projects_1.runners_token_encrypted, projects_1.bfg_object_map, projects_1.detected_repository_languages, projects_1.merge_requests_disable_committers_approval, projects_1.require_password_to_approve, projects_1.emails_disabled, projects_1.max_pages_size, projects_1.max_artifacts_size, projects_1.pull_mirror_branch_prefix, projects_1.remove_source_branch_after_merge, projects_1.marked_for_deletion_at, projects_1.marked_for_deletion_by_user_id, projects_1.suggestion_commit_message, projects_1.autoclose_referenced_issues
                           ->  Append  (cost=1219.55..7859.18 rows=583 width=4316) (actual time=0.000..0.000 rows=0 loops=0)
                                 ->  Nested Loop Semi Join  (cost=1219.55..4340.99 rows=1 width=723) (actual time=0.000..0.000 rows=0 loops=0)
                                       ->  Nested Loop  (cost=1218.55..3506.96 rows=660 width=727) (actual time=0.000..0.000 rows=0 loops=0)
                                             ->  HashAggregate  (cost=1218.11..1224.71 rows=660 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Group Key: events_1.project_id
                                                   ->  Index Scan using index_events_on_author_id_and_created_at on public.events events_1  (cost=0.57..1216.46 rows=660 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                                                         Index Cond: ((events_1.author_id = 3614858) AND (events_1.created_at > '2019-08-26 10:09:35.999128+00'::timestamp with time zone))
                                                         Filter: ((events_1.action = 5) OR ((events_1.action = 1) AND ((events_1.target_type)::text = ANY ('{MergeRequest,Issue,DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events_1.action = 3) AND ((events_1.target_type)::text = ANY ('{MergeRequest,Issue}'::text[]))) OR ((events_1.action = 7) AND ((events_1.target_type)::text = 'MergeRequest'::text)) OR ((events_1.action = 2) AND ((events_1.target_type)::text = ANY ('{DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events_1.action = 6) AND ((events_1.target_type)::text = 'Note'::text)))
                                                         Rows Removed by Filter: 0
                                             ->  Index Scan using projects_pkey on public.projects projects_1  (cost=0.43..3.45 rows=1 width=723) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Index Cond: (projects_1.id = events_1.project_id)
                                       ->  Nested Loop  (cost=1.01..1.25 rows=1 width=8) (actual time=0.000..0.000 rows=0 loops=0)
                                             ->  Index Only Scan using projects_pkey on public.projects projects_2  (cost=0.43..0.47 rows=1 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Index Cond: (projects_2.id = projects_1.id)
                                                   Heap Fetches: 0
                                             ->  Index Only Scan using index_project_authorizations_on_user_id_project_id_access_level on public.project_authorizations  (cost=0.57..0.78 rows=1 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Index Cond: ((project_authorizations.user_id = 86) AND (project_authorizations.project_id = projects_2.id))
                                                   Heap Fetches: 0
                                 ->  Nested Loop  (cost=1218.55..3509.44 rows=582 width=723) (actual time=0.000..0.000 rows=0 loops=0)
                                       ->  HashAggregate  (cost=1218.11..1224.71 rows=660 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                                             Group Key: events_2.project_id
                                             ->  Index Scan using index_events_on_author_id_and_created_at on public.events events_2  (cost=0.57..1216.46 rows=660 width=4) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Index Cond: ((events_2.author_id = 3614858) AND (events_2.created_at > '2019-08-26 10:09:36.014385+00'::timestamp with time zone))
                                                   Filter: ((events_2.action = 5) OR ((events_2.action = 1) AND ((events_2.target_type)::text = ANY ('{MergeRequest,Issue,DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events_2.action = 3) AND ((events_2.target_type)::text = ANY ('{MergeRequest,Issue}'::text[]))) OR ((events_2.action = 7) AND ((events_2.target_type)::text = 'MergeRequest'::text)) OR ((events_2.action = 2) AND ((events_2.target_type)::text = ANY ('{DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events_2.action = 6) AND ((events_2.target_type)::text = 'Note'::text)))
                                                   Rows Removed by Filter: 0
                                       ->  Index Scan using projects_pkey on public.projects projects_3  (cost=0.43..3.45 rows=1 width=723) (actual time=0.000..0.000 rows=0 loops=0)
                                             Index Cond: (projects_3.id = events_2.project_id)
                                             Filter: (projects_3.visibility_level = ANY ('{0,10,20}'::integer[]))
                                             Rows Removed by Filter: 0
   ->  Hash  (cost=3.63..3.63 rows=1 width=72) (actual time=11.894..11.894 rows=0 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 8kB
         Buffers: shared hit=1 read=3
         I/O Timings: read=11.795
         ->  Index Scan using index_events_on_author_id_and_created_at on public.events  (cost=0.57..3.63 rows=1 width=72) (actual time=11.893..11.893 rows=0 loops=1)
               Index Cond: ((events.author_id = 3614858) AND (events.created_at >= '2020-08-16 00:00:00+00'::timestamp with time zone) AND (events.created_at <= '2020-08-16 23:59:59.999999+00'::timestamp with time zone))
               Filter: ((events.action = 5) OR ((events.action = 1) AND ((events.target_type)::text = ANY ('{MergeRequest,Issue,DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events.action = 3) AND ((events.target_type)::text = ANY ('{MergeRequest,Issue}'::text[]))) OR ((events.action = 7) AND ((events.target_type)::text = 'MergeRequest'::text)) OR ((events.action = 2) AND ((events.target_type)::text = ANY ('{DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events.action = 6) AND ((events.target_type)::text = 'Note'::text)))
               Rows Removed by Filter: 0
               Buffers: shared hit=1 read=3
               I/O Timings: read=11.795

Summary:

Time: 16.410 ms
  - planning: 3.951 ms
  - execution: 12.459 ms
    - I/O read: 11.795 ms
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 1 (~8.00 KiB) from the buffer pool
  - reads: 3 (~24.00 KiB) from the OS file cache, including disk I/O
  - dirtied: 0
  - writes: 0

The other modified query is in activity_dates which produces the following SQL:

WITH "user_events_in_range" AS (SELECT "events"."id", "events"."project_id", "events"."target_type", "events"."target_id", "events"."action", date(events.created_at + INTERVAL '0 seconds') as date FROM "events" WHERE (action = 5
OR (action = 1 AND target_type IN ('MergeRequest','Issue','DesignManagement::Design','WikiPage::Meta'))
OR (action = 3 AND target_type IN ('MergeRequest','Issue'))
OR (action = 7 AND target_type IN ('MergeRequest'))
OR (action = 2 AND target_type IN ('DesignManagement::Design','WikiPage::Meta'))
OR (action = 6 AND target_type IN ('Note'))
) AND "events"."created_at" >= '2019-09-04 00:00:00'
  AND "events"."created_at" <= '2020-09-04 23:59:59.999999'
  AND "events"."author_id" = 3614858
)

SELECT date as key, sum(total_amount)::int as value

FROM ((SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" WHERE "events"."project_id" IN (SELECT "projects"."id" FROM "projects" LEFT JOIN project_features ON projects.id = project_features.project_id WHERE "projects"."id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) AND ("project_features"."repository_access_level" > 0 OR "project_features"."repository_access_level" IS NULL)) GROUP BY "events"."project_id", "target_type", "action", "date" HAVING "events"."action" = 5)
UNION ALL
(SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" WHERE "events"."project_id" IN (SELECT "projects"."id" FROM "projects" LEFT JOIN project_features ON projects.id = project_features.project_id WHERE "projects"."id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) AND ("project_features"."issues_access_level" > 0 OR "project_features"."issues_access_level" IS NULL)) GROUP BY "events"."project_id", "target_type", "action", "date" HAVING "events"."action" IN (1, 3) AND "events"."target_type" = 'Issue')
UNION ALL
(SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" WHERE "events"."project_id" IN (SELECT "projects"."id" FROM "projects" LEFT JOIN project_features ON projects.id = project_features.project_id WHERE "projects"."id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) AND ("project_features"."wiki_access_level" > 0 OR "project_features"."wiki_access_level" IS NULL)) GROUP BY "events"."project_id", "target_type", "action", "date" HAVING "events"."action" IN (1, 2) AND "events"."target_type" = 'WikiPage::Meta')
UNION ALL
(SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" WHERE "events"."project_id" IN (SELECT "projects"."id" FROM "projects" LEFT JOIN project_features ON projects.id = project_features.project_id WHERE "projects"."id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) AND ("project_features"."merge_requests_access_level" > 0 OR "project_features"."merge_requests_access_level" IS NULL)) GROUP BY "events"."project_id", "target_type", "action", "date" HAVING "events"."action" IN (7, 1, 3) AND "events"."target_type" = 'MergeRequest')
UNION ALL
(SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" WHERE "events"."project_id" IN (SELECT "projects"."id" FROM "projects" LEFT JOIN project_features ON projects.id = project_features.project_id WHERE "projects"."id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) AND ("project_features"."issues_access_level" > 0 OR "project_features"."issues_access_level" IS NULL)) GROUP BY "events"."project_id", "target_type", "action", "date" HAVING "events"."action" IN (1, 2) AND "events"."target_type" = 'DesignManagement::Design')
UNION ALL
(SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" INNER JOIN notes ON target_type = 'Note' AND target_id = notes.id WHERE "events"."project_id" IN (SELECT "projects"."id" FROM "projects" LEFT JOIN project_features ON projects.id = project_features.project_id WHERE "projects"."id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) AND ("project_features"."merge_requests_access_level" > 0 OR "project_features"."merge_requests_access_level" IS NULL)) GROUP BY "events"."project_id", "target_type", "action", "date", notes.noteable_type HAVING (action = 6 AND notes.noteable_type = 'MergeRequest'))
UNION ALL
(SELECT "events"."project_id", "target_type", "action", "date", COUNT("events"."id") AS total_amount FROM "user_events_in_range" AS "events" INNER JOIN notes ON target_type = 'Note' AND target_id = notes.id WHERE "events"."project_id" IN (278964, 53, 42, 41, 40, 39, 38, 37, 36, 35) GROUP BY "events"."project_id", "target_type", "action", "date", notes.noteable_type HAVING (action = 6 AND notes.noteable_type != 'MergeRequest'))) events GROUP BY date

Which has the following monster of an explain (https://explain.depesz.com/s/PBIK):

   Group Key: "*SELECT* 1".date
   Buffers: shared hit=15317 read=7926 dirtied=118
   I/O Timings: read=1160.433
   CTE user_events_in_range
     ->  Index Scan using index_events_on_author_id_and_created_at on public.events events_7  (cost=0.57..1250.61 rows=675 width=27) (actual time=15.142..742.475 rows=3970 loops=1)
           Index Cond: ((events_7.author_id = 3614858) AND (events_7.created_at >= '2019-09-04 00:00:00+00'::timestamp with time zone) AND (events_7.created_at <= '2020-09-04 23:59:59.999999+00'::timestamp with time zone))
           Filter: ((events_7.action = 5) OR ((events_7.action = 1) AND ((events_7.target_type)::text = ANY ('{MergeRequest,Issue,DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events_7.action = 3) AND ((events_7.target_type)::text = ANY ('{MergeRequest,Issue}'::text[]))) OR ((events_7.action = 7) AND ((events_7.target_type)::text = 'MergeRequest'::text)) OR ((events_7.action = 2) AND ((events_7.target_type)::text = ANY ('{DesignManagement::Design,WikiPage::Meta}'::text[]))) OR ((events_7.action = 6) AND ((events_7.target_type)::text = 'Note'::text)))
           Rows Removed by Filter: 2328
           Buffers: shared hit=443 read=5857 dirtied=80
           I/O Timings: read=707.939
   ->  Sort  (cost=311.76..311.78 rows=8 width=12) (actual time=1252.562..1252.619 rows=684 loops=1)
         Sort Key: "*SELECT* 1".date
         Sort Method: quicksort  Memory: 57kB
         Buffers: shared hit=15317 read=7926 dirtied=118
         I/O Timings: read=1160.433
         ->  Append  (cost=71.35..311.64 rows=8 width=12) (actual time=787.594..1252.316 rows=684 loops=1)
               Buffers: shared hit=15314 read=7926 dirtied=118
               I/O Timings: read=1160.433
               ->  Subquery Scan on *SELECT* 1  (cost=71.35..71.42 rows=2 width=12) (actual time=787.592..788.490 rows=227 loops=1)
                     Buffers: shared hit=498 read=5874 dirtied=81
                     I/O Timings: read=740.639
                     ->  Aggregate  (cost=71.35..71.40 rows=2 width=50) (actual time=787.591..788.446 rows=227 loops=1)
                           Group Key: events.project_id, events.target_type, events.action, events.date
                           Buffers: shared hit=498 read=5874 dirtied=81
                           I/O Timings: read=740.639
                           ->  Sort  (cost=71.35..71.35 rows=2 width=46) (actual time=787.580..787.724 rows=1899 loops=1)
                                 Sort Key: events.project_id, events.target_type, events.date
                                 Sort Method: quicksort  Memory: 138kB
                                 Buffers: shared hit=498 read=5874 dirtied=81
                                 I/O Timings: read=740.639
                                 ->  Nested Loop Semi Join  (cost=0.87..71.34 rows=2 width=46) (actual time=53.609..785.917 rows=1899 loops=1)
                                       Buffers: shared hit=492 read=5874 dirtied=81
                                       I/O Timings: read=740.639
                                       ->  CTE Scan on user_events_in_range events  (cost=0.00..15.19 rows=3 width=46) (actual time=15.148..747.940 rows=2076 loops=1)
                                             Filter: (events.action = 5)
                                             Rows Removed by Filter: 1894
                                             Buffers: shared hit=443 read=5857 dirtied=80
                                             I/O Timings: read=707.939
                                       ->  Materialize  (cost=0.87..55.72 rows=10 width=4) (actual time=0.004..0.017 rows=6 loops=2076)
                                             Buffers: shared hit=49 read=17 dirtied=1
                                             I/O Timings: read=32.701
                                             ->  Nested Loop Left Join  (cost=0.87..55.67 rows=10 width=4) (actual time=7.774..33.061 rows=6 loops=1)
                                                   Filter: ((project_features.repository_access_level > 0) OR (project_features.repository_access_level IS NULL))
                                                   Rows Removed by Filter: 0
                                                   Buffers: shared hit=49 read=17 dirtied=1
                                                   I/O Timings: read=32.701
                                                   ->  Index Only Scan using projects_pkey on public.projects  (cost=0.43..21.02 rows=10 width=4) (actual time=4.299..17.469 rows=6 loops=1)
                                                         Index Cond: (projects.id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[]))
                                                         Heap Fetches: 3
                                                         Buffers: shared hit=32 read=10 dirtied=1
                                                         I/O Timings: read=17.260
                                                   ->  Index Scan using index_project_features_on_project_id on public.project_features  (cost=0.43..3.45 rows=1 width=8) (actual time=2.591..2.591 rows=1 loops=6)
                                                         Index Cond: (projects.id = project_features.project_id)
                                                         Buffers: shared hit=17 read=7
                                                         I/O Timings: read=15.441
               ->  Subquery Scan on *SELECT* 2  (cost=41.32..41.36 rows=1 width=12) (actual time=1.881..1.979 rows=90 loops=1)
                     Buffers: shared hit=1116
                     ->  Aggregate  (cost=41.32..41.35 rows=1 width=50) (actual time=1.880..1.960 rows=90 loops=1)
                           Group Key: events_1.project_id, events_1.target_type, events_1.action, events_1.date
                           Buffers: shared hit=1116
                           ->  Sort  (cost=41.32..41.33 rows=1 width=46) (actual time=1.874..1.886 rows=139 loops=1)
                                 Sort Key: events_1.project_id, events_1.action, events_1.date
                                 Sort Method: quicksort  Memory: 35kB
                                 Buffers: shared hit=1116
                                 ->  Nested Loop Semi Join  (cost=0.87..41.31 rows=1 width=46) (actual time=0.075..1.675 rows=139 loops=1)
                                       Buffers: shared hit=1113
                                       ->  CTE Scan on user_events_in_range events_1  (cost=0.00..16.88 rows=1 width=46) (actual time=0.008..0.725 rows=164 loops=1)
                                             Filter: ((events_1.action = ANY ('{1,3}'::integer[])) AND ((events_1.target_type)::text = 'Issue'::text))
                                             Rows Removed by Filter: 3806
                                       ->  Nested Loop Left Join  (cost=0.87..24.43 rows=1 width=4) (actual time=0.005..0.005 rows=1 loops=164)
                                             Filter: ((project_features_1.issues_access_level > 0) OR (project_features_1.issues_access_level IS NULL))
                                             Rows Removed by Filter: 0
                                             Buffers: shared hit=1113
                                             ->  Index Only Scan using projects_pkey on public.projects projects_1  (cost=0.43..20.96 rows=1 width=4) (actual time=0.002..0.002 rows=1 loops=164)
                                                   Index Cond: ((projects_1.id = events_1.project_id) AND (projects_1.id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[])))
                                                   Heap Fetches: 139
                                                   Buffers: shared hit=557
                                             ->  Index Scan using index_project_features_on_project_id on public.project_features project_features_1  (cost=0.43..3.45 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=139)
                                                   Index Cond: (projects_1.id = project_features_1.project_id)
                                                   Buffers: shared hit=556
               ->  Subquery Scan on *SELECT* 3  (cost=41.32..41.36 rows=1 width=12) (actual time=2.471..2.471 rows=0 loops=1)
                     ->  Aggregate  (cost=41.32..41.35 rows=1 width=50) (actual time=2.470..2.470 rows=0 loops=1)
                           Group Key: events_2.project_id, events_2.target_type, events_2.action, events_2.date
                           ->  Sort  (cost=41.32..41.33 rows=1 width=46) (actual time=2.468..2.468 rows=0 loops=1)
                                 Sort Key: events_2.project_id, events_2.action, events_2.date
                                 Sort Method: quicksort  Memory: 25kB
                                 ->  Nested Loop Semi Join  (cost=0.87..41.31 rows=1 width=46) (actual time=2.453..2.453 rows=0 loops=1)
                                       ->  CTE Scan on user_events_in_range events_2  (cost=0.00..16.88 rows=1 width=46) (actual time=0.242..0.870 rows=701 loops=1)
                                             Filter: ((events_2.action = ANY ('{1,2}'::integer[])) AND ((events_2.target_type)::text = 'WikiPage::Meta'::text))
                                             Rows Removed by Filter: 3269
                                       ->  Nested Loop Left Join  (cost=0.87..24.43 rows=1 width=4) (actual time=0.002..0.002 rows=0 loops=701)
                                             Filter: ((project_features_2.wiki_access_level > 0) OR (project_features_2.wiki_access_level IS NULL))
                                             Rows Removed by Filter: 0
                                             ->  Index Only Scan using projects_pkey on public.projects projects_2  (cost=0.43..20.96 rows=1 width=4) (actual time=0.001..0.001 rows=0 loops=701)
                                                   Index Cond: ((projects_2.id = events_2.project_id) AND (projects_2.id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[])))
                                                   Heap Fetches: 0
                                             ->  Index Scan using index_project_features_on_project_id on public.project_features project_features_2  (cost=0.43..3.45 rows=1 width=8) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Index Cond: (projects_2.id = project_features_2.project_id)
               ->  Subquery Scan on *SELECT* 4  (cost=42.16..42.20 rows=1 width=12) (actual time=2.002..2.103 rows=93 loops=1)
                     Buffers: shared hit=1033
                     ->  Aggregate  (cost=42.16..42.19 rows=1 width=50) (actual time=2.001..2.084 rows=93 loops=1)
                           Group Key: events_3.project_id, events_3.target_type, events_3.action, events_3.date
                           Buffers: shared hit=1033
                           ->  Sort  (cost=42.16..42.17 rows=1 width=46) (actual time=1.994..2.005 rows=129 loops=1)
                                 Sort Key: events_3.project_id, events_3.action, events_3.date
                                 Sort Method: quicksort  Memory: 35kB
                                 Buffers: shared hit=1033
                                 ->  Nested Loop Semi Join  (cost=0.87..42.15 rows=1 width=46) (actual time=0.078..1.818 rows=129 loops=1)
                                       Buffers: shared hit=1033
                                       ->  CTE Scan on user_events_in_range events_3  (cost=0.00..17.72 rows=1 width=46) (actual time=0.007..0.652 rows=141 loops=1)
                                             Filter: (((events_3.target_type)::text = 'MergeRequest'::text) AND (events_3.action = ANY ('{7,1,3}'::integer[])))
                                             Rows Removed by Filter: 3829
                                       ->  Nested Loop Left Join  (cost=0.87..24.43 rows=1 width=4) (actual time=0.008..0.008 rows=1 loops=141)
                                             Filter: ((project_features_3.merge_requests_access_level > 0) OR (project_features_3.merge_requests_access_level IS NULL))
                                             Rows Removed by Filter: 0
                                             Buffers: shared hit=1033
                                             ->  Index Only Scan using projects_pkey on public.projects projects_3  (cost=0.43..20.96 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=141)
                                                   Index Cond: ((projects_3.id = events_3.project_id) AND (projects_3.id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[])))
                                                   Heap Fetches: 129
                                                   Buffers: shared hit=517
                                             ->  Index Scan using index_project_features_on_project_id on public.project_features project_features_3  (cost=0.43..3.45 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=129)
                                                   Index Cond: (projects_3.id = project_features_3.project_id)
                                                   Buffers: shared hit=516
               ->  Subquery Scan on *SELECT* 5  (cost=41.32..41.36 rows=1 width=12) (actual time=0.676..0.676 rows=0 loops=1)
                     ->  Aggregate  (cost=41.32..41.35 rows=1 width=50) (actual time=0.675..0.675 rows=0 loops=1)
                           Group Key: events_4.project_id, events_4.target_type, events_4.action, events_4.date
                           ->  Sort  (cost=41.32..41.33 rows=1 width=46) (actual time=0.673..0.673 rows=0 loops=1)
                                 Sort Key: events_4.project_id, events_4.action, events_4.date
                                 Sort Method: quicksort  Memory: 25kB
                                 ->  Nested Loop Semi Join  (cost=0.87..41.31 rows=1 width=46) (actual time=0.661..0.661 rows=0 loops=1)
                                       ->  CTE Scan on user_events_in_range events_4  (cost=0.00..16.88 rows=1 width=46) (actual time=0.357..0.641 rows=1 loops=1)
                                             Filter: ((events_4.action = ANY ('{1,2}'::integer[])) AND ((events_4.target_type)::text = 'DesignManagement::Design'::text))
                                             Rows Removed by Filter: 3969
                                       ->  Nested Loop Left Join  (cost=0.87..24.43 rows=1 width=4) (actual time=0.018..0.019 rows=0 loops=1)
                                             Filter: ((project_features_4.issues_access_level > 0) OR (project_features_4.issues_access_level IS NULL))
                                             Rows Removed by Filter: 0
                                             ->  Index Only Scan using projects_pkey on public.projects projects_4  (cost=0.43..20.96 rows=1 width=4) (actual time=0.017..0.017 rows=0 loops=1)
                                                   Index Cond: ((projects_4.id = events_4.project_id) AND (projects_4.id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[])))
                                                   Heap Fetches: 0
                                             ->  Index Scan using index_project_features_on_project_id on public.project_features project_features_4  (cost=0.43..3.45 rows=1 width=8) (actual time=0.000..0.000 rows=0 loops=0)
                                                   Index Cond: (projects_4.id = project_features_4.project_id)
               ->  Subquery Scan on *SELECT* 6  (cost=44.91..44.95 rows=1 width=12) (actual time=450.001..450.515 rows=185 loops=1)
                     Buffers: shared hit=8580 read=2052 dirtied=37
                     I/O Timings: read=419.794
                     ->  Aggregate  (cost=44.91..44.94 rows=1 width=60) (actual time=449.999..450.476 rows=185 loops=1)
                           Group Key: events_5.project_id, events_5.target_type, events_5.action, events_5.date, notes.noteable_type
                           Buffers: shared hit=8580 read=2052 dirtied=37
                           I/O Timings: read=419.794
                           ->  Sort  (cost=44.91..44.92 rows=1 width=56) (actual time=449.990..450.049 rows=640 loops=1)
                                 Sort Key: events_5.project_id, events_5.date
                                 Sort Method: quicksort  Memory: 75kB
                                 Buffers: shared hit=8580 read=2052 dirtied=37
                                 I/O Timings: read=419.794
                                 ->  Nested Loop  (cost=1.44..44.90 rows=1 width=56) (actual time=3.519..448.874 rows=640 loops=1)
                                       Buffers: shared hit=8580 read=2052 dirtied=37
                                       I/O Timings: read=419.794
                                       ->  Nested Loop Semi Join  (cost=0.87..41.31 rows=1 width=50) (actual time=0.056..15.997 rows=818 loops=1)
                                             Buffers: shared hit=6545
                                             ->  CTE Scan on user_events_in_range events_5  (cost=0.00..16.88 rows=1 width=50) (actual time=0.003..1.827 rows=887 loops=1)
                                                   Filter: (((events_5.target_type)::text = 'Note'::text) AND (events_5.action = 6))
                                                   Rows Removed by Filter: 3083
                                             ->  Nested Loop Left Join  (cost=0.87..24.43 rows=1 width=4) (actual time=0.015..0.015 rows=1 loops=887)
                                                   Filter: ((project_features_5.merge_requests_access_level > 0) OR (project_features_5.merge_requests_access_level IS NULL))
                                                   Rows Removed by Filter: 0
                                                   Buffers: shared hit=6545
                                                   ->  Index Only Scan using projects_pkey on public.projects projects_5  (cost=0.43..20.96 rows=1 width=4) (actual time=0.006..0.006 rows=1 loops=887)
                                                         Index Cond: ((projects_5.id = events_5.project_id) AND (projects_5.id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[])))
                                                         Heap Fetches: 818
                                                         Buffers: shared hit=3273
                                                   ->  Index Scan using index_project_features_on_project_id on public.project_features project_features_5  (cost=0.43..3.45 rows=1 width=8) (actual time=0.004..0.004 rows=1 loops=818)
                                                         Index Cond: (projects_5.id = project_features_5.project_id)
                                                         Buffers: shared hit=3272
                                       ->  Index Scan using notes_pkey on public.notes  (cost=0.57..3.59 rows=1 width=14) (actual time=0.528..0.528 rows=1 loops=818)
                                             Index Cond: (notes.id = events_5.target_id)
                                             Filter: ((notes.noteable_type)::text = 'MergeRequest'::text)
                                             Rows Removed by Filter: 0
                                             Buffers: shared hit=2035 read=2052 dirtied=37
                                             I/O Timings: read=419.794
               ->  Subquery Scan on *SELECT* 7  (cost=28.92..28.96 rows=1 width=12) (actual time=5.830..5.958 rows=89 loops=1)
                     Buffers: shared hit=4087
                     ->  Aggregate  (cost=28.92..28.95 rows=1 width=60) (actual time=5.829..5.941 rows=89 loops=1)
                           Group Key: events_6.project_id, events_6.target_type, events_6.action, events_6.date, notes_1.noteable_type
                           Buffers: shared hit=4087
                           ->  Sort  (cost=28.92..28.92 rows=1 width=56) (actual time=5.821..5.835 rows=175 loops=1)
                                 Sort Key: events_6.project_id, events_6.date, notes_1.noteable_type
                                 Sort Method: quicksort  Memory: 38kB
                                 Buffers: shared hit=4087
                                 ->  Nested Loop  (cost=0.57..28.91 rows=1 width=56) (actual time=0.042..5.697 rows=175 loops=1)
                                       Buffers: shared hit=4087
                                       ->  CTE Scan on user_events_in_range events_6  (cost=0.00..25.31 rows=1 width=50) (actual time=0.006..0.951 rows=818 loops=1)
                                             Filter: (((events_6.target_type)::text = 'Note'::text) AND (events_6.action = 6) AND (events_6.project_id = ANY ('{278964,53,42,41,40,39,38,37,36,35}'::integer[])))
                                             Rows Removed by Filter: 3152
                                       ->  Index Scan using notes_pkey on public.notes notes_1  (cost=0.57..3.59 rows=1 width=14) (actual time=0.005..0.005 rows=0 loops=818)
                                             Index Cond: (notes_1.id = events_6.target_id)
                                             Filter: ((notes_1.noteable_type)::text <> 'MergeRequest'::text)
                                             Rows Removed by Filter: 1
                                             Buffers: shared hit=4087

see https://explain.depesz.com/s/xiMA

and see for the query before the sum aggregation: https://explain.depesz.com/s/rWr3

Summary:

Time: 1.262 s
  - planning: 8.341 ms
  - execution: 1.254 s
    - I/O read: 1.160 s
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 15317 (~119.70 MiB) from the buffer pool
  - reads: 7926 (~61.90 MiB) from the OS file cache, including disk I/O
  - dirtied: 118 (~944.00 KiB)
  - writes: 0

This replaces a query with the following timings:

Time: 10.460 s
  - planning: 7.630 ms
  - execution: 10.452 s
    - I/O read: 10.324 s
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 18129 (~141.60 MiB) from the buffer pool
  - reads: 5876 (~45.90 MiB) from the OS file cache, including disk I/O
  - dirtied: 92 (~736.00 KiB)
  - writes: 0

See: old contribution calendar query

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • Label as security and @ mention @gitlab-com/gl-security/appsec
  • The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • Security reports checked/validated by a reviewer from the AppSec team
Edited by Alex Kalderimis

Merge request reports