Skip to content

Commit

Permalink
large refactor, ship a few columns from the user table into user_stats
Browse files Browse the repository at this point in the history
  • Loading branch information
SamSaffron committed Oct 7, 2013
1 parent 4613006 commit 5bf26ec
Show file tree
Hide file tree
Showing 29 changed files with 354 additions and 251 deletions.
2 changes: 1 addition & 1 deletion app/jobs/scheduled/periodical_updates.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def execute(args)
CategoryFeaturedTopic.feature_topics

# Update view counts for users
User.update_view_counts
UserStat.update_view_counts

# Update the scores of posts
ScoreCalculator.new.calculate
Expand Down
2 changes: 1 addition & 1 deletion app/models/post_timing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def self.destroy_for(user_id, topic_id)


def self.process_timings(current_user, topic_id, topic_time, timings)
current_user.update_time_read!
current_user.user_stat.update_time_read!

highest_seen = 1
timings.each do |post_number, time|
Expand Down
27 changes: 15 additions & 12 deletions app/models/site_customization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,21 @@ def mobile_stylesheet_link_tag
#
# Table name: site_customizations
#
# id :integer not null, primary key
# name :string(255) not null
# stylesheet :text
# header :text
# position :integer not null
# user_id :integer not null
# enabled :boolean not null
# key :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
# override_default_style :boolean default(FALSE), not null
# stylesheet_baked :text default(""), not null
# id :integer not null, primary key
# name :string(255) not null
# stylesheet :text
# header :text
# position :integer not null
# user_id :integer not null
# enabled :boolean not null
# key :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
# override_default_style :boolean default(FALSE), not null
# stylesheet_baked :text default(""), not null
# mobile_stylesheet :text
# mobile_header :text
# mobile_stylesheet_baked :text
#
# Indexes
#
Expand Down
6 changes: 4 additions & 2 deletions app/models/topic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,9 @@ def update_category_topic_count_by(num)
#
# Indexes
#
# idx_topics_user_id_deleted_at (user_id)
# index_forum_threads_on_bumped_at (bumped_at)
# idx_topics_user_id_deleted_at (user_id)
# index_forum_threads_on_bumped_at (bumped_at)
# index_topics_on_deleted_at_and_visible_and_archetype_and_id (deleted_at,visible,archetype,id)
# index_topics_on_id_and_deleted_at (id,deleted_at)
#

7 changes: 4 additions & 3 deletions app/models/upload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ def self.get_from_url(url)
#
# Indexes
#
# index_uploads_on_sha1 (sha1) UNIQUE
# index_uploads_on_url (url)
# index_uploads_on_user_id (user_id)
# index_uploads_on_id_and_url (id,url)
# index_uploads_on_sha1 (sha1) UNIQUE
# index_uploads_on_url (url)
# index_uploads_on_user_id (user_id)
#

74 changes: 3 additions & 71 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def has_visit_record?(date)

def update_visit_record!(date)
unless has_visit_record?(date)
update_column(:days_visited, days_visited + 1)
user_stat.update_column(:days_visited, user_stat.days_visited + 1)
user_visits.create!(visited_at: date)
end
end
Expand Down Expand Up @@ -316,42 +316,6 @@ def avatar_template
uploaded_avatar_path || User.gravatar_template(email)
end

# Updates the denormalized view counts for all users
def self.update_view_counts

# NOTE: we only update the counts for users we have seen in the last hour
# this avoids a very expensive query that may run on the entire user base
# we also ensure we only touch the table if data changes

# Update denormalized topics_entered
exec_sql "UPDATE users SET topics_entered = X.c
FROM
(SELECT v.user_id,
COUNT(DISTINCT parent_id) AS c
FROM views AS v
WHERE parent_type = 'Topic' AND v.user_id IN (
SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at
)
GROUP BY v.user_id) AS X
WHERE
X.user_id = users.id AND
X.c <> topics_entered
", seen_at: 1.hour.ago

