feat: add cron job to remove orphan conversations (#13335)
## Description This PR includes cron job to delete the orphans ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## 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 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces a scheduled cleanup for conversations missing `contact` or `inbox`. > > - Adds `Internal::RemoveOrphanConversationsService` to batch-delete orphan conversations (scoped by optional `account`, within a configurable `days` window) with progress logging > - New `Internal::RemoveOrphanConversationsJob` that invokes the service; scheduled via `config/schedule.yml` to run every 12 hours on `housekeeping` queue > - Refactors rake task `chatwoot:ops:cleanup_orphan_conversations` to use the service and report `total_deleted` after confirmation > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 59a24715cc59f048d08db3f588cde6fa036f3166. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
committed by
GitHub
parent
aaeea6c9bf
commit
d166ae73bc
11
app/jobs/internal/remove_orphan_conversations_job.rb
Normal file
11
app/jobs/internal/remove_orphan_conversations_job.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# housekeeping
|
||||
# remove conversations that do not have a contact_id
|
||||
# orphan conversations without contact cannot be accessed or used
|
||||
|
||||
class Internal::RemoveOrphanConversationsJob < ApplicationJob
|
||||
queue_as :housekeeping
|
||||
|
||||
def perform
|
||||
Internal::RemoveOrphanConversationsService.new.perform
|
||||
end
|
||||
end
|
||||
34
app/services/internal/remove_orphan_conversations_service.rb
Normal file
34
app/services/internal/remove_orphan_conversations_service.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
class Internal::RemoveOrphanConversationsService
|
||||
def initialize(account: nil, days: 1)
|
||||
@account = account
|
||||
@days = days
|
||||
end
|
||||
|
||||
def perform
|
||||
orphan_conversations = build_orphan_conversations_query
|
||||
total_deleted = 0
|
||||
|
||||
Rails.logger.info '[RemoveOrphanConversationsService] Starting removal of orphan conversations'
|
||||
|
||||
orphan_conversations.find_in_batches(batch_size: 1000) do |batch|
|
||||
conversation_ids = batch.map(&:id)
|
||||
Conversation.where(id: conversation_ids).destroy_all
|
||||
total_deleted += batch.size
|
||||
Rails.logger.info "[RemoveOrphanConversationsService] Deleted #{batch.size} orphan conversations (#{total_deleted} total)"
|
||||
end
|
||||
|
||||
Rails.logger.info "[RemoveOrphanConversationsService] Completed. Total deleted: #{total_deleted}"
|
||||
total_deleted
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_orphan_conversations_query
|
||||
base = @account ? @account.conversations : Conversation.all
|
||||
base = base.where('conversations.created_at > ?', @days.days.ago)
|
||||
base = base.left_outer_joins(:contact, :inbox)
|
||||
|
||||
# Find conversations whose associated contact or inbox record is missing
|
||||
base.where(contacts: { id: nil }).or(base.where(inboxes: { id: nil }))
|
||||
end
|
||||
end
|
||||
@@ -66,3 +66,10 @@ remove_old_notification_job:
|
||||
cron: '30 22 * * *'
|
||||
class: 'Notification::RemoveOldNotificationJob'
|
||||
queue: purgable
|
||||
|
||||
# executed every 12 hours
|
||||
# to remove orphan conversations without contact
|
||||
remove_orphan_conversations_job:
|
||||
cron: '0 */12 * * *'
|
||||
class: 'Internal::RemoveOrphanConversationsJob'
|
||||
queue: housekeeping
|
||||
|
||||
@@ -15,13 +15,13 @@ namespace :chatwoot do
|
||||
days_input = $stdin.gets.strip
|
||||
days = days_input.empty? ? 7 : days_input.to_i
|
||||
|
||||
# Build a common base relation with identical joins for OR compatibility
|
||||
service = Internal::RemoveOrphanConversationsService.new(account: account, days: days)
|
||||
|
||||
# Preview count using the same query logic
|
||||
base = account
|
||||
.conversations
|
||||
.where('conversations.created_at > ?', days.days.ago)
|
||||
.left_outer_joins(:contact, :inbox)
|
||||
|
||||
# Find conversations whose associated contact or inbox record is missing
|
||||
conversations = base.where(contacts: { id: nil }).or(base.where(inboxes: { id: nil }))
|
||||
|
||||
count = conversations.count
|
||||
@@ -31,8 +31,8 @@ namespace :chatwoot do
|
||||
print 'Do you want to delete these conversations? (y/N): '
|
||||
confirm = $stdin.gets.strip.downcase
|
||||
if %w[y yes].include?(confirm)
|
||||
conversations.destroy_all
|
||||
puts 'Conversations deleted.'
|
||||
total_deleted = service.perform
|
||||
puts "#{total_deleted} conversations deleted."
|
||||
else
|
||||
puts 'No conversations were deleted.'
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user