Skip to content

Commit 485dc7d

Browse files
committed
Add fallback for PostgreSQL without upsert in CopyStatusStats (mastodon#8903)
Fix mastodon#8590
1 parent a1b9044 commit 485dc7d

1 file changed

Lines changed: 41 additions & 9 deletions

File tree

db/migrate/20180812173710_copy_status_stats.rb

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,52 @@ class CopyStatusStats < ActiveRecord::Migration[5.2]
33

44
def up
55
safety_assured do
6-
Status.unscoped.select('id').find_in_batches(batch_size: 5_000) do |statuses|
7-
execute <<-SQL.squish
8-
INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at)
9-
SELECT id, reblogs_count, favourites_count, created_at, updated_at
10-
FROM statuses
11-
WHERE id IN (#{statuses.map(&:id).join(', ')})
12-
ON CONFLICT (status_id) DO UPDATE
13-
SET reblogs_count = EXCLUDED.reblogs_count, favourites_count = EXCLUDED.favourites_count
14-
SQL
6+
if supports_upsert?
7+
up_fast
8+
else
9+
up_slow
1510
end
1611
end
1712
end
1813

1914
def down
2015
# Nothing
2116
end
17+
18+
private
19+
20+
def supports_upsert?
21+
version = select_one("SELECT current_setting('server_version_num') AS v")['v'].to_i
22+
version >= 90500
23+
end
24+
25+
def up_fast
26+
say 'Upsert is available, importing counters using the fast method'
27+
28+
Status.unscoped.select('id').find_in_batches(batch_size: 5_000) do |statuses|
29+
execute <<-SQL.squish
30+
INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at)
31+
SELECT id, reblogs_count, favourites_count, created_at, updated_at
32+
FROM statuses
33+
WHERE id IN (#{statuses.map(&:id).join(', ')})
34+
ON CONFLICT (status_id) DO UPDATE
35+
SET reblogs_count = EXCLUDED.reblogs_count, favourites_count = EXCLUDED.favourites_count
36+
SQL
37+
end
38+
end
39+
40+
def up_slow
41+
say 'Upsert is not available in PostgreSQL below 9.5, falling back to slow import of counters'
42+
43+
# We cannot use bulk INSERT or overarching transactions here because of possible
44+
# uniqueness violations that we need to skip over
45+
Status.unscoped.select('id, reblogs_count, favourites_count, created_at, updated_at').find_each do |status|
46+
begin
47+
params = [[nil, status.id], [nil, status.reblogs_count], [nil, status.favourites_count], [nil, status.created_at], [nil, status.updated_at]]
48+
exec_insert('INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)', nil, params)
49+
rescue ActiveRecord::RecordNotUnique
50+
next
51+
end
52+
end
53+
end
2254
end

0 commit comments

Comments
 (0)