ruby
43 lines · 7 steps
Debouncing search reindex jobs in Rails
A cache-backed lock collapses rapid record changes into a single delayed reindex job.
Explained by
highlit
1class SearchIndexReconciler
2 DEBOUNCE_WINDOW = 5.seconds
3
4 def self.schedule(record)
5 new(record).schedule
6 end
7
8 def initialize(record)
9 @record = record
10 @key = "reconcile:#{record.class.name.underscore}:#{record.id}"
11 end
12
13 def schedule
14 return if debounced?
15
16 ReconcileSearchIndexJob
17 .set(wait: DEBOUNCE_WINDOW)
18 .perform_later(@record.class.name, @record.id)
19
20 mark_scheduled
21 end
22
23 private
24
25 def debounced?
26 !Rails.cache.write(@key, true, unless_exist: true, expires_in: DEBOUNCE_WINDOW)
27 end
28
29 def mark_scheduled
30 Rails.logger.info("[#{self.class}] enqueued reconciliation for #{@key}")
31 end
32end
33
34class ReconcileSearchIndexJob < ApplicationJob
35 queue_as :indexing
36 discard_on ActiveRecord::RecordNotFound
37
38 def perform(class_name, id)
39 record = class_name.constantize.find(id)
40 Rails.cache.delete("reconcile:#{class_name.underscore}:#{id}")
41 Search::Indexer.new(record).reindex!
42 end
43end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1An atomic cache write with unless_exist gives you a distributed debounce lock for free.
- 2Deferring the job with a wait window lets bursts of changes settle into one reindex.
- 3Deleting the debounce key inside the job reopens the window so future edits get scheduled.
Related explainers
ruby
require "charlock_holmes" class TextFileNormalizer DEFAULT_CONFIDENCE = 60
Normalizing text files to clean UTF-8 in Ruby
encoding
text-processing
file-io
Intermediate
8 steps
ruby
require 'date' require 'set' class BusinessDayCalculator
Counting business days in Ruby
dates
sets
ranges
Intermediate
8 steps
php
<?php namespace App\Http\Controllers;
A cached autocomplete endpoint in Laravel
caching
validation
query-ranking
Intermediate
8 steps
ruby
class Document < ApplicationRecord class StaleObjectError < StandardError def initialize(id) super("Document ##{id} was modified by another process")
Optimistic locking with retries in Rails
optimistic-locking
concurrency
transactions
Advanced
8 steps
ruby
class BackfillUsersFullName < ActiveRecord::Migration[7.1] disable_ddl_transaction! BATCH_SIZE = 5_000
How to backfill a column safely in Rails
migrations
batching
backfill
Intermediate
7 steps
go
package handler type flightResult struct { status int
Deduping in-flight requests in Gin
middleware
concurrency
deduplication
Advanced
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/debouncing-search-reindex-jobs-in-rails-explained-ruby-5e22/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.