Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit caa24ca

Browse files
v3: Improve performance of v3/routes
Refactor routes fetcher to use a dataset of readable guids instead of an array. We also short circuit the admin case which speeds up worse case scenario significantly. [#174499866] Co-authored-by: Seth Boyles <sboyles@pivotal.io> Co-authored-by: Merric de Launey <mdelauney@pivotal.io>
1 parent 0bde5d1 commit caa24ca

7 files changed

Lines changed: 84 additions & 23 deletions

File tree

app/controllers/v3/domains_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def check_routes
6363
message = RoutesListMessage.from_params(check_route_params)
6464
unprocessable!(message.errors.full_messages) unless message.valid?
6565

66-
dataset = RouteFetcher.fetch(message, Route.dataset.all.map(&:guid))
66+
dataset = RouteFetcher.fetch(message, Route.dataset)
6767
matching_route = false
6868
if dataset.any?
6969
matching_route = true

app/controllers/v3/routes_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def index
2323

2424
dataset = RouteFetcher.fetch(
2525
message,
26-
permission_queryer.readable_route_guids,
26+
permission_queryer.readable_route_dataset,
2727
eager_loaded_associations: Presenters::V3::RoutePresenter.associated_resources
2828
)
2929

@@ -198,7 +198,7 @@ def index_by_app
198198

199199
dataset = RouteFetcher.fetch(
200200
message,
201-
permission_queryer.readable_route_guids,
201+
permission_queryer.readable_route_dataset,
202202
eager_loaded_associations: Presenters::V3::RoutePresenter.associated_resources
203203
)
204204

app/fetchers/route_fetcher.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
module VCAP::CloudController
44
class RouteFetcher < BaseListFetcher
55
class << self
6-
def fetch(message, readable_route_guids, eager_loaded_associations: [])
7-
dataset = Route.where(guid: readable_route_guids).eager(eager_loaded_associations).qualify
6+
def fetch(message, readable_route_dataset, eager_loaded_associations: [])
7+
dataset = readable_route_dataset.eager(eager_loaded_associations).qualify
88
filter(message, dataset)
99
end
1010

lib/cloud_controller/permissions.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,15 @@ def can_read_from_isolation_segment?(isolation_segment)
145145
end
146146

147147
def readable_route_guids
148-
VCAP::CloudController::Route.user_visible(@user, can_read_globally?).map(&:guid)
148+
readable_route_dataset.map(&:guid)
149+
end
150+
151+
def readable_route_dataset
152+
if can_read_globally?
153+
VCAP::CloudController::Route.dataset
154+
else
155+
VCAP::CloudController::Route.user_visible(@user, can_read_globally?)
156+
end
149157
end
150158

151159
def readable_secret_space_guids

lib/cloud_controller/permissions/queryer.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ def can_read_from_isolation_segment?(isolation_segment)
180180
end
181181
end
182182

183+
def readable_route_dataset
184+
db_permissions.readable_route_dataset
185+
end
186+
183187
def readable_route_guids
184188
science 'readable_route_guids' do |e|
185189
e.use { db_permissions.readable_route_guids }

spec/unit/fetchers/route_fetcher_spec.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module VCAP::CloudController
2525
let(:routes_filter) { {} }
2626

2727
it 'eager loads the specified resources for the routes' do
28-
results = RouteFetcher.fetch(message, [route1.guid, route2.guid], eager_loaded_associations: [:labels, :domain]).all
28+
results = RouteFetcher.fetch(message, Route.where(guid: [route1.guid, route2.guid]), eager_loaded_associations: [:labels, :domain]).all
2929

3030
expect(results.first.associations.key?(:labels)).to be true
3131
expect(results.first.associations.key?(:domain)).to be true
@@ -38,7 +38,7 @@ module VCAP::CloudController
3838
let(:routes_filter) { { hosts: 'host2' } }
3939

4040
it 'only returns the matching route' do
41-
results = RouteFetcher.fetch(message, [route1.guid, route2.guid]).all
41+
results = RouteFetcher.fetch(message, Route.where(guid: [route1.guid, route2.guid])).all
4242
expect(results.length).to eq(1)
4343
expect(results[0].guid).to eq(route2.guid)
4444
end
@@ -48,7 +48,7 @@ module VCAP::CloudController
4848
let(:routes_filter) { { hosts: 'unknown-host' } }
4949

5050
it 'returns no routes' do
51-
results = RouteFetcher.fetch(message, [route1.guid, route2.guid]).all
51+
results = RouteFetcher.fetch(message, Route.where(guid: [route1.guid, route2.guid])).all
5252
expect(results.length).to eq(0)
5353
end
5454
end
@@ -59,7 +59,7 @@ module VCAP::CloudController
5959
let(:routes_filter) { { paths: '/path1' } }
6060

6161
it 'only returns the matching route' do
62-
results = RouteFetcher.fetch(message, [route1.guid, route2.guid]).all
62+
results = RouteFetcher.fetch(message, Route.where(guid: [route1.guid, route2.guid])).all
6363
expect(results.length).to eq(1)
6464
expect(results[0].guid).to eq(route1.guid)
6565
end
@@ -69,7 +69,7 @@ module VCAP::CloudController
6969
let(:routes_filter) { { paths: 'unknown-path' } }
7070

7171
it 'returns no routes' do
72-
results = RouteFetcher.fetch(message, [route1.guid, route2.guid]).all
72+
results = RouteFetcher.fetch(message, Route.where(guid: [route1.guid, route2.guid])).all
7373
expect(results.length).to eq(0)
7474
end
7575
end
@@ -80,7 +80,7 @@ module VCAP::CloudController
8080
let(:routes_filter) { { space_guids: space1.guid } }
8181

8282
it 'only returns the matching route' do
83-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
83+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
8484
expect(results.length).to eq(1)
8585
expect(results[0].guid).to eq(route2.guid)
8686
end
@@ -90,7 +90,7 @@ module VCAP::CloudController
9090
let(:routes_filter) { { space_guids: '???' } }
9191

9292
it 'returns no routes' do
93-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
93+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
9494
expect(results.length).to eq(0)
9595
end
9696
end
@@ -101,7 +101,7 @@ module VCAP::CloudController
101101
let(:routes_filter) { { organization_guids: space1.organization.guid } }
102102

103103
it 'only returns the matching route' do
104-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
104+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
105105
expect(results.length).to eq(1)
106106
expect(results[0].guid).to eq(route2.guid)
107107
end
@@ -111,7 +111,7 @@ module VCAP::CloudController
111111
let(:routes_filter) { { organization_guids: '???' } }
112112

113113
it 'returns no routes' do
114-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
114+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
115115
expect(results.length).to eq(0)
116116
end
117117
end
@@ -122,7 +122,7 @@ module VCAP::CloudController
122122
let(:routes_filter) { { domain_guids: domain2.guid } }
123123

124124
it 'only returns the matching route' do
125-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
125+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
126126
expect(results.length).to eq(1)
127127
expect(results[0].guid).to eq(route3.guid)
128128
end
@@ -132,7 +132,7 @@ module VCAP::CloudController
132132
let(:routes_filter) { { domain_guids: '???' } }
133133

134134
it 'returns no routes' do
135-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
135+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
136136
expect(results.length).to eq(0)
137137
end
138138
end
@@ -157,7 +157,7 @@ module VCAP::CloudController
157157
let(:routes_filter) { { ports: '8888' } }
158158

159159
it 'only returns the matching route' do
160-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid, route_with_ports.guid]).all
160+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid, route_with_ports.guid])).all
161161
expect(results.length).to eq(1)
162162
expect(results[0].guid).to eq(route_with_ports.guid)
163163
end
@@ -167,7 +167,7 @@ module VCAP::CloudController
167167
let(:routes_filter) { { ports: '123' } }
168168

