@@ -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
2254end
0 commit comments