From 8c72f7bfbcfaab59540c59703ccf15a023ed6730 Mon Sep 17 00:00:00 2001
From: Eric Pugh
Date: Fri, 10 Jan 2025 08:05:57 -0500
Subject: [PATCH] Backout counter_cache (#1165)
It caused other issues like in tests, and I'm not sure it was a slam dunk for performance. Instead we need to be smarter about how we decide to load this data.
---
.env.example | 3 ++
.../api/v1/cases/dropdown_controller.rb | 2 +-
.../authentication/current_book_manager.rb | 34 ++++---------------
.../authentication/current_case_manager.rb | 34 +++----------------
app/controllers/home_controller.rb | 18 +++++-----
app/javascript/application2.js | 1 +
app/models/book.rb | 11 +++++-
app/models/query_doc_pair.rb | 2 +-
app/views/home/_book_summary.html.erb | 5 +--
app/views/home/show.html.erb | 2 +-
app/views/layouts/_dropdown_case.html.erb | 2 ++
app/views/layouts/_header.html.erb | 5 +--
...3404_add_query_doc_pairs_count_to_books.rb | 5 ---
...218173819_reset_all_book_cache_counters.rb | 17 ----------
db/schema.rb | 3 +-
.../api/v1/cases/dropdown_controller_test.rb | 4 +--
test/fixtures/books.yml | 1 -
test/models/book_test.rb | 1 -
18 files changed, 45 insertions(+), 105 deletions(-)
create mode 100644 app/views/layouts/_dropdown_case.html.erb
delete mode 100644 db/migrate/20241218173404_add_query_doc_pairs_count_to_books.rb
delete mode 100644 db/migrate/20241218173819_reset_all_book_cache_counters.rb
diff --git a/.env.example b/.env.example
index 607a709dc..e37cca7f9 100644
--- a/.env.example
+++ b/.env.example
@@ -5,6 +5,9 @@ PORT=3000
RACK_ENV=development
RAILS_ENV=development
+# This makes Quepid display detailed error messages.
+QUEPID_CONSIDER_ALL_REQUESTS_LOCAL=true
+
MAX_THREADS=2
WEB_CONCURRENCY=2
diff --git a/app/controllers/api/v1/cases/dropdown_controller.rb b/app/controllers/api/v1/cases/dropdown_controller.rb
index e1663f85e..085ff061f 100644
--- a/app/controllers/api/v1/cases/dropdown_controller.rb
+++ b/app/controllers/api/v1/cases/dropdown_controller.rb
@@ -5,7 +5,7 @@ module V1
module Cases
class DropdownController < Api::ApiController
def index
- @cases = recent_cases 3
+ @cases = recent_cases 4
respond_with @cases
end
diff --git a/app/controllers/concerns/authentication/current_book_manager.rb b/app/controllers/concerns/authentication/current_book_manager.rb
index 6147558b0..eff4f7e06 100644
--- a/app/controllers/concerns/authentication/current_book_manager.rb
+++ b/app/controllers/concerns/authentication/current_book_manager.rb
@@ -20,45 +20,23 @@ def check_book
end
def set_recent_books
- @recent_books = recent_books(3)
+ @recent_books = recent_books(4)
end
- # rubocop:disable Metrics/MethodLength
def recent_books count
if current_user
- # Using joins/includes will not return the proper list in the
- # correct order because rails refuses to include the
- # `book_metadata`.`last_viewed_at` column in the SELECT statement
- # which will then cause the ordering not to work properly.
- # So instead, we have this beauty!
- sql = "
- SELECT DISTINCT `books`.`id`, `book_metadata`.`last_viewed_at`
- FROM `books`
- LEFT OUTER JOIN `book_metadata` ON `book_metadata`.`book_id` = `books`.`id`
- LEFT OUTER JOIN `teams_books` ON `teams_books`.`book_id` = `books`.`id`
- LEFT OUTER JOIN `teams` ON `teams`.`id` = `teams_books`.`team_id`
- LEFT OUTER JOIN `teams_members` ON `teams_members`.`team_id` = `teams`.`id`
- LEFT OUTER JOIN `users` ON `users`.`id` = `teams_members`.`member_id`
- WHERE (`teams_members`.`member_id` = #{current_user.id} OR `books`.`owner_id` = #{current_user.id})
- ORDER BY `book_metadata`.`last_viewed_at` DESC, `books`.`id` DESC
- LIMIT #{count}
- "
-
- results = ActiveRecord::Base.connection.execute(sql)
-
- book_ids = results.map do |row|
- row.first.to_i
- end
+ book_ids = current_user.book_metadata.order(last_viewed_at: :desc).limit(count).pluck(:book_id)
# map to objects
- # cases = Case.includes(:tries).where(id: [ case_ids ])
- books = Book.where(id: [ book_ids ])
+ books = current_user.books_involved_with.where(id: book_ids)
+
+ # the WHERE IN clause doesn't guarantee returning in order, so this sorts the cases in order of last viewing.
books = books.sort_by { |x| book_ids.index x.id }
+
else
books = []
end
books
end
- # rubocop:enable Metrics/MethodLength
end
end
diff --git a/app/controllers/concerns/authentication/current_case_manager.rb b/app/controllers/concerns/authentication/current_case_manager.rb
index bc6fc6c76..bbe8b3c38 100644
--- a/app/controllers/concerns/authentication/current_case_manager.rb
+++ b/app/controllers/concerns/authentication/current_case_manager.rb
@@ -35,47 +35,23 @@ def set_case
end
def set_recent_cases
- @recent_cases = recent_cases(3)
+ @recent_cases = recent_cases(4)
end
- # rubocop:disable Metrics/MethodLength
def recent_cases count
if current_user
- # Using joins/includes will not return the proper list in the
- # correct order because rails refuses to include the
- # `case_metadata`.`last_viewed_at` column in the SELECT statement
- # which will then cause the ordering not to work properly.
- # So instead, we have this beauty!
- sql = "
- SELECT DISTINCT `cases`.`id`, `case_metadata`.`last_viewed_at`
- FROM `cases`
- LEFT OUTER JOIN `case_metadata` ON `case_metadata`.`case_id` = `cases`.`id`
- LEFT OUTER JOIN `teams_cases` ON `teams_cases`.`case_id` = `cases`.`id`
- LEFT OUTER JOIN `teams` ON `teams`.`id` = `teams_cases`.`team_id`
- LEFT OUTER JOIN `teams_members` ON `teams_members`.`team_id` = `teams`.`id`
- LEFT OUTER JOIN `users` ON `users`.`id` = `teams_members`.`member_id`
- WHERE (`teams_members`.`member_id` = #{current_user.id} OR `cases`.`owner_id` = #{current_user.id})
- AND (`cases`.`archived` = false OR `cases`.`archived` IS NULL)
- ORDER BY `case_metadata`.`last_viewed_at` DESC, `cases`.`id` DESC
- LIMIT #{count}
- "
-
- results = ActiveRecord::Base.connection.execute(sql)
-
- case_ids = results.map do |row|
- row.first.to_i
- end
+ case_ids = current_user.case_metadata.order(last_viewed_at: :desc).limit(count).pluck(:case_id)
# map to objects
- # cases = Case.includes(:tries).where(id: [ case_ids ])
- cases = Case.where(id: [ case_ids ])
+ cases = current_user.cases_involved_with.where(id: case_ids).not_archived
+
+ # the WHERE IN clause doesn't guarantee returning in order, so this sorts the cases in order of last viewing.
cases = cases.sort_by { |x| case_ids.index x.id }
else
cases = []
end
cases
end
- # rubocop:enable Metrics/MethodLength
def check_case
render json: { message: 'Case not found!' }, status: :not_found unless @case
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index ae2a15f1d..ee36b3a8a 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -7,6 +7,7 @@ class HomeController < ApplicationController
def show
# with_counts adds a `case.queries_count` field, which avoids loading
# all queries and makes bullet happy.
+ # @cases = recent_cases(30)
@cases = @current_user.cases_involved_with.not_archived.with_counts
.includes([ :metadata ])
.order('`case_metadata`.`last_viewed_at` DESC, `cases`.`id` DESC')
@@ -15,15 +16,16 @@ def show
@most_recent_cases = @cases[0...4].sort_by { |c| c.case_name.downcase }
@most_recent_books = recent_books(4)
- @lookup_for_books = {}
- @most_recent_books.each do |book|
- judged_by_current_user = book.judgements.where(user: @current_user).count
- if judged_by_current_user.positive? && judged_by_current_user < book.query_doc_pairs.size
- @lookup_for_books[book] = book.query_doc_pairs.size - judged_by_current_user
- end
- end
- @most_recent_books.sort_by!(&:name)
+ # @lookup_for_books = {}
+ # @most_recent_books.each do |book|
+ # judged_by_current_user = book.judgements.where(user: @current_user).count
+ # if judged_by_current_user.positive? && judged_by_current_user < book.query_doc_pairs.size
+ # @lookup_for_books[book] = book.query_doc_pairs.size - judged_by_current_user
+ # end
+ # end
+
+ @most_recent_books = @most_recent_books.sort_by { |b| b.name.downcase }
# Homepage is too slow so we have to cut some stuff out ;-(
# candidate_cases = @cases.select { |kase| kase.scores.scored.count.positive? }
diff --git a/app/javascript/application2.js b/app/javascript/application2.js
index d6ae5ddcf..d57d1b3b6 100644
--- a/app/javascript/application2.js
+++ b/app/javascript/application2.js
@@ -12,6 +12,7 @@ import Cookies from 'js-cookie'
LocalTime.start()
Turbo.config.drive.progressBarDelay = 1
+Turbo.session.drive = false
import "vega"
import "vega-lite"
diff --git a/app/models/book.rb b/app/models/book.rb
index d0336954b..a53beaa6d 100644
--- a/app/models/book.rb
+++ b/app/models/book.rb
@@ -9,7 +9,6 @@
# import_job :string(255)
# name :string(255)
# populate_job :string(255)
-# query_doc_pairs_count :integer default(0), not null
# show_rank :boolean default(FALSE)
# support_implicit_judgements :boolean
# created_at :datetime not null
@@ -66,6 +65,16 @@ class Book < ApplicationRecord
# Scopes
include ForUserScope
+ scope :with_counts, -> {
+ select <<~SQL.squish
+ books.*,
+ (
+ SELECT COUNT(query_doc_pairs.id) FROM query_doc_pairs
+ WHERE book_id = books.id
+ ) AS query_doc_pairs_count
+ SQL
+ }
+
private
def delete_attachments
diff --git a/app/models/query_doc_pair.rb b/app/models/query_doc_pair.rb
index 227b79ba3..d1ca4669f 100644
--- a/app/models/query_doc_pair.rb
+++ b/app/models/query_doc_pair.rb
@@ -25,7 +25,7 @@
# fk_rails_... (book_id => books.id)
#
class QueryDocPair < ApplicationRecord
- belongs_to :book, counter_cache: true
+ belongs_to :book
has_many :judgements, dependent: :destroy, autosave: true
validates :query_text, presence: true, length: { maximum: 2048 }
diff --git a/app/views/home/_book_summary.html.erb b/app/views/home/_book_summary.html.erb
index cd12c0e98..3bb1227c6 100644
--- a/app/views/home/_book_summary.html.erb
+++ b/app/views/home/_book_summary.html.erb
@@ -12,10 +12,7 @@
-
- <% if @lookup_for_books[book] && @lookup_for_books[book] > 0 %>
- 🐸 You need to judge <%= @lookup_for_books[book] %> out <%= book.query_doc_pairs.size %> query/doc pairs.
- <% end %>
+
<%= link_to 'Judge', book_judge_path(book), data: { turbo_prefetch: false }, class: 'btn btn-sm btn-primary', role: 'button' %>
diff --git a/app/views/home/show.html.erb b/app/views/home/show.html.erb
index f63485afd..293709562 100644
--- a/app/views/home/show.html.erb
+++ b/app/views/home/show.html.erb
@@ -54,7 +54,7 @@ Inspired by https://dev.to/themesberg/tutorial-how-to-build-a-simple-admin-dashb
- <%= render partial: "case", collection: @cases, as: :kase %>
+ <%= render partial: "case", collection: @cases, as: :kase, cached: true %>
diff --git a/app/views/layouts/_dropdown_case.html.erb b/app/views/layouts/_dropdown_case.html.erb
new file mode 100644
index 000000000..20faeda53
--- /dev/null
+++ b/app/views/layouts/_dropdown_case.html.erb
@@ -0,0 +1,2 @@
+<%= link_to_core_case kase.case_name, kase, kase.last_try_number %>
+
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index 833081725..5006e88d2 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -18,10 +18,7 @@
RECENT CASES
- <% set_recent_cases.each do |kase| %>
- <%= link_to_core_case kase.case_name, kase, kase.last_try_number %>
-
- <% end %>
+ <%= render partial: 'layouts/dropdown_case', collection: set_recent_cases, as: :kase, cached: true %>
diff --git a/db/migrate/20241218173404_add_query_doc_pairs_count_to_books.rb b/db/migrate/20241218173404_add_query_doc_pairs_count_to_books.rb
deleted file mode 100644
index 07580b9dc..000000000
--- a/db/migrate/20241218173404_add_query_doc_pairs_count_to_books.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddQueryDocPairsCountToBooks < ActiveRecord::Migration[8.0]
- def change
- add_column :books, :query_doc_pairs_count, :integer, default: 0, null: false
- end
-end
diff --git a/db/migrate/20241218173819_reset_all_book_cache_counters.rb b/db/migrate/20241218173819_reset_all_book_cache_counters.rb
deleted file mode 100644
index fa5ccd897..000000000
--- a/db/migrate/20241218173819_reset_all_book_cache_counters.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class ResetAllBookCacheCounters < ActiveRecord::Migration[8.0]
- def up
-
- Book.all.each do |book|
-
- Book.reset_counters(book.id, :query_doc_pairs)
-
- end
-
- end
-
- def down
-
- # no rollback needed
-
- end
-end
diff --git a/db/schema.rb b/db/schema.rb
index 12533d079..878471aa8 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].define(version: 2025_01_07_195202) do
+ActiveRecord::Schema[8.0].define(version: 2025_01_08_171851) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_bin", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
@@ -196,7 +196,6 @@
t.string "export_job"
t.string "import_job"
t.string "populate_job"
- t.integer "query_doc_pairs_count", default: 0, null: false
t.index ["selection_strategy_id"], name: "index_books_on_selection_strategy_id"
end
diff --git a/test/controllers/api/v1/cases/dropdown_controller_test.rb b/test/controllers/api/v1/cases/dropdown_controller_test.rb
index 8b9d3a57d..8d8bcc90b 100644
--- a/test/controllers/api/v1/cases/dropdown_controller_test.rb
+++ b/test/controllers/api/v1/cases/dropdown_controller_test.rb
@@ -87,7 +87,7 @@ class DropdownControllerTest < ActionController::TestCase
assert_not_includes ids, archived.id
end
- test 'limits list to 3 cases' do
+ test 'limits list to 4 cases' do
get :index
assert_response :ok
@@ -95,7 +95,7 @@ class DropdownControllerTest < ActionController::TestCase
body = response.parsed_body
cases = body['all_cases']
- assert cases.length <= 3
+ assert cases.length <= 4
end
test 'returns list of cases ordered by last viewed date' do
diff --git a/test/fixtures/books.yml b/test/fixtures/books.yml
index df3543bc8..426f33fb1 100644
--- a/test/fixtures/books.yml
+++ b/test/fixtures/books.yml
@@ -7,7 +7,6 @@
# import_job :string(255)
# name :string(255)
# populate_job :string(255)
-# query_doc_pairs_count :integer default(0), not null
# show_rank :boolean default(FALSE)
# support_implicit_judgements :boolean
# created_at :datetime not null
diff --git a/test/models/book_test.rb b/test/models/book_test.rb
index 48eaf75f1..6334f13e6 100644
--- a/test/models/book_test.rb
+++ b/test/models/book_test.rb
@@ -9,7 +9,6 @@
# import_job :string(255)
# name :string(255)
# populate_job :string(255)
-# query_doc_pairs_count :integer default(0), not null
# show_rank :boolean default(FALSE)
# support_implicit_judgements :boolean
# created_at :datetime not null