169169
it 'returns no routes' do
170-
results = RouteFetcher.fetch(message, [route2.guid, route3.guid]).all
170+
results = RouteFetcher.fetch(message, Route.where(guid: [route2.guid, route3.guid])).all
171171
expect(results.length).to eq(0)
172172
end
173173
end
@@ -186,10 +186,10 @@ module VCAP::CloudController
186186
VCAP::CloudController::RouteLabelModel.make(resource_guid: route3.guid, key_name: 'dog', value: 'chihuahua')
187187
end
188188

189-
let(:results) { RouteFetcher.fetch(message, [route1.guid, route3.guid]).all }
189+
let(:results) { RouteFetcher.fetch(message, Route.where(guid: [route1.guid, route3.guid])).all }
190190

191191
context 'only the label_selector is present' do
192-
let(:results) { RouteFetcher.fetch(message, [route1.guid]).all }
192+
let(:results) { RouteFetcher.fetch(message, Route.where(guid: [route1.guid])).all }
193193

194194
let(:message) {
195195
RoutesListMessage.from_params({ 'label_selector' => 'dog in (chihuahua,scooby-doo)' })
@@ -220,7 +220,7 @@ module VCAP::CloudController
220220
let(:routes_filter) { { app_guids: [app_model.guid, app_model2.guid] } }
221221

222222
it 'only returns routes that are mapped to the app' do
223-
results = RouteFetcher.fetch(message, Route.all.map(&:guid)).all
223+
results = RouteFetcher.fetch(message, Route.dataset).all
224224
expect(results).to contain_exactly(route1, route2)
225225
end
226226
end
@@ -232,7 +232,7 @@ module VCAP::CloudController
232232
let(:routes_filter) { { app_guids: [app_model.guid] } }
233233

234234
it 'only returns routes that are mapped to the app' do
235-
results = RouteFetcher.fetch(message, Route.all.map(&:guid)).all
235+
results = RouteFetcher.fetch(message, Route.dataset).all
236236
expect(results).to contain_exactly(route1, route2)
237237
end
238238
end

spec/unit/lib/cloud_controller/permissions_spec.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,55 @@ module VCAP::CloudController
805805
end
806806
end
807807

808+
describe '#readable_route_dataset' do
809+
it 'returns all the routes for admins' do
810+
user = set_current_user_as_admin
811+
subject = Permissions.new(user)
812+
813+
org1 = Organization.make
814+
space1 = Space.make(organization: org1)
815+
route1 = Route.make(space: space1)
816+
route2 = Route.make(space: space1)
817+
org2 = Organization.make
818+
space2 = Space.make(organization: org2)
819+
route3 = Route.make(space: space2)
820+
821+
dataset = subject.readable_route_dataset
822+
823+
expect(dataset.first(guid: route1.guid)).to be_present
824+
expect(dataset.first(guid: route2.guid)).to be_present
825+
expect(dataset.first(guid: route3.guid)).to be_present
826+
end
827+
828+
it 'returns routes where the user has an appropriate org membership' do
829+
manager_org = Organization.make
830+
manager_space = Space.make(organization: manager_org)
831+
manager_route = Route.make(space: manager_space)
832+
manager_org.add_manager(user)
833+
834+
auditor_org = Organization.make
835+
auditor_space = Space.make(organization: auditor_org)
836+
auditor_route = Route.make(space: auditor_space)
837+
auditor_org.add_auditor(user)
838+
839+
billing_manager_org = Organization.make
840+
billing_manager_space = Space.make(organization: billing_manager_org)
841+
billing_manager_route = Route.make(space: billing_manager_space)
842+
billing_manager_org.add_billing_manager(user)
843+
844+
member_org = Organization.make
845+
member_space = Space.make(organization: member_org)
846+
member_route = Route.make(space: member_space)
847+
member_org.add_user(user)
848+
849+
dataset = permissions.readable_route_dataset
850+
851+
expect(dataset.first(guid: manager_route.guid)).to be_present
852+
expect(dataset.first(guid: auditor_route.guid)).to be_present
853+
expect(dataset.first(guid: billing_manager_route.guid)).to be_nil
854+
expect(dataset.first(guid: member_route.guid)).to be_nil
855+
end
856+
end
808857
describe '#readable_route_guids' do
809858
it 'returns all the route guids for admins' do
810859
user = set_current_user_as_admin

0 commit comments

Comments
 (0)