Skip to content

Commit 8bc5844

Browse files
committed
feat: optimise query for loading latest verification for the latest pacts for each tag on index page
1 parent 2080bcf commit 8bc5844

11 files changed

+111
-16
lines changed

db/migrations/20180311_optimise_head_matrix.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# Add provider_version_order to original definition
1919
# The most recent verification for each pact_version
2020
# provider_version column is DEPRECATED, use provider_version_number
21-
# Think this can be replaced by latest_verification_id_for_pact_version_and_provider_version?
21+
# Think this can be replaced by latest_verif_id_for_pact_version_and_provider_version?
2222
v = :verifications
2323
create_or_replace_view(:latest_verifications,
2424
from(v)

db/migrations/20180720_create_latest_pact_publication_ids.rb

+2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
# for a pact are deleted together when you delete the pact resource for that
99
# consumer version, and when that happens, this row will cascade delete.
1010
create_table(:latest_pact_publication_ids_by_consumer_versions, charset: 'utf8') do
11+
foreign_key :consumer_id, :pacticipants, nil: false, on_delete: :cascade # redundant, but speeds up queries by removing need for extra join
1112
foreign_key :consumer_version_id, :versions, nil: false, on_delete: :cascade
1213
foreign_key :provider_id, :pacticipants, nil: false, on_delete: :cascade
1314
foreign_key :pact_publication_id, :pact_publications, nil: false, on_delete: :cascade, unique: true
1415
index [:provider_id, :consumer_version_id], unique: true, name: "unq_latest_ppid_prov_conver"
16+
index [:provider_id, :consumer_id], name: "lpp_provider_id_consumer_id_index"
1517
end
1618
end
1719

db/migrations/20180721_migrate_latest_pact_publication_ids.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Sequel.migration do
22
up do
33
# The danger with this migration is that a pact publication created by an old node will be lost
4-
rows = from(:latest_pact_publications_by_consumer_versions).select(:consumer_version_id, :provider_id, :id)
4+
rows = from(:latest_pact_publications_by_consumer_versions).select(:consumer_id, :consumer_version_id, :provider_id, :id)
55
from(:latest_pact_publication_ids_by_consumer_versions).insert(rows)
66
end
77

db/migrations/20180723_create_latest_verification_ids.rb

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55
# latest revision speeds queries up.
66
# There is no way to delete an individual verification result yet, but when there
77
# is, we'll need to re-calculate the latest.
8-
create_table(:latest_verification_id_for_pact_version_and_provider_version, charset: 'utf8') do
8+
create_table(:latest_verif_id_for_pact_version_and_provider_version, charset: 'utf8') do
9+
foreign_key :consumer_id, :pacticipants, nil: false, on_delete: :cascade # not required, but useful to avoid extra joins
910
foreign_key :pact_version_id, :pact_versions, nil: false, on_delete: :cascade
10-
foreign_key :provider_version_id, :versions, nil: false, on_delete: :cascade
1111
foreign_key :provider_id, :pacticipants, nil: false, on_delete: :cascade # not required, but useful to avoid extra joins
12+
foreign_key :provider_version_id, :versions, nil: false, on_delete: :cascade
1213
foreign_key :verification_id, :verifications, nil: false, on_delete: :cascade, unique: true
1314
index [:pact_version_id, :provider_version_id], unique: true, name: "unq_latest_verifid_pvid_provid"
1415
end
1516
end
1617

1718
down do
18-
drop_table(:latest_verification_id_for_pact_version_and_provider_version)
19+
drop_table(:latest_verif_id_for_pact_version_and_provider_version)
1920
end
2021
end

db/migrations/20180724_migrate_latest_verification_ids.rb

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
Sequel.migration do
22
up do
3-
# Not sure if we need the provider_id, but it might come in handy
3+
# consumer_id and provider_id are redundant by avoid making extra joins when creating views
44
rows = from(:verifications).select_group(
5+
Sequel[:verifications][:consumer_id],
56
Sequel[:verifications][:pact_version_id],
6-
Sequel[:verifications][:provider_version_id],
7-
Sequel[:versions][:pacticipant_id].as(:provider_id))
7+
Sequel[:verifications][:provider_id],
8+
Sequel[:verifications][:provider_version_id])
89
.select_append{ max(verifications[id]).as(verification_id) }
9-
.join(:versions, { Sequel[:verifications][:provider_version_id] => Sequel[:versions][:id] })
1010