# Update denormalzied posts_read_count
exec_sql "UPDATE users SET posts_read_count = X.c
FROM
(SELECT pt.user_id,
COUNT(*) AS c
FROM post_timings AS pt
WHERE pt.user_id IN (
SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at
)
GROUP BY pt.user_id) AS X
WHERE X.user_id = users.id AND
X.c <> posts_read_count
", seen_at: 1.hour.ago
end

# The following count methods are somewhat slow - definitely don't use them in a loop.
# They might need to be denormalized
Expand Down Expand Up @@ -458,20 +422,6 @@ def treat_as_new_topic_start_date
end
end

MAX_TIME_READ_DIFF = 100
# attempt to add total read time to user based on previous time this was called
def update_time_read!
last_seen_key = "user-last-seen:#{id}"
last_seen = $redis.get(last_seen_key)
if last_seen.present?
diff = (Time.now.to_f - last_seen.to_f).round
if diff > 0 && diff < MAX_TIME_READ_DIFF
User.where(id: id, time_read: time_read).update_all ["time_read = time_read + ?", diff]
end
end
$redis.set(last_seen_key, Time.now.to_f)
end

def readable_name
return "#{name} (#{username})" if name.present? && name != username
username
Expand All @@ -486,18 +436,6 @@ def self.count_by_signup_date(sinceDaysAgo=30)
where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count
end

