perf: skip conversation loading in /meta endpoint (#13564)

# Pull Request Template

## Summary
- Adds `perform_meta_only` method to `ConversationFinder` that runs
setup and counts without loading the paginated conversation list
- Updates `/api/v1/conversations/meta` to use `perform_meta_only`
instead of `perform`

## Problem
The `/meta` endpoint calls `ConversationFinder#perform` which:
1. Runs all filters and setup (`set_up`)
2. Computes 3 COUNT queries (`set_count_for_all_conversations`)
3. Filters by assignee type
4. **Builds the full paginated conversation list** with
`.includes(:taggings, :inbox, {assignee: {avatar_attachment: [:blob]}},
{contact: {avatar_attachment: [:blob]}}, :team, :contact_inbox)` +
sorting + pagination

The controller then **discards the conversations** and only uses the
counts:
```ruby
def meta
result = conversation_finder.perform
@conversations_count = result[:count]  # conversations thrown away
end
```

## Type of change

- [x] Performance fix

## How Has This Been Tested?

- [ ] Verify /meta returns correct mine/unassigned/assigned/all counts
- [ ] Verify counts update when switching inbox, team, or status filters
- [ ] Verify conversation list still loads correctly (uses perform, not
affected)
- [ ] Monitor response time reduction for /meta in NewRelic after deploy

## Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
Vishnu Narayanan
2026-02-20 21:20:19 +05:30
committed by GitHub
parent 418bd177f8
commit 2441487a76
3 changed files with 43 additions and 1 deletions

View File

@@ -15,7 +15,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
end
def meta
result = conversation_finder.perform
result = conversation_finder.perform_meta_only
@conversations_count = result[:count]
end

View File

@@ -55,6 +55,22 @@ class ConversationFinder
}
end
def perform_meta_only
set_up
mine_count, unassigned_count, all_count, = set_count_for_all_conversations
assigned_count = all_count - unassigned_count
{
count: {
mine_count: mine_count,
assigned_count: assigned_count,
unassigned_count: unassigned_count,
all_count: all_count
}
}
end
private
def set_up

View File

@@ -190,6 +190,32 @@ describe ConversationFinder do
end
end
context 'with perform_meta_only' do
let(:params) { { assignee_type: 'assigned' } }
it 'returns only count without conversations' do
result = conversation_finder.perform_meta_only
expect(result).to have_key(:count)
expect(result).not_to have_key(:conversations)
end
it 'returns the correct counts' do
result = conversation_finder.perform_meta_only
expect(result[:count]).to eq({
mine_count: 2,
assigned_count: 3,
unassigned_count: 1,
all_count: 4
})
end
it 'returns same counts as perform' do
meta_result = conversation_finder.perform_meta_only
full_result = conversation_finder.perform
expect(meta_result[:count]).to eq(full_result[:count])
end
end
context 'with unattended' do
let(:params) { { status: 'open', assignee_type: 'me', conversation_type: 'unattended' } }