Skip to content

Add support for ordering group runners by most active

What does this MR do and why?

  • Changelog: added

This MR adds support for checking for the most active group runners. It does so by changing ee/app/finders/ee/ci/runners_finder.rb and the Ci::Runner model to allow sorting by most_active_desc for group runners when a group is specified.

Part of #452185 (closed), #437849 (closed)

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

Scenario Screenshot
Using DIRECT membership image
Not specifying membership image
Using DESCENDANTS membership image

How to set up and validate locally

Numbered steps to set up and validate the change are strongly suggested.

  1. Create a group runner with the following tags: shell, gdk, mac, for instance on http://gdk.test:3000/groups/gitlab-org/-/runners

  2. Create a simple pipeline on a project inside that group:

    default:
      tags:
        - shell
        - gdk
        - mac
    
    before_script:
      - echo "Before script section"
      - echo "For example you might run an update here or install a build dependency"
      - echo "Or perhaps you might print out some debugging details"
    
    after_script:
      - echo "After script section"
      - echo "For example you might do some cleanup here"
    
    build1:
      stage: build
      script:
        - echo "Do your build here"
        - sleep 30
    
    test1:
      stage: test
      script:
        - echo "Do a test here"
        - echo "For example run a test suite"
        - sleep 30
    
    test2:
      stage: test
      script:
        - echo "Do another parallel test here"
        - echo "For example run a lint test"
        - sleep 60
    
    deploy1:
      stage: deploy
      script:
        - echo "Do your deploy here"
      environment: production
  3. Go to http://gdk.test:3000/-/graphql-explorer

  4. Run the following query:

    {
      group(fullPath: "gitlab-org") {
        runners(first: 5, type: GROUP_TYPE, membership: DIRECT, sort: MOST_ACTIVE_DESC) {
          nodes {
            id
            jobs(statuses: RUNNING) {
              count
            }
          }
        }
      }
    }

Database query plans

Query most active group runners on group ID 9970 (gitlab-org)

https://postgres.ai/console/gitlab/gitlab-production-ci-deprecated/sessions/27821/commands/86657

SELECT "ci_runners".*
FROM "ci_runners"
  INNER JOIN "ci_runner_namespaces" ON "ci_runner_namespaces"."runner_id" = "ci_runners"."id"
  INNER JOIN (
    SELECT "ci_running_builds"."runner_id", ROW_NUMBER() OVER (PARTITION BY runner_id ORDER BY runner_id) AS rn
    FROM "ci_running_builds"
    WHERE "ci_running_builds"."runner_type" = 2
      AND "ci_running_builds"."runner_owner_namespace_xid" = 9970) AS "limited_builds" ON "limited_builds"."runner_id" = "ci_runners"."id"
  AND "limited_builds".rn <= 1000
WHERE "ci_runner_namespaces"."namespace_id" = 9970
  AND "ci_runners"."runner_type" = 2
GROUP BY "ci_runners"."id"
ORDER BY COUNT(limited_builds.runner_id) DESC NULLS LAST, "ci_runners"."id" DESC
LIMIT 5 OFFSET 0
 Limit  (cost=103.23..103.23 rows=1 width=280) (actual time=1.049..1.053 rows=5 loops=1)
   Buffers: shared hit=666
   I/O Timings: read=0.000 write=0.000
   ->  Sort  (cost=103.23..103.23 rows=1 width=280) (actual time=1.048..1.050 rows=5 loops=1)
         Sort Key: (count(limited_builds.runner_id)) DESC NULLS LAST, ci_runners.id DESC
         Sort Method: quicksort  Memory: 29kB
         Buffers: shared hit=666
         I/O Timings: read=0.000 write=0.000
         ->  Aggregate  (cost=103.20..103.22 rows=1 width=280) (actual time=0.953..0.995 rows=8 loops=1)
               Group Key: ci_runners.id
               Buffers: shared hit=660
               I/O Timings: read=0.000 write=0.000
               ->  Sort  (cost=103.20..103.20 rows=1 width=280) (actual time=0.943..0.951 rows=128 loops=1)
                     Sort Key: ci_runners.id DESC
                     Sort Method: quicksort  Memory: 91kB
                     Buffers: shared hit=660
                     I/O Timings: read=0.000 write=0.000
                     ->  Nested Loop  (cost=14.72..103.19 rows=1 width=280) (actual time=0.234..0.817 rows=128 loops=1)
                           Buffers: shared hit=660
                           I/O Timings: read=0.000 write=0.000
                           ->  Hash Join  (cost=14.29..99.74 rows=1 width=12) (actual time=0.200..0.466 rows=128 loops=1)
                                 Hash Cond: (limited_builds.runner_id = ci_runner_namespaces.runner_id)
                                 Buffers: shared hit=148
                                 I/O Timings: read=0.000 write=0.000
                                 ->  Subquery Scan on limited_builds  (cost=0.29..85.57 rows=61 width=8) (actual time=0.121..0.361 rows=128 loops=1)
                                       Filter: (limited_builds.rn <= 1000)
                                       Rows Removed by Filter: 0
                                       Buffers: shared hit=131
                                       I/O Timings: read=0.000 write=0.000
                                       ->  WindowAgg  (cost=0.29..83.30 rows=182 width=16) (actual time=0.120..0.345 rows=128 loops=1)
                                             Buffers: shared hit=131
                                             I/O Timings: read=0.000 write=0.000
                                             ->  Index Only Scan using idx_ci_running_builds_on_runner_type_and_owner_xid_and_id on public.ci_running_builds  (cost=0.29..80.11 rows=182 width=8) (actual time=0.060..0.273 rows=128 loops=1)
                                                   Index Cond: ((ci_running_builds.runner_type = 2) AND (ci_running_builds.runner_owner_namespace_xid = 9970))
                                                   Heap Fetches: 125
                                                   Buffers: shared hit=131
                                                   I/O Timings: read=0.000 write=0.000
                                 ->  Hash  (cost=13.90..13.90 rows=8 width=4) (actual time=0.050..0.050 rows=14 loops=1)
                                       Buckets: 1024  Batches: 1  Memory Usage: 9kB
                                       Buffers: shared hit=17
                                       I/O Timings: read=0.000 write=0.000
                                       ->  Index Scan using index_ci_runner_namespaces_on_namespace_id on public.ci_runner_namespaces  (cost=0.43..13.90 rows=8 width=4) (actual time=0.023..0.045 rows=14 loops=1)
                                             Index Cond: (ci_runner_namespaces.namespace_id = 9970)
                                             Buffers: shared hit=17
                                             I/O Timings: read=0.000 write=0.000
                           ->  Index Scan using index_ci_runners_on_runner_type_and_id on public.ci_runners  (cost=0.43..3.45 rows=1 width=272) (actual time=0.002..0.002 rows=1 loops=128)
                                 Index Cond: ((ci_runners.runner_type = 2) AND (ci_runners.id = ci_runner_namespaces.runner_id))
                                 Buffers: shared hit=512
                                 I/O Timings: read=0.000 write=0.000
Edited by Pedro Pombeiro

Merge request reports