Skip to content

Commit

Permalink
PERF: removed N*2+1 in availability topic (#599)
Browse files Browse the repository at this point in the history
WE "recently" did a few changes in the way we use the availability topic

- We used to have 1 topic per year, but have now moved to just using one topic and deleted past posts
- We used to delete past automatic holidays, but we don't anymore since we use them to keep track of #leave days used
- There are more employees covering more regions

All three changes made the query that list all the events to be displayed in the availability topic go 💥

It used to be

- 1 query to list all events from 6 months ago up to 6 months in the future
- 1 query for every events to load the associated user
- 1 query for every events to load the associated's user timezone

So basically, a N*2 + 1 😅

This commit fixes the issue by doing

- 1 query to get all "standalone" events (those associated to a post)
- 1 query to get all the users timezones (just the ones who have an holiday in the current period).
- 1 query to get all the "automatic" events (aka. holidays that are comming from the holidays gem).

So we went from N*2 + 1 down to 3 🚀
  • Loading branch information
ZogStriP authored Aug 14, 2024
1 parent d6ad734 commit f268ecc
Showing 1 changed file with 66 additions and 37 deletions.
103 changes: 66 additions & 37 deletions plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -358,52 +358,81 @@ module ::DiscoursePostEvent
end

add_to_serializer(:post, :calendar_details, include_condition: -> { object.is_first_post? }) do
grouped = {}
standalones = []

CalendarEvent
.where(topic_id: object.topic_id)
.where("post_id IS NOT NULL OR start_date >= ?", 6.months.ago)
.order(:start_date, :end_date)
.each do |event|
if event.post_id
standalones << {
start_date = 6.months.ago

standalone_sql = <<~SQL
SELECT post_number, description, start_date, end_date, username, recurrence, timezone
FROM calendar_events
WHERE topic_id = :topic_id
AND post_id IS NOT NULL
ORDER BY start_date, end_date
SQL

standalones =
DB
.query(standalone_sql, topic_id: object.topic_id)
.map do |row|
{
type: :standalone,
post_number: event.post_number,
message: event.description,
from: event.start_date,
to: event.end_date,
username: event.username,
recurring: event.recurrence,
post_url: Post.url("-", event.topic_id, event.post_number),
timezone: event.timezone,
}
else
identifier = "#{event.region.split("_").first}-#{event.start_date.strftime("%Y-%j")}"

grouped[identifier] ||= {
type: :grouped,
from: event.start_date,
timezone: event.timezone,
name: [],
users: [],
post_number: row.post_number,
message: row.description,
from: row.start_date,
to: row.end_date,
username: row.username,
recurring: row.recurrence,
post_url: Post.url("-", object.topic_id, row.post_number),
timezone: row.timezone,
}
end

user = User.find_by_username(event.username)
timezones =
UserOption
.where(
user_id:
CalendarEvent.where(
topic_id: object.topic_id,
post_id: nil,
start_date: start_date..,
).select(:user_id),
)
.where("LENGTH(COALESCE(timezone, '')) > 0")
.pluck(:user_id, :timezone)
.to_h

grouped[identifier][:name] << event.description
grouped[identifier][:users] << {
username: event.username,
timezone: user.present? ? user.user_option.timezone : nil,
}
end
grouped = {}

grouped_sql = <<~SQL
SELECT region, start_date, timezone, user_id, username, description
FROM calendar_events
WHERE topic_id = :topic_id
AND post_id IS NULL
AND start_date >= :start_date
ORDER BY region, start_date
SQL

DB
.query(grouped_sql, topic_id: object.topic_id, start_date: start_date)
.each do |row|
identifier = "#{row.region.split("_").first}-#{row.start_date.strftime("%Y-%j")}"

grouped[identifier] ||= {
type: :grouped,
from: row.start_date,
timezone: row.timezone,
name: [],
users: [],
}

grouped[identifier][:name] << row.description
grouped[identifier][:users] << { username: row.username, timezone: timezones[row.user_id] }
end

grouped.each do |_, v|
v[:name].sort!.uniq!
v[:name].uniq!
v[:name].sort!
v[:name] = v[:name].join(", ")
v[:users].sort! { |a, b| a[:username] <=> b[:username] }
v[:users].uniq! { |u| u[:username] }
v[:users].sort! { |a, b| a[:username] <=> b[:username] }
end

standalones + grouped.values
Expand Down

0 comments on commit f268ecc

Please sign in to comment.