1111
# The danger with this migration is that a verification created by an old node will be lost
12-
from(:latest_verification_id_for_pact_version_and_provider_version).insert(rows)
12+
from(:latest_verif_id_for_pact_version_and_provider_version).insert(rows)
1313
end
1414

1515
down do

db/migrations/20180726_recreate_views.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from latest_pact_publication_ids_by_consumer_versions lpp
99
inner join pact_publications pp
1010
on pp.id = lpp.pact_publication_id
11-
left outer join latest_verification_id_for_pact_version_and_provider_version lv
11+
left outer join latest_verif_id_for_pact_version_and_provider_version lv
1212
on lv.pact_version_id = pp.pact_version_id"
1313
)
1414

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Sequel.migration do
2+
up do
3+
ltcvo = :latest_tagged_pact_consumer_version_orders
4+
versions_join = {
5+
Sequel[ltcvo][:consumer_id] => Sequel[:cv][:pacticipant_id],
6+
Sequel[ltcvo][:latest_consumer_version_order] => Sequel[:cv][:order]
7+
}
8+
lpp_join = {
9+
Sequel[:lpp][:consumer_version_id] => Sequel[:cv][:id],
10+
Sequel[ltcvo][:provider_id] => Sequel[:lpp][:provider_id]
11+
}
12+
# todo add pact_version_id to latest_pact_publication_ids_by_consumer_versions?
13+
pp_join = {
14+
Sequel[:pp][:id] => Sequel[:lpp][:pact_publication_id]
15+
}
16+
verifications_join = {
17+
Sequel[:v][:pact_version_id] => Sequel[:pp][:pact_version_id]
18+
}
19+
view = from(ltcvo).select_group(
20+
Sequel[ltcvo][:provider_id],
21+
Sequel[ltcvo][:consumer_id],
22+
Sequel[ltcvo][:tag_name].as(:consumer_version_tag_name))
23+
.select_append{ max(v[id]).as(latest_verification_id) }
24+
.join(:versions, versions_join, { table_alias: :cv } )
25+
.join(:latest_pact_publication_ids_by_consumer_versions, lpp_join, { table_alias: :lpp })
26+
.join(:pact_publications, pp_join, { table_alias: :pp })
27+
.join(:verifications, verifications_join, { table_alias: :v })
28+
29+
create_or_replace_view(:latest_verification_ids_for_consumer_version_tags, view)
30+
end
31+
32+
down do
33+
# The latest verification id for each consumer version tag
34+
create_or_replace_view(:latest_verification_ids_for_consumer_version_tags,
35+
"select
36+
pv.pacticipant_id as provider_id,
37+
lpp.consumer_id,
38+
t.name as consumer_version_tag_name,
39+
max(v.id) as latest_verification_id
40+
from verifications v
41+
join latest_pact_publications_by_consumer_versions lpp
42+
on v.pact_version_id = lpp.pact_version_id
43+
join tags t
44+
on lpp.consumer_version_id = t.version_id
45+
join versions pv
46+
on v.provider_version_id = pv.id
47+
group by pv.pacticipant_id, lpp.consumer_id, t.name")
48+
end
49+
end

lib/pact_broker/api/resources/verification.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def content_types_provided
1414
[["application/hal+json", :to_json], ["application/json", :to_json]]
1515
end
1616

17-
# Remember to update latest_verification_id_for_pact_version_and_provider_version
17+
# Remember to update latest_verif_id_for_pact_version_and_provider_version
1818
# if/when DELETE is implemented
1919
def allowed_methods
2020
["GET", "OPTIONS"]

lib/pact_broker/pacts/repository.rb

+13-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,19 @@ def update id, params
4949
end
5050

5151
def update_latest_pact_publication_ids(pact_publication)
52-
latest_pact_publication_params = { consumer_version_id: pact_publication.consumer_version_id, provider_id: pact_publication.provider_id, pact_publication_id: pact_publication.id }
53-
AllPactPublications.db[:latest_pact_publication_ids_by_consumer_versions].insert_ignore.insert(latest_pact_publication_params)
52+
key = {
53+
consumer_version_id: pact_publication.consumer_version_id,
54+
provider_id: pact_publication.provider_id,
55+
}
56+
57+
other = {
58+
pact_publication_id: pact_publication.id, consumer_id: pact_publication.consumer_id
59+
}
60+
61+
row = key.merge(other)
62+
63+
table = AllPactPublications.db[:latest_pact_publication_ids_by_consumer_versions]
64+
PactBroker::Repositories::Helpers.upsert(table, key, other)
5465
end
5566

5667
def delete params

lib/pact_broker/repositories/helpers.rb

+21
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def mysql?
2020
Sequel::Model.db.adapter_scheme.to_s =~ /mysql/
2121
end
2222

23+
def postgres?
24+
Sequel::Model.db.adapter_scheme.to_s == "postgres"
25+
end
26+
2327
def select_all_qualified
2428
select(Sequel[model.table_name].*)
2529
end
@@ -31,6 +35,23 @@ def select_for_subquery column
3135
select(column)
3236
end
3337
end
38+
39+
# TODO refactor to use proper dataset module
40+
def upsert table, key, other
41+
row = key.merge(other)
42+
if postgres?
43+
table.insert_conflict(update: other, target: key.keys).insert(row)
44+
elsif mysql?
45+
table.on_duplicate_key_update.insert(row)
46+
else
47+
# Sqlite
48+
if table.where(key).count == 0
49+
table.insert(row)
50+
else
51+
table.where(key).update(row)
52+
end
53+
end
54+
end
3455
end
3556
end
3657
end

lib/pact_broker/verifications/repository.rb

+13-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,18 @@ def create verification, provider_version_number, pact
3333
end
3434

3535
def update_latest_verification_id verification
36-
latest_verification_params = { pact_version_id: verification.pact_version_id, provider_version_id: verification.provider_version_id, provider_id: verification.provider_version.pacticipant_id, verification_id: verification.id }
37-
PactBroker::Domain::Verification.db[:latest_verification_id_for_pact_version_and_provider_version].insert_ignore.insert(latest_verification_params)
36+
key = {
37+
pact_version_id: verification.pact_version_id, provider_version_id: verification.provider_version_id
38+
}
39+
40+
other = {
41+
provider_id: verification.provider_version.pacticipant_id,
42+
verification_id: verification.id,
43+
consumer_id: verification.consumer_id
44+
}
45+
46+
table = PactBroker::Domain::Verification.db[:latest_verif_id_for_pact_version_and_provider_version]
47+
PactBroker::Repositories::Helpers.upsert(table, key, other)
3848
end
3949

4050
def find consumer_name, provider_name, pact_version_sha, verification_number
@@ -100,6 +110,7 @@ def find_latest_verification_for_tags consumer_name, provider_name, consumer_ver
100110
.tag(consumer_version_tag)
101111
.provider_version_tag(provider_version_tag)
102112

113+
103114
query.reverse_order(
104115
Sequel[:latest_pact_publications_by_consumer_versions][:consumer_version_order],
105116
Sequel[:latest_pact_publications_by_consumer_versions][:revision_number],

0 commit comments

Comments
 (0)