def update_topic_reply_count
self.topic_reply_count =
Topic
.where(['id in (
SELECT topic_id FROM posts p
JOIN topics t2 ON t2.id = p.topic_id
WHERE p.deleted_at IS NULL AND
t2.user_id <> p.user_id AND
p.user_id = ?
)', self.id])
.count
end

def secure_category_ids
cats = self.staff? ? Category.where(read_restricted: true) : secure_categories.references(:categories)
Expand Down Expand Up @@ -549,7 +487,7 @@ def update_tracked_topics

def create_user_stat
stat = UserStat.new
stat.user_id = self.id
stat.user_id = id
stat.save!
end

Expand Down Expand Up @@ -647,8 +585,6 @@ def set_default_email_digest
# approved :boolean default(FALSE), not null
# approved_by_id :integer
# approved_at :datetime
# topics_entered :integer default(0), not null
# posts_read_count :integer default(0), not null
# digest_after_days :integer
# previous_visit_at :datetime
# banned_at :datetime
Expand All @@ -657,22 +593,18 @@ def set_default_email_digest
# auto_track_topics_after_msecs :integer
# views :integer default(0), not null
# flag_level :integer default(0), not null
# time_read :integer default(0), not null
# days_visited :integer default(0), not null
# ip_address :string
# new_topic_duration_minutes :integer
# external_links_in_new_tab :boolean default(FALSE), not null
# enable_quoting :boolean default(TRUE), not null
# moderator :boolean default(FALSE)
# likes_given :integer default(0), not null
# likes_received :integer default(0), not null
# topic_reply_count :integer default(0), not null
# blocked :boolean default(FALSE)
# dynamic_favicon :boolean default(FALSE), not null
# title :string(255)
# use_uploaded_avatar :boolean default(FALSE)
# uploaded_avatar_template :string(255)
# uploaded_avatar_id :integer
# email_always :boolean default(FALSE), not null
#
# Indexes
#
Expand Down
4 changes: 2 additions & 2 deletions app/models/user_action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,9 @@ def self.ensure_consistency!

def self.update_like_count(user_id, action_type, delta)
if action_type == LIKE
User.where(id: user_id).update_all("likes_given = likes_given + #{delta.to_i}")
UserStat.where(user_id: user_id).update_all("likes_given = likes_given + #{delta.to_i}")
elsif action_type == WAS_LIKED
User.where(id: user_id).update_all("likes_received = likes_received + #{delta.to_i}")
UserStat.where(user_id: user_id).update_all("likes_received = likes_received + #{delta.to_i}")
end
end

Expand Down
13 changes: 7 additions & 6 deletions app/models/user_history.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ def previous_value_is_json?

# == Schema Information
#
# Table name: staff_action_logs
# Table name: user_histories
#
# id :integer not null, primary key
# action :integer not null
# staff_user_id :integer not null
# acting_user_id :integer
# target_user_id :integer
# details :text
# created_at :datetime not null
Expand All @@ -81,12 +81,13 @@ def previous_value_is_json?
# subject :text
# previous_value :text
# new_value :text
# topic_id :integer
#
# Indexes
#
# index_staff_action_logs_on_action_and_id (action,id)
# index_staff_action_logs_on_staff_user_id_and_id (staff_user_id,id)
# index_staff_action_logs_on_subject_and_id (subject,id)
# index_staff_action_logs_on_target_user_id_and_id (target_user_id,id)
# index_staff_action_logs_on_action_and_id (action,id)
# index_staff_action_logs_on_subject_and_id (subject,id)
# index_staff_action_logs_on_target_user_id_and_id (target_user_id,id)
# index_user_histories_on_acting_user_id_and_action_and_id (acting_user_id,action,id)
#

80 changes: 80 additions & 0 deletions app/models/user_stat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,84 @@ class UserStat < ActiveRecord::Base

belongs_to :user

# Updates the denormalized view counts for all users
def self.update_view_counts

# NOTE: we only update the counts for users we have seen in the last hour
# this avoids a very expensive query that may run on the entire user base
# we also ensure we only touch the table if data changes

# Update denormalized topics_entered
exec_sql "UPDATE user_stats SET topics_entered = X.c
FROM
(SELECT v.user_id,
COUNT(DISTINCT parent_id) AS c
FROM views AS v
WHERE parent_type = 'Topic' AND v.user_id IN (
SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at
)
GROUP BY v.user_id) AS X
WHERE
X.user_id = user_stats.user_id AND
X.c <> topics_entered
", seen_at: 1.hour.ago

# Update denormalzied posts_read_count
exec_sql "UPDATE user_stats SET posts_read_count = X.c
FROM
(SELECT pt.user_id,
COUNT(*) AS c
FROM post_timings AS pt
WHERE pt.user_id IN (
SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at
)
GROUP BY pt.user_id) AS X
WHERE X.user_id = user_stats.user_id AND
X.c <> posts_read_count
", seen_at: 1.hour.ago
end

def update_topic_reply_count
self.topic_reply_count =
Topic
.where(['id in (
SELECT topic_id FROM posts p
JOIN topics t2 ON t2.id = p.topic_id
WHERE p.deleted_at IS NULL AND
t2.user_id <> p.user_id AND
p.user_id = ?
)', self.user_id])
.count
end

MAX_TIME_READ_DIFF = 100
# attempt to add total read time to user based on previous time this was called
def update_time_read!
last_seen_key = "user-last-seen:#{id}"
last_seen = $redis.get(last_seen_key)
if last_seen.present?
diff = (Time.now.to_f - last_seen.to_f).round
if diff > 0 && diff < MAX_TIME_READ_DIFF
UserStat.where(user_id: id, time_read: time_read).update_all ["time_read = time_read + ?", diff]
end
end
$redis.set(last_seen_key, Time.now.to_f)
end

end

# == Schema Information
#
# Table name: user_stats
#
# user_id :integer not null, primary key
# has_custom_avatar :boolean default(FALSE), not null
# topics_entered :integer default(0), not null
# time_read :integer default(0), not null
# days_visited :integer default(0), not null
# posts_read_count :integer default(0), not null
# likes_given :integer default(0), not null
# likes_received :integer default(0), not null
# topic_reply_count :integer default(0), not null
#

6 changes: 3 additions & 3 deletions app/models/user_visit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ def self.by_day(sinceDaysAgo=30)

def self.ensure_consistency!
exec_sql <<SQL
UPDATE users u set days_visited =
UPDATE user_stats u set days_visited =
(
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.id
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id
)
WHERE days_visited <>
(
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.id
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id
)
SQL
end
Expand Down
3 changes: 2 additions & 1 deletion app/models/view.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def self.create_for(parent, ip, user=nil)
#
# Indexes
#
# index_views_on_parent_id_and_parent_type (parent_id,parent_type)
# index_views_on_parent_id_and_parent_type (parent_id,parent_type)
# index_views_on_user_id_and_parent_type_and_parent_id (user_id,parent_type,parent_id)
#

Loading

0 comments on commit 5bf26ec

Please sign in to comment.