fix(perf): disable tags counter cache to prevent label deadlocks (#14021)
Label attach/detach against a shared label no longer deadlocks under parallel load. During high-concurrency label writes (for example, a broadcast script attaching a campaign label to many conversations at once), Chatwoot previously hit periodic `ActiveRecord::Deadlocked` errors and tail-latency spikes on the tags table. This PR removes the contention by disabling the `acts-as-taggable-on` counter cache, which Chatwoot never reads. ## Closes Fixes [INF-68](https://linear.app/chatwoot/issue/INF-68) (event 2) ## How to reproduce 1. Seed an account with ~20 conversations and 5 labels. 2. Spawn 20 parallel threads, each calling `conversation.update!(label_list: shared_labels.shuffle)` against different conversations. 3. Observe `ActiveRecord::Deadlocked` exceptions and p99 label-write latency well above 1s. With the counter cache disabled, the deadlock cycle cannot form. ## How this was tested - Ran a 20-thread synthetic load test locally, each thread attaching 5 shared labels (shuffled per request) to different conversations. With the counter cache enabled: 8 deadlocks across 300 attempts, p99 ~2.2s. With the counter cache disabled: zero deadlocks, p99 ~306ms (roughly 85% tail-latency reduction). The `UPDATE tags SET taggings_count = ...` statement disappears from the SQL log entirely. - Verified at boot via `rails runner` that `ActsAsTaggableOn::Tagging.reflect_on_association(:tag).options[:counter_cache]` returns `false` after the initializer runs. The gem wires `belongs_to :tag, counter_cache: ActsAsTaggableOn.tags_counter` at class-load time, so the initializer must sit ahead of the `Tagging` autoload path; this confirms it does.
This commit is contained in:
10
config/initializers/acts_as_taggable_on.rb
Normal file
10
config/initializers/acts_as_taggable_on.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
# Disable the taggings counter cache on tags.
|
||||
#
|
||||
# Each tagging INSERT/DELETE would otherwise issue
|
||||
# `UPDATE tags SET taggings_count = ... WHERE id = ?`, which serialises
|
||||
# concurrent label writes on the same tag row and deadlocks under
|
||||
# parallel multi-label updates (refer INF-68)
|
||||
#
|
||||
# Safe because Chatwoot does not read `tags.taggings_count` anywhere;
|
||||
# label reports compute counts directly via GROUP BY on taggings.
|
||||
ActsAsTaggableOn.tags_counter = false
|
||||
Reference in New Issue
Block a user