PERF: batch update post timings
previously we would issue a query per row in post timings, this batches it
This commit is contained in:
parent
61df4bd90a
commit
f0c74d7685
|
@ -29,6 +29,25 @@ class PostTiming < ActiveRecord::Base
|
|||
TopicUser.ensure_consistency!(topic_id)
|
||||
end
|
||||
|
||||
def self.record_new_timing(args)
|
||||
begin
|
||||
exec_sql("INSERT INTO post_timings (topic_id, user_id, post_number, msecs)
|
||||
SELECT :topic_id, :user_id, :post_number, :msecs
|
||||
WHERE NOT EXISTS(SELECT 1 FROM post_timings
|
||||
WHERE topic_id = :topic_id
|
||||
AND user_id = :user_id
|
||||
AND post_number = :post_number)",
|
||||
args)
|
||||
rescue PG::UniqueViolation
|
||||
# concurrency is hard, we are not running serialized so this can possibly
|
||||
# still happen, if it happens we just don't care, its an invalid record anyway
|
||||
return
|
||||
end
|
||||
|
||||
Post.where(['topic_id = :topic_id and post_number = :post_number', args]).update_all 'reads = reads + 1'
|
||||
UserStat.where(user_id: args[:user_id]).update_all 'posts_read_count = posts_read_count + 1'
|
||||
end
|
||||
|
||||
# Increases a timer if a row exists, otherwise create it
|
||||
def self.record_timing(args)
|
||||
rows = exec_sql_row_count("UPDATE post_timings
|
||||
|
@ -38,25 +57,7 @@ class PostTiming < ActiveRecord::Base
|
|||
AND post_number = :post_number",
|
||||
args)
|
||||
|
||||
if rows == 0
|
||||
|
||||
begin
|
||||
exec_sql("INSERT INTO post_timings (topic_id, user_id, post_number, msecs)
|
||||
SELECT :topic_id, :user_id, :post_number, :msecs
|
||||
WHERE NOT EXISTS(SELECT 1 FROM post_timings
|
||||
WHERE topic_id = :topic_id
|
||||
AND user_id = :user_id
|
||||
AND post_number = :post_number)",
|
||||
args)
|
||||
rescue PG::UniqueViolation
|
||||
# concurrency is hard, we are not running serialized so this can possibly
|
||||
# still happen, if it happens we just don't care, its an invalid record anyway
|
||||
return
|
||||
end
|
||||
|
||||
Post.where(['topic_id = :topic_id and post_number = :post_number', args]).update_all 'reads = reads + 1'
|
||||
UserStat.where(user_id: args[:user_id]).update_all 'posts_read_count = posts_read_count + 1'
|
||||
end
|
||||
record_new_timing(args) if rows == 0
|
||||
end
|
||||
|
||||
|
||||
|
@ -74,15 +75,45 @@ class PostTiming < ActiveRecord::Base
|
|||
account_age_msecs = ((Time.now - current_user.created_at) * 1000.0)
|
||||
|
||||
highest_seen = 1
|
||||
timings.each do |post_number, time|
|
||||
if post_number >= 0 && time < account_age_msecs
|
||||
PostTiming.record_timing(topic_id: topic_id,
|
||||
post_number: post_number,
|
||||
user_id: current_user.id,
|
||||
msecs: time)
|
||||
|
||||
join_table = []
|
||||
|
||||
timings = timings.find_all do |post_number, time|
|
||||
post_number >= 0 && time < account_age_msecs
|
||||
end
|
||||
|
||||
timings.each_with_index do |(post_number, time), index|
|
||||
join_table << "SELECT #{topic_id.to_i} topic_id, #{post_number.to_i} post_number,
|
||||
#{current_user.id.to_i} user_id, #{time.to_i} msecs, #{index} idx"
|
||||
|
||||
|
||||
highest_seen = post_number.to_i > highest_seen ?
|
||||
post_number.to_i : highest_seen
|
||||
end
|
||||
|
||||
if join_table.length > 0
|
||||
sql = <<SQL
|
||||
|
||||
UPDATE post_timings t
|
||||
SET msecs = t.msecs + x.msecs
|
||||
FROM (#{join_table.join(" UNION ALL ")}) x
|
||||
WHERE x.topic_id = t.topic_id AND
|
||||
x.post_number = t.post_number AND
|
||||
x.user_id = t.user_id
|
||||
RETURNING x.idx
|
||||
SQL
|
||||
|
||||
result = exec_sql(sql)
|
||||
result.type_map = SqlBuilder.pg_type_map
|
||||
existing = Set.new(result.column_values(0))
|
||||
|
||||
timings.each_with_index do |(post_number, time),index|
|
||||
unless existing.include?(index)
|
||||
PostTiming.record_new_timing(topic_id: topic_id,
|
||||
post_number: post_number,
|
||||
user_id: current_user.id,
|
||||
msecs: time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue