Loading
Scope instance-level integration lookups to organization
What does this MR do and why?
Instance-level integrations (instance = true) now belong to one organization. Lookups filter by organization_id, so an org never picks up another org's defaults, and two orgs can each have their own Jira.
find_or_initialize_non_project_specific_integrationtakesorganization_id:for instance rows, and raises when it's missingdefault_integration,default_integrationsandinherited_descendants_from_self_or_ancestors_fromonly look at the owner's org- new
for_organizationscope, used by the admin integrations page andProject#integration_instances - type uniqueness is scoped to
organization_id, matching the unique index from !258698 (merged) - add
organization_level?, drop the unusedinstance_exists_for? - admin
overridesonly lists projects from the current org
for_instance stays for now. Its other callers move in #631289 (closed).
No feature flag. Self-managed has a single org, so nothing changes there.
Database
GitLab.com has no instance rows today, so the instance parts come back empty. The plans mostly show which index gets picked.
| Query | Where it runs | Plan |
|---|---|---|
| Admin overrides, default org | Admin::IntegrationsController#overrides |
plan, 72 ms, cold cache |
Admin overrides, small custom org (3000409) |
Admin::IntegrationsController#overrides |
plan, 3 ms, starts from index_projects_on_organization_id_and_id |
default_integrations union |
project and group create and transfer | plan, 13 ms, cold cache, instance part uses index_integrations_on_type_new_and_organization_id_unique |
instance_level_integration |
default_integration fallback |
plan, 0.016 ms, uses index_integrations_on_type_new_and_organization_id_unique |
Admin overrides plan, default org
Limit (cost=1.14..39.99 rows=20 width=749) (actual time=22.937..72.428 rows=20 loops=1)
Buffers: shared hit=37 read=68
I/O Timings: read=59.950 write=0.000
-> Nested Loop (cost=1.14..4759884.03 rows=2450278 width=749) (actual time=22.935..72.417 rows=20 loops=1)
Buffers: shared hit=37 read=68
I/O Timings: read=59.950 write=0.000
-> Index Scan using index_integrations_on_type_new on public.integrations (cost=0.56..623379.59 rows=2450286 width=4) (actual time=7.772..7.810 rows=20 loops=1)
Index Cond: (integrations.type_new = 'Integrations::Jira'::text)
Filter: (integrations.active AND ((integrations.inherit_from_id IS NULL) OR (integrations.inherit_from_id <> 1)))
Buffers: shared read=5
I/O Timings: read=7.726 write=0.000
-> Memoize (cost=0.57..1.75 rows=1 width=749) (actual time=3.227..3.227 rows=1 loops=20)
Cache Key: integrations.project_id
Cache Mode: logical
Hits: 0 Misses: 20 Evictions: 0 Overflows: 0 Memory Usage: 13kB
Buffers: shared hit=37 read=63
I/O Timings: read=52.224 write=0.000
-> Index Scan using projects_pkey on public.projects (cost=0.56..1.74 rows=1 width=749) (actual time=2.630..2.630 rows=1 loops=20)
Index Cond: (projects.id = integrations.project_id)
Filter: (projects.organization_id = 1)
Buffers: shared hit=37 read=63
I/O Timings: read=52.224 write=0.000
Settings: work_mem = '230MB', jit = 'off', random_page_cost = '1.5', seq_page_cost = '4', effective_cache_size = '472585MB'
Query ID: -2804112599988663296Admin overrides plan, small custom org
Limit (cost=1.12..778.52 rows=8 width=749) (actual time=2.950..2.951 rows=0 loops=1)
Buffers: shared hit=3 read=4
I/O Timings: read=2.901 write=0.000
-> Nested Loop (cost=1.12..778.52 rows=8 width=749) (actual time=2.948..2.949 rows=0 loops=1)
Buffers: shared hit=3 read=4
I/O Timings: read=2.901 write=0.000
-> Index Scan using index_projects_on_organization_id_and_id on public.projects (cost=0.56..140.84 rows=178 width=749) (actual time=2.947..2.948 rows=0 loops=1)
Index Cond: (projects.organization_id = 3000409)
Buffers: shared hit=3 read=4
I/O Timings: read=2.901 write=0.000
-> Index Scan using index_integrations_on_project_id_and_type_new_unique on public.integrations (cost=0.56..3.58 rows=1 width=4) (never executed)
Index Cond: ((integrations.project_id = projects.id) AND (integrations.type_new = 'Integrations::Jira'::text))
Filter: (integrations.active AND ((integrations.inherit_from_id IS NULL) OR (integrations.inherit_from_id <> 1)))
I/O Timings: read=0.000 write=0.000
Settings: seq_page_cost = '4', effective_cache_size = '472585MB', work_mem = '230MB', jit = 'off', random_page_cost = '1.5'
Query ID: -2804112599988663296default_integrations plan
Sort (cost=13.93..13.94 rows=2 width=267) (actual time=13.297..13.300 rows=1 loops=1)
Sort Key: integrations.type_new, (array_position('{2750817,9970}'::bigint[], integrations.group_id)), integrations.instance DESC
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=41 read=12
I/O Timings: read=13.042 write=0.000
-> Subquery Scan on integrations (cost=13.74..13.92 rows=2 width=267) (actual time=13.266..13.270 rows=1 loops=1)
Buffers: shared hit=30 read=12
I/O Timings: read=13.042 write=0.000
-> Unique (cost=13.74..13.92 rows=2 width=263) (actual time=13.257..13.260 rows=1 loops=1)
Buffers: shared hit=30 read=12
I/O Timings: read=13.042 write=0.000
-> Sort (cost=13.74..13.74 rows=2 width=263) (actual time=13.255..13.257 rows=1 loops=1)
Sort Key: integrations_1.id, integrations_1.project_id, integrations_1.created_at, integrations_1.updated_at, integrations_1.active, integrations_1.push_events, integrations_1.issues_events, integrations_1.merge_requests_events, integrations_1.tag_push_events, integrations_1.note_events, integrations_1.category, integrations_1.wiki_page_events, integrations_1.pipeline_events, integrations_1.confidential_issues_events, integrations_1.commit_events, integrations_1.job_events, integrations_1.confidential_note_events, integrations_1.deployment_events, integrations_1.comment_on_event_enabled, integrations_1.instance, integrations_1.comment_detail, integrations_1.inherit_from_id, integrations_1.alert_events, integrations_1.group_id, integrations_1.type_new, integrations_1.vulnerability_events, integrations_1.archive_trace_events, integrations_1.encrypted_properties, integrations_1.encrypted_properties_iv, integrations_1.incident_events, integrations_1.group_mention_events, integrations_1.group_confidential_mention_events, integrations_1.organization_id, integrations_1.event_filters, integrations_1.filter
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=30 read=12
I/O Timings: read=13.042 write=0.000
-> Append (cost=0.12..13.73 rows=2 width=263) (actual time=7.329..13.217 rows=1 loops=1)
Buffers: shared hit=13 read=12
I/O Timings: read=13.042 write=0.000
-> Index Scan using index_integrations_on_type_new_and_organization_id_unique on public.integrations integrations_1 (cost=0.12..3.14 rows=1 width=427) (actual time=0.001..0.001 rows=0 loops=1)
Index Cond: (integrations_1.organization_id = 1)
Filter: (integrations_1.active AND (integrations_1.type_new <> ALL ('{Integrations::AmazonQ,Integrations::BeyondIdentity}'::text[])))
Buffers: shared hit=1
I/O Timings: read=0.000 write=0.000
-> Nested Loop (cost=1.13..10.58 rows=1 width=427) (actual time=7.326..13.212 rows=1 loops=1)
Buffers: shared hit=12 read=12
I/O Timings: read=13.042 write=0.000
-> Index Only Scan using index_namespaces_on_type_and_id on public.namespaces (cost=0.57..5.67 rows=1 width=4) (actual time=4.067..6.745 rows=2 loops=1)
Index Cond: ((namespaces.type = 'Group'::text) AND (namespaces.id = ANY ('{9970,2750817}'::integer[])))
Heap Fetches: 0
Buffers: shared hit=2 read=8
I/O Timings: read=6.674 write=0.000
-> Index Scan using index_integrations_on_unique_group_id_and_type_new on public.integrations integrations_2 (cost=0.56..4.91 rows=1 width=427) (actual time=3.226..3.227 rows=0 loops=2)
Index Cond: (integrations_2.group_id = namespaces.id)
Filter: (integrations_2.active AND (integrations_2.inherit_from_id IS NULL) AND (integrations_2.type_new <> ALL ('{Integrations::AmazonQ,Integrations::BeyondIdentity}'::text[])))
Rows Removed by Filter: 1
Buffers: shared hit=10 read=4
I/O Timings: read=6.368 write=0.000
Settings: jit = 'off', random_page_cost = '1.5', seq_page_cost = '4', effective_cache_size = '472585MB', work_mem = '230MB'
Query ID: 3966530287687146193instance_level_integration plan
Limit (cost=0.12..3.14 rows=1 width=427) (actual time=0.016..0.016 rows=0 loops=1)
Buffers: shared hit=5
I/O Timings: read=0.000 write=0.000
-> Index Scan using index_integrations_on_type_new_and_organization_id_unique on public.integrations (cost=0.12..3.14 rows=1 width=427) (actual time=0.015..0.015 rows=0 loops=1)
Index Cond: ((integrations.type_new = 'Integrations::Jira'::text) AND (integrations.organization_id = 1))
Buffers: shared hit=5
I/O Timings: read=0.000 write=0.000
Settings: work_mem = '230MB', jit = 'off', random_page_cost = '1.5', seq_page_cost = '4', effective_cache_size = '472585MB'
Query ID: -3243719665763061232No plans for the rest, since nothing really changes for them:
inherited_descendants_from_self_or_ancestors_fromalready had theOR instance = truebranch. Addingorganization_idonly narrows it.for_organizationandfind_or_initialize_non_project_specific_integrationare the same kind of lookup asinstance_level_integration, on the same partial unique index.- The type uniqueness check has no
instance = true, so it can't use the partial index. It's still cheap, since only instance rows haveorganization_idset (index_integrations_on_organization_id).
References
Related to #631287 (closed)
Edited by Rez