From fd7467e63b981dea5fac2badf03b63dcdd776aca Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 7 Dec 2025 14:02:48 +0100 Subject: [PATCH] Add schedule to run raw data archival job monthly --- app/jobs/family/invitations/cleanup_job.rb | 2 ++ app/jobs/points/raw_data/archive_job.rb | 2 ++ app/services/points/raw_data/archiver.rb | 4 ++-- config/schedule.yml | 5 +++++ spec/services/points/raw_data/archiver_spec.rb | 13 ++++++++----- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/jobs/family/invitations/cleanup_job.rb b/app/jobs/family/invitations/cleanup_job.rb index a80ad443..7100938a 100644 --- a/app/jobs/family/invitations/cleanup_job.rb +++ b/app/jobs/family/invitations/cleanup_job.rb @@ -4,6 +4,8 @@ class Family::Invitations::CleanupJob < ApplicationJob queue_as :families def perform + return unless DawarichSettings.family_feature_enabled? + Rails.logger.info 'Starting family invitations cleanup' expired_count = Family::Invitation.where(status: :pending) diff --git a/app/jobs/points/raw_data/archive_job.rb b/app/jobs/points/raw_data/archive_job.rb index 4ea6dfdc..de788361 100644 --- a/app/jobs/points/raw_data/archive_job.rb +++ b/app/jobs/points/raw_data/archive_job.rb @@ -6,6 +6,8 @@ module Points queue_as :archival def perform + return unless ENV['ARCHIVE_RAW_DATA'] == 'true' + stats = Points::RawData::Archiver.new.call Rails.logger.info("Archive job complete: #{stats}") diff --git a/app/services/points/raw_data/archiver.rb b/app/services/points/raw_data/archiver.rb index f12d3783..76a13feb 100644 --- a/app/services/points/raw_data/archiver.rb +++ b/app/services/points/raw_data/archiver.rb @@ -84,8 +84,8 @@ module Points Rails.logger.info("Skipping #{lock_key} - already locked") unless lock_acquired rescue StandardError => e - Rails.logger.error("Archive failed for #{user_id}/#{year}/#{month}: #{e.message}") - Sentry.capture_exception(e) if defined?(Sentry) + ExceptionReporter.call(e, "Failed to archive points for user #{user_id}, #{year}-#{month}") + @stats[:failed] += 1 end diff --git a/config/schedule.yml b/config/schedule.yml index ae920927..11f81d24 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -49,3 +49,8 @@ nightly_family_invitations_cleanup_job: cron: "30 2 * * *" # every day at 02:30 class: "Family::Invitations::CleanupJob" queue: family + +raw_data_archival_job: + cron: "0 2 1 * *" # Monthly on the 1st at 2 AM + class: "Points::RawData::ArchiveJob" + queue: archival diff --git a/spec/services/points/raw_data/archiver_spec.rb b/spec/services/points/raw_data/archiver_spec.rb index 97911b0c..c0bc71e6 100644 --- a/spec/services/points/raw_data/archiver_spec.rb +++ b/spec/services/points/raw_data/archiver_spec.rb @@ -7,7 +7,6 @@ RSpec.describe Points::RawData::Archiver do let(:archiver) { described_class.new } before do - # Stub broadcasting to avoid ActionCable issues in tests allow(PointsChannel).to receive(:broadcast_to) end @@ -20,6 +19,7 @@ RSpec.describe Points::RawData::Archiver do it 'returns early without processing' do result = archiver.call + expect(result).to eq({ processed: 0, archived: 0, failed: 0 }) end end @@ -44,18 +44,20 @@ RSpec.describe Points::RawData::Archiver do it 'marks points as archived' do archiver.call + expect(Point.where(raw_data_archived: true).count).to eq(5) end it 'nullifies raw_data column' do archiver.call - Point.where(user: user).each do |point| + Point.where(user: user).find_each do |point| expect(point.raw_data).to be_nil end end it 'returns correct stats' do result = archiver.call + expect(result[:processed]).to eq(1) expect(result[:archived]).to eq(5) expect(result[:failed]).to eq(0) @@ -115,7 +117,8 @@ RSpec.describe Points::RawData::Archiver do it 'creates archive with correct metadata' do archiver.archive_specific_month(user.id, test_date.year, test_date.month) - archive = Points::RawDataArchive.last + archive = user.raw_data_archives.last + expect(archive.user_id).to eq(user.id) expect(archive.year).to eq(test_date.year) expect(archive.month).to eq(test_date.month) @@ -126,7 +129,7 @@ RSpec.describe Points::RawData::Archiver do it 'attaches compressed file' do archiver.archive_specific_month(user.id, test_date.year, test_date.month) - archive = Points::RawDataArchive.last + archive = user.raw_data_archives.last expect(archive.file).to be_attached expect(archive.file.filename.to_s).to match(/raw_data_.*_chunk001\.jsonl\.gz/) end @@ -154,7 +157,7 @@ RSpec.describe Points::RawData::Archiver do # Verify first batch is archived june_points_batch1.each(&:reload) - expect(june_points_batch1.all? { |p| p.raw_data_archived }).to be true + expect(june_points_batch1.all?(&:raw_data_archived)).to be true # Add more points for same month (retrospective import) # Use unique timestamps to avoid uniqueness validation errors