mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	feat: Add job to remove stale contact inboxes (#8096)
This commit is contained in:
		| @@ -253,3 +253,9 @@ AZURE_APP_SECRET= | |||||||
|  |  | ||||||
| # Sentiment analysis model file path | # Sentiment analysis model file path | ||||||
| SENTIMENT_FILE_PATH= | SENTIMENT_FILE_PATH= | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Housekeeping/Performance related configurations | ||||||
|  | # Set to true if you want to remove stale contact inboxes | ||||||
|  | # contact_inboxes with no conversation older than 90 days will be removed | ||||||
|  | # REMOVE_STALE_CONTACT_INBOX_JOB_STATUS=false | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								app/jobs/internal/remove_stale_contact_inboxes_job.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/jobs/internal/remove_stale_contact_inboxes_job.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | # housekeeping | ||||||
|  | # remove contact inboxes that does not have any conversations | ||||||
|  | # and are older than 3 months | ||||||
|  |  | ||||||
|  | class Internal::RemoveStaleContactInboxesJob < ApplicationJob | ||||||
|  |   queue_as :scheduled_jobs | ||||||
|  |  | ||||||
|  |   def perform | ||||||
|  |     Internal::RemoveStaleContactInboxesService.new.perform | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -33,6 +33,13 @@ class ContactInbox < ApplicationRecord | |||||||
|  |  | ||||||
|   has_many :conversations, dependent: :destroy_async |   has_many :conversations, dependent: :destroy_async | ||||||
|  |  | ||||||
|  |   # contact_inboxes that are not associated with any conversation | ||||||
|  |   scope :stale_without_conversations, lambda { |time_period| | ||||||
|  |     left_joins(:conversations) | ||||||
|  |       .where('contact_inboxes.created_at < ?', time_period) | ||||||
|  |       .where(conversations: { contact_id: nil }) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   def webhook_data |   def webhook_data | ||||||
|     { |     { | ||||||
|       id: id, |       id: id, | ||||||
|   | |||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | class Internal::RemoveStaleContactInboxesService | ||||||
|  |   def perform | ||||||
|  |     return unless remove_stale_contact_inbox_job_enabled? | ||||||
|  |  | ||||||
|  |     time_period = 90.days.ago | ||||||
|  |     contact_inboxes_to_delete = stale_contact_inboxes(time_period) | ||||||
|  |  | ||||||
|  |     log_stale_contact_inboxes_deletion(contact_inboxes_to_delete, time_period) | ||||||
|  |  | ||||||
|  |     # Since the number of records to delete is very high, | ||||||
|  |     # delete_all would be faster than destroy_all since it operates at database level | ||||||
|  |     # and avoid loading all the records in memory | ||||||
|  |     # Transaction and batching is used to avoid deadlock and memory issues | ||||||
|  |     ContactInbox.transaction do | ||||||
|  |       contact_inboxes_to_delete | ||||||
|  |         .find_in_batches(batch_size: 10_000) do |group| | ||||||
|  |           ContactInbox.where(id: group.map(&:id)).delete_all | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def remove_stale_contact_inbox_job_enabled? | ||||||
|  |     job_status = ENV.fetch('REMOVE_STALE_CONTACT_INBOX_JOB_STATUS', false) | ||||||
|  |     return false unless ActiveModel::Type::Boolean.new.cast(job_status) | ||||||
|  |  | ||||||
|  |     true | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def stale_contact_inboxes(time_period) | ||||||
|  |     ContactInbox.stale_without_conversations(time_period) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def log_stale_contact_inboxes_deletion(contact_inboxes, time_period) | ||||||
|  |     count = contact_inboxes.count | ||||||
|  |     Rails.logger.info "Deleting #{count} stale contact inboxes older than #{time_period}" | ||||||
|  |  | ||||||
|  |     # Log the SQL query without executing it | ||||||
|  |     sql_query = contact_inboxes.to_sql | ||||||
|  |     Rails.logger.info("SQL Query: #{sql_query}") | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -19,3 +19,10 @@ trigger_imap_email_inboxes_job: | |||||||
|   cron: '*/1 * * * *' |   cron: '*/1 * * * *' | ||||||
|   class: 'Inboxes::FetchImapEmailInboxesJob' |   class: 'Inboxes::FetchImapEmailInboxesJob' | ||||||
|   queue: scheduled_jobs |   queue: scheduled_jobs | ||||||
|  |  | ||||||
|  | # executed daily at 2230 UTC | ||||||
|  | # which is our lowest traffic time | ||||||
|  | remove_stale_contact_inboxes_job.rb: | ||||||
|  |   cron: '30 22 * * *' | ||||||
|  |   class: 'Internal::RemoveStaleContactInboxesJob' | ||||||
|  |   queue: scheduled_jobs | ||||||
|   | |||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | # spec/services/remove_stale_contact_inboxes_service_spec.rb | ||||||
|  |  | ||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe Internal::RemoveStaleContactInboxesService do | ||||||
|  |   describe '#perform' do | ||||||
|  |     it 'does not delete stale contact inboxes if REMOVE_STALE_CONTACT_INBOX_JOB_STATUS is false' do | ||||||
|  |       # default value of REMOVE_STALE_CONTACT_INBOX_JOB_STATUS is false | ||||||
|  |       create(:contact_inbox, created_at: 3.days.ago) | ||||||
|  |       create(:contact_inbox, created_at: 91.days.ago) | ||||||
|  |       create(:contact_inbox, created_at: 92.days.ago) | ||||||
|  |       create(:contact_inbox, created_at: 93.days.ago) | ||||||
|  |       create(:contact_inbox, created_at: 94.days.ago) | ||||||
|  |  | ||||||
|  |       service = described_class.new | ||||||
|  |       expect { service.perform }.not_to change(ContactInbox, :count) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     it 'deletes stale contact inboxes' do | ||||||
|  |       with_modified_env REMOVE_STALE_CONTACT_INBOX_JOB_STATUS: 'true' do | ||||||
|  |         create(:contact_inbox, created_at: 3.days.ago) | ||||||
|  |         create(:contact_inbox, created_at: 91.days.ago) | ||||||
|  |         create(:contact_inbox, created_at: 92.days.ago) | ||||||
|  |         create(:contact_inbox, created_at: 93.days.ago) | ||||||
|  |         create(:contact_inbox, created_at: 94.days.ago) | ||||||
|  |  | ||||||
|  |         service = described_class.new | ||||||
|  |         expect { service.perform }.to change(ContactInbox, :count).by(-4) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
		Reference in New Issue
	
	Block a user
	 Vishnu Narayanan
					Vishnu Narayanan