Skip to content

Derive Current Organization from Path or Current User

Rutger Wessels requested to merge 443829-use-routable into master

What does this MR do and why?

We want to set Current.organization based on these priorities (see #443829 (closed))

  • URL path
  • if that fails: Current User first organization
  • If that fails: Default organization

This might change in the future but for Cells 1.0, this should work.

For mapping an URL to a current organization, I identified three cases

  • params[:namespace_id] is set. We can use Routes to map this to an Organization
  • params[:group_id] is set. We can use Routes to map this to an Organization
  • params[:id] is set. We only use this if we are in GroupController

The Rack middleware will be removed. We will use a new Gitlab::Current::Organization class that will be called from ApplicationController. We still need to find out how we can do something similar for GraphQL and Rest API url's.

Database changes

Two scopes are added. Postgres.ai did not have snapshots at the time. My local dev database has data inserted with MASS_INSERT=1 and I also added a million fake organizations and organization_users to the database.

Organizations::Organization.with_namespace_path('gitlab/gitlab-org').

Explain output (Postgres.ai did not have a snapshot when I tried)

# EXPLAIN ANALYZE SELECT "organizations".* FROM "organizations" INNER JOIN "namespaces" ON "namespaces"."organization_id" = "organizations"."id" INNER JOIN "routes" "route" ON "route"."source_type" = 'Namespace' AND "route"."source_id" = "namespaces"."id" WHERE "route"."path" = 'gitlab/gitlab-org';

                                                                           QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=1.13..17.19 rows=1 width=90) (actual time=0.025..0.026 rows=0 loops=1)
   ->  Nested Loop  (cost=0.98..17.02 rows=1 width=8) (actual time=0.025..0.025 rows=0 loops=1)
         ->  Index Scan using index_routes_on_path_text_pattern_ops on routes route  (cost=0.55..8.57 rows=1 width=4) (actual time=0.025..0.025 rows=0 loops=1)
               Index Cond: ((path)::text = 'gitlab/gitlab-org'::text)
               Filter: ((source_type)::text = 'Namespace'::text)
         ->  Index Scan using namespaces_pkey on namespaces  (cost=0.43..8.45 rows=1 width=12) (never executed)
               Index Cond: (id = route.source_id)
   ->  Index Scan using organizations_pkey on organizations  (cost=0.15..0.17 rows=1 width=90) (never executed)
         Index Cond: (id = namespaces.organization_id)
 Planning Time: 0.335 ms
 Execution Time: 0.042 ms

So it is using indexes for all three tables. `

Organizations::Organization.with_user(user)

Explain output:

# EXPLAIN ANALYZE SELECT "organizations".* FROM "organizations" INNER JOIN "organization_users" ON "organization_users"."organization_id" = "organizations"."id" INNER JOIN "users" ON "users"."id" = "organization_users"."user_id" WHERE "users"."id" = 1 ORDER BY organization_users.id asc;

                                                                          QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=1128.89..1129.13 rows=98 width=100) (actual time=0.057..0.058 rows=1 loops=1)
   Sort Key: organization_users.id
   Sort Method: quicksort  Memory: 25kB
   ->  Nested Loop  (cost=9.90..1125.65 rows=98 width=100) (actual time=0.054..0.055 rows=1 loops=1)
         ->  Index Only Scan using users_pkey on users  (cost=0.42..4.44 rows=1 width=4) (actual time=0.031..0.032 rows=1 loops=1)
               Index Cond: (id = 1)
               Heap Fetches: 0
         ->  Nested Loop  (cost=9.47..1120.22 rows=98 width=108) (actual time=0.021..0.022 rows=1 loops=1)
               ->  Bitmap Heap Scan on organization_users  (cost=9.05..296.86 rows=98 width=24) (actual time=0.004..0.005 rows=1 loops=1)
                     Recheck Cond: (user_id = 1)
                     Heap Blocks: exact=1
                     ->  Bitmap Index Scan on index_organization_users_on_user_id  (cost=0.00..9.02 rows=98 width=0) (actual time=0.003..0.003 rows=1 loops=1)
                           Index Cond: (user_id = 1)
               ->  Index Scan using organizations_pkey on organizations  (cost=0.42..8.40 rows=1 width=92) (actual time=0.016..0.016 rows=1 loops=1)
                     Index Cond: (id = organization_users.organization_id)
 Planning Time: 0.299 ms
 Execution Time: 0.074 ms

All joins are using indexes

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.

Testing

# Create two new orgs
snake = Organizations::Organization.create(name: "Snake Oil", path: 'snake-oil')
gitlab = Organizations::Organization.create(name: "GitLab", path: 'gitlab')

# Make root user member of both
root = User.find_by_username('root')
snake.users << root
gitlab.users << root

# Assign Org snake to Groups
twitter = Group.find_by_path('twitter')
twitter.update(organization: snake)

glorg = Group.find_by_path('gitlab-org')
glorg.update(organization: gitlab)

We do not support rendering Current Organization name yet, so I modified this haml file:

# app/views/groups/_home_panel.html.haml
        = @group.name
        = Current.organization.name

Now go to /twitter/ url, the group name will now be Twitter Snake Oil (= Current Organization) /gitlab-org/ url: the group name will have Gitlab

Other groups will have 'Default'

image

Related to #443829 (closed)

Edited by Rutger Wessels

Merge request reports