Created
February 20, 2021 19:26
-
-
Save val99erie/021840116bcd0403dcf51147f7cf809d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# See the comments in the ProjectPositionSequencer class for info about how the gaps are set | |
# for the "position" of the Project records. | |
# Goals of this class: | |
# * Reset the positions so that they have proper gaps between records. | |
# * Try to perform the updates in fewer SQL queries, so that it's not 1 update per record. | |
# Assumption: It's okay if the update takes a long time. For now, the rebalancer is rarely | |
# needed, and it is run manually. It won't be run at a time-sensitive moment, like for | |
# example, during an HTTP request/response. | |
# Assumption: We don't need to lock the records during the rebalance. During prototype | |
# phase, we don't yet have any situation of records being editing during rebalance. In the | |
# future, project lists will be scoped by user, and rebalancing will be more selectively | |
# targeted. | |
# Assumption: There will never be enough Project records (scoped per user) that we need to | |
# worry about hitting the max value for the position column. So for now, don't worry about | |
# that edge case. Just let Postgres throw an error. | |
# (See: ProjectPositionSequencer.max_position) | |
class ProjectPositionBalancer | |
def self.rebalance | |
Project.connection.exec_update(rebalancing_query) | |
end | |
def self.gap | |
ProjectPositionSequencer.gap | |
end | |
# A self-join on the "projects" table (P1, P2, P3). | |
# | |
# The "P2" sub-query does this: | |
# 1. Gets a list of all Project records in the correct | |
# order by position. | |
# 2. Uses a window function called row_number() to | |
# label each record with its row number in the | |
# ordered list. | |
# | |
# Then I can rebalance the gaps just by multiplying the | |
# record's row number by the desired gap. | |
# | |
def self.rebalancing_query | |
<<-SQL | |
UPDATE projects P1 | |
SET position = P3.row_num * #{gap} | |
FROM ( | |
SELECT P2.position, row_number() over (ORDER BY P2.position) AS row_num | |
FROM projects P2 | |
) P3 | |
WHERE P3.position = P1.position; | |
SQL | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment