feat(captain-memory): add AgingJob with TTL + LRU cap, weekly cron

This commit is contained in:
Rodribm10 2026-04-19 01:05:02 -03:00
parent fb6673664a
commit b3077b2b26
3 changed files with 79 additions and 0 deletions

View File

@ -107,3 +107,9 @@ captain_contact_memory_silence_detector_job:
cron: '*/10 * * * *'
class: 'Captain::ContactMemories::SilenceDetectorJob'
queue: scheduled_jobs
# weekly - Sundays at 03:00 UTC - TTL aging + LRU cap of contact memories
captain_contact_memory_aging_job:
cron: '0 3 * * 0'
class: 'Captain::ContactMemories::AgingJob'
queue: scheduled_jobs

View File

@ -0,0 +1,47 @@
class Captain::ContactMemories::AgingJob < ApplicationJob
queue_as :scheduled_jobs
DELETE_ON_EXPIRE = %w[reclamacao feedback_positivo vinculo_social].freeze
MAX_ACTIVE_PER_CONTACT = 50
def perform
soft_delete_expired_deletable
prune_per_contact_lru
end
private
def soft_delete_expired_deletable
Captain::ContactMemory
.where(memory_type: DELETE_ON_EXPIRE)
.where('expires_at < ?', Time.current)
.where(deleted_at: nil)
.update_all(deleted_at: Time.current, updated_at: Time.current) # rubocop:disable Rails/SkipsModelValidations
end
def prune_per_contact_lru
over_limit_contact_ids.each do |contact_id|
excess = Captain::ContactMemory.active.for_contact(contact_id).count - MAX_ACTIVE_PER_CONTACT
next if excess <= 0
ids_to_delete = Captain::ContactMemory
.active
.for_contact(contact_id)
.order(last_verified_at: :asc)
.limit(excess)
.pluck(:id)
Captain::ContactMemory
.where(id: ids_to_delete)
.update_all(deleted_at: Time.current, updated_at: Time.current) # rubocop:disable Rails/SkipsModelValidations
end
end
def over_limit_contact_ids
Captain::ContactMemory
.active
.group(:contact_id)
.having('COUNT(*) > ?', MAX_ACTIVE_PER_CONTACT)
.pluck(:contact_id)
end
end

View File

@ -0,0 +1,26 @@
require 'rails_helper'
RSpec.describe Captain::ContactMemories::AgingJob do
let(:account) { create(:account) }
let(:contact) { create(:contact, account: account) }
it 'soft-deletes expired reclamacao (delete on expire type)' do
mem = create(:captain_contact_memory, account: account, contact: contact, memory_type: 'reclamacao', expires_at: 1.day.ago)
described_class.perform_now
expect(mem.reload.deleted_at).not_to be_nil
end
it 'does NOT soft-delete expired preferencia (reduce-weight type)' do
mem = create(:captain_contact_memory, account: account, contact: contact, memory_type: 'preferencia', expires_at: 1.day.ago)
described_class.perform_now
expect(mem.reload.deleted_at).to be_nil
end
it 'applies LRU soft-delete when contact exceeds 50 active facts' do
55.times do |i|
create(:captain_contact_memory, account: account, contact: contact, last_verified_at: i.days.ago)
end
described_class.perform_now
expect(Captain::ContactMemory.where(contact: contact).active.count).to eq(50)
end
end