FIX: on rebakes should not recreate quoted_posts records

FIX: on rebakes should not recreate quoted_posts records

Previously every rebake would remove and recreate records in this table This caused created_at and updated_at to keep changing

Yes, I know the SQL is somewhat complex, but this makes quote extraction more efficient cause we do everything in 2 round trips.

This also removes some concurrency protection we should no longer need

diff --git a/app/models/quoted_post.rb b/app/models/quoted_post.rb
index 6726de0..7a7111f 100644
--- a/app/models/quoted_post.rb
+++ b/app/models/quoted_post.rb
@@ -11,8 +11,6 @@ class QuotedPost < ActiveRecord::Base
 
     uniq = {}
 
-    DB.exec("DELETE FROM quoted_posts WHERE post_id = :post_id", post_id: post.id)
-
     doc.css("aside.quote[data-topic]").each do |a|
       topic_id = a['data-topic'].to_i
       post_number = a['data-post'].to_i
@@ -22,21 +20,45 @@ class QuotedPost < ActiveRecord::Base
       next if post.topic_id == topic_id && post.post_number == post_number
 
       uniq[[topic_id, post_number]] = true
+    end
+
+    if uniq.length == 0
+      DB.exec("DELETE FROM quoted_posts WHERE post_id = :post_id", post_id: post.id)
+    else
+
+      args = {
+        post_id: post.id,
+        topic_ids: uniq.keys.map(&:first),
+        post_numbers: uniq.keys.map(&:second)
+      }
+
+      DB.exec(<<~SQL, args)
+        INSERT INTO quoted_posts (post_id, quoted_post_id, created_at, updated_at)
+        SELECT :post_id, p.id, current_timestamp, current_timestamp
+        FROM posts p
+        JOIN (
+          SELECT
+            unnest(ARRAY[:topic_ids]) topic_id,
+            unnest(ARRAY[:post_numbers]) post_number
+        ) X ON X.topic_id = p.topic_id AND X.post_number = p.post_number
+        LEFT JOIN quoted_posts q on q.post_id = :post_id AND q.quoted_post_id = p.id
+        WHERE q.id IS NULL
+      SQL
 
-      begin
-        # It would be so much nicer if we used post_id in quotes
-        DB.exec(<<~SQL, post_id: post.id, post_number: post_number, topic_id: topic_id)
-          INSERT INTO quoted_posts (post_id, quoted_post_id, created_at, updated_at)
-            SELECT :post_id, p.id, current_timestamp, current_timestamp
-            FROM posts p
-              LEFT JOIN quoted_posts q on q.post_id = :post_id AND q.quoted_post_id = p.id
-            WHERE post_number = :post_number AND
-                  topic_id = :topic_id AND
-                  q.id IS NULL
-        SQL
-      rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation
-        # it's fine
-      end
+      DB.exec(<<~SQL, args)
+        DELETE FROM quoted_posts
+        WHERE post_id = :post_id
+        AND id IN (
+          SELECT q1.id FROM quoted_posts q1
+          LEFT JOIN posts p1 ON p1.id = q1.quoted_post_id
+          LEFT JOIN (
+            SELECT
+              unnest(ARRAY[:topic_ids]) topic_id,
+              unnest(ARRAY[:post_numbers]) post_number
+          ) X on X.topic_id = p1.topic_id AND X.post_number = p1.post_number
+          WHERE q1.post_id = :post_id AND X.topic_id IS NULL
+        )
+      SQL
     end
 
     # simplest place to add this code
diff --git a/spec/models/quoted_post_spec.rb b/spec/models/quoted_post_spec.rb
index 593fe6f..fbe0429 100644
--- a/spec/models/quoted_post_spec.rb
+++ b/spec/models/quoted_post_spec.rb
@@ -70,11 +70,19 @@ describe QuotedPost do
     HTML
 
     QuotedPost.create!(post_id: post2.id, quoted_post_id: 999)
+    quote = QuotedPost.create!(post_id: post2.id, quoted_post_id: post1.id)
+    original_date = quote.created_at
+
+    freeze_time 1.hour.from_now
 
     QuotedPost.extract_from(post2)
     expect(QuotedPost.where(post_id: post2.id).count).to eq(1)
     expect(QuotedPost.find_by(post_id: post2.id, quoted_post_id: post1.id)).not_to eq(nil)
 
+    quote.reload
+
+    expect(original_date).to eq_time(quote.created_at)
+
     expect(post2.reply_quoted).to eq(false)
   end
 end

GitHub sha: b2dc65f9

1 Like

FIX: grant first quote at the date post was created

This commit has been mentioned on Discourse Meta. There might be relevant details there:

1 Like