Skip to content

Commit b3be5d8

Browse files
committed
Add default order by id to one_to_many and many_to_many associations
Ensures deterministic ordering for all association queries by adding a default `order: :id` to the vcap_relations plugin. This fixes sporadic test failures in parallel test runs where database query order was non-deterministic. Explicit order options still override this default, and callers can use `.order(:field)` on the dataset to override as needed. Associations with a block (custom dataset transformation) do not get the default order, as the transformation may not be compatible with ordering by id. Also removes now-redundant explicit `order: :id` from two models and fixes orgs_visibility to order by service_plan_id (the only column in its select list).
1 parent b27da11 commit b3be5d8

File tree

5 files changed

+39
-9
lines changed

5 files changed

+39
-9
lines changed

app/models/runtime/buildpack_lifecycle_data_model.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ class BuildpackLifecycleDataModel < Sequel::Model(:buildpack_lifecycle_data)
2828
one_to_many :buildpack_lifecycle_buildpacks,
2929
class: '::VCAP::CloudController::BuildpackLifecycleBuildpackModel',
3030
key: :buildpack_lifecycle_data_guid,
31-
primary_key: :guid,
32-
order: :id
31+
primary_key: :guid
3332
plugin :nested_attributes
3433
nested_attributes :buildpack_lifecycle_buildpacks, destroy: true
3534
add_association_dependencies buildpack_lifecycle_buildpacks: :destroy

app/models/runtime/cnb_lifecycle_data_model.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ class CNBLifecycleDataModel < Sequel::Model(:cnb_lifecycle_data)
2727
one_to_many :buildpack_lifecycle_buildpacks,
2828
class: '::VCAP::CloudController::BuildpackLifecycleBuildpackModel',
2929
key: :cnb_lifecycle_data_guid,
30-
primary_key: :guid,
31-
order: :id
30+
primary_key: :guid
3231
plugin :nested_attributes
3332
nested_attributes :buildpack_lifecycle_buildpacks, destroy: true
3433
add_association_dependencies buildpack_lifecycle_buildpacks: :destroy

app/models/services/service_plan.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ServicePlan < Sequel::Model
1111

1212
add_association_dependencies service_plan_visibilities: :destroy
1313

14-
one_to_many(:orgs_visibility, clone: :service_plan_visibilities) { |ds| ds.select(:service_plan_id).distinct }
14+
one_to_many(:orgs_visibility, clone: :service_plan_visibilities, order: :service_plan_id) { |ds| ds.select(:service_plan_id).distinct }
1515

1616
one_to_many :labels, class: 'VCAP::CloudController::ServicePlanLabelModel', key: :resource_guid, primary_key: :guid
1717
add_association_dependencies labels: :destroy

lib/sequel_plugins/vcap_relations.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ def one_through_one(name, opts={})
5656
#
5757
# See the default many_to_many implementation for a description of the args
5858
# and return values.
59-
def many_to_many(name, opts={})
59+
def many_to_many(name, opts={}, &block)
60+
opts[:order] ||= :id unless block
61+
6062
singular_name = name.to_s.singularize
6163
ids_attr = "#{singular_name}_ids"
6264
guids_attr = "#{singular_name}_guids"
@@ -83,7 +85,7 @@ def many_to_many(name, opts={})
8385
self.name.split('::').last.underscore.pluralize.to_sym
8486

8587
define_to_many_methods(name, singular_name, ids_attr, guids_attr)
86-
super
88+
super(name, opts, &block)
8789
end
8890

8991
# Override one_to_many in order to add an override the default Sequel
@@ -93,15 +95,17 @@ def many_to_many(name, opts={})
9395
#
9496
# See the default one_to_many implementation for a description of the args
9597
# and return values.
96-
def one_to_many(name, opts={})
98+
def one_to_many(name, opts={}, &block)
99+
opts[:order] ||= :id unless block
100+
97101
singular_name = name.to_s.singularize
98102
ids_attr = "#{singular_name}_ids"
99103
guids_attr = "#{singular_name}_guids"
100104

101105
opts[:reciprocal] ||= self.name.split('::').last.underscore.to_sym
102106

103107
define_to_many_methods(name, singular_name, ids_attr, guids_attr)
104-
super
108+
super(name, opts, &block)
105109
end
106110

107111
private

spec/unit/lib/sequel_plugins/vcap_relations_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ def define_model(name)
2929
expect(@o.dogs).to be_empty
3030
end
3131

32+
it 'orders by id by default' do
33+
expect(@o.dogs_dataset.sql).to match(/ORDER BY .id.$/)
34+
end
35+
36+
it 'allows overriding the default order' do
37+
owner_klass.one_to_many :dogs, order: Sequel.desc(:id)
38+
39+
expect(@o.dogs_dataset.sql).to match(/ORDER BY .id. DESC$/)
40+
end
41+
42+
it 'allows overriding the default order via dataset' do
43+
expect(@o.dogs_dataset.order(:created_at).sql).to match(/ORDER BY .created_at.$/)
44+
end
45+
3246
it 'adds a add_<relation> method that takes an object' do
3347
d = dog_klass.create
3448
@o.add_dog d
@@ -167,6 +181,20 @@ def define_model(name)
167181
expect(@d1.names).to be_empty
168182
end
169183

184+
it 'orders by id by default' do
185+
expect(@d1.names_dataset.sql).to match(/ORDER BY .id.$/)
186+
end
187+
188+
it 'allows overriding the default order' do
189+
dog_klass.many_to_many :names, order: Sequel.desc(:id)
190+
191+
expect(@d1.names_dataset.sql).to match(/ORDER BY .id. DESC$/)
192+
end
193+
194+
it 'allows overriding the default order via dataset' do
195+
expect(@d1.names_dataset.order(:created_at).sql).to match(/ORDER BY .created_at.$/)
196+
end
197+
170198
it 'adds a add_<relation> method that takes an object' do
171199
@d1.add_name @n1
172200
expect(@d1.names).to include(@n1)

0 commit comments

Comments
 (0)