From ddc96b5d8c78d9c55a7807a588702c8e14dbb4a1 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Sun, 6 Apr 2025 15:02:58 +0100 Subject: [PATCH 1/6] Rename VaccinationConfirmationsJob To `SendVaccinationConfirmationsJob` to match the naming convention with the other jobs that send out communications. --- ...nfirmations_job.rb => send_vaccination_confirmations_job.rb} | 2 +- config/environments/production.rb | 2 +- spec/features/doubles_vaccination_administered_spec.rb | 2 +- spec/features/hpv_vaccination_administered_spec.rb | 2 +- spec/features/hpv_vaccination_already_had_spec.rb | 2 +- spec/features/hpv_vaccination_clinic_spec.rb | 2 +- spec/features/hpv_vaccination_delayed_spec.rb | 2 +- spec/features/hpv_vaccination_offline_spec.rb | 2 +- spec/features/menacwy_vaccination_administered_spec.rb | 2 +- spec/features/td_ipv_vaccination_administered_spec.rb | 2 +- ...s_job_spec.rb => send_vaccination_confirmations_job_spec.rb} | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) rename app/jobs/{vaccination_confirmations_job.rb => send_vaccination_confirmations_job.rb} (93%) rename spec/jobs/{vaccination_confirmations_job_spec.rb => send_vaccination_confirmations_job_spec.rb} (97%) diff --git a/app/jobs/vaccination_confirmations_job.rb b/app/jobs/send_vaccination_confirmations_job.rb similarity index 93% rename from app/jobs/vaccination_confirmations_job.rb rename to app/jobs/send_vaccination_confirmations_job.rb index 30210da2e8..041731186a 100644 --- a/app/jobs/vaccination_confirmations_job.rb +++ b/app/jobs/send_vaccination_confirmations_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class VaccinationConfirmationsJob < ApplicationJob +class SendVaccinationConfirmationsJob < ApplicationJob include VaccinationMailerConcern queue_as :notifications diff --git a/config/environments/production.rb b/config/environments/production.rb index 67f446099b..bf2303e0b4 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -147,7 +147,7 @@ }, vaccination_confirmations: { cron: "every day at 7pm", - class: "VaccinationConfirmationsJob", + class: "SendVaccinationConfirmationsJob", description: "Send vaccination confirmation emails to parents" } } diff --git a/spec/features/doubles_vaccination_administered_spec.rb b/spec/features/doubles_vaccination_administered_spec.rb index 6415f08dd8..cc25689594 100644 --- a/spec/features/doubles_vaccination_administered_spec.rb +++ b/spec/features/doubles_vaccination_administered_spec.rb @@ -133,7 +133,7 @@ def then_i_see_the_patient_is_vaccinated_for_td_ipv end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_vaccinations diff --git a/spec/features/hpv_vaccination_administered_spec.rb b/spec/features/hpv_vaccination_administered_spec.rb index 3485e7ecdf..e1c2b7b72d 100644 --- a/spec/features/hpv_vaccination_administered_spec.rb +++ b/spec/features/hpv_vaccination_administered_spec.rb @@ -224,7 +224,7 @@ def then_i_see_the_patient end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_vaccination diff --git a/spec/features/hpv_vaccination_already_had_spec.rb b/spec/features/hpv_vaccination_already_had_spec.rb index 8437bca694..dbf3799987 100644 --- a/spec/features/hpv_vaccination_already_had_spec.rb +++ b/spec/features/hpv_vaccination_already_had_spec.rb @@ -100,7 +100,7 @@ def then_i_see_that_the_status_is_vaccinated end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_saying_the_vaccination_didnt_happen diff --git a/spec/features/hpv_vaccination_clinic_spec.rb b/spec/features/hpv_vaccination_clinic_spec.rb index 2c48d2cf36..bdca004ec3 100644 --- a/spec/features/hpv_vaccination_clinic_spec.rb +++ b/spec/features/hpv_vaccination_clinic_spec.rb @@ -122,7 +122,7 @@ def then_i_see_that_the_status_is_vaccinated end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_vaccination diff --git a/spec/features/hpv_vaccination_delayed_spec.rb b/spec/features/hpv_vaccination_delayed_spec.rb index 9fa944d4d2..8b579ddd99 100644 --- a/spec/features/hpv_vaccination_delayed_spec.rb +++ b/spec/features/hpv_vaccination_delayed_spec.rb @@ -112,7 +112,7 @@ def then_i_see_the_patient_has_no_outcome_yet end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_delay diff --git a/spec/features/hpv_vaccination_offline_spec.rb b/spec/features/hpv_vaccination_offline_spec.rb index 1acb8f2f22..aa8834485f 100644 --- a/spec/features/hpv_vaccination_offline_spec.rb +++ b/spec/features/hpv_vaccination_offline_spec.rb @@ -282,7 +282,7 @@ def and_the_clinic_location_is_displayed end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_vaccination diff --git a/spec/features/menacwy_vaccination_administered_spec.rb b/spec/features/menacwy_vaccination_administered_spec.rb index f436457daf..c475475b34 100644 --- a/spec/features/menacwy_vaccination_administered_spec.rb +++ b/spec/features/menacwy_vaccination_administered_spec.rb @@ -200,7 +200,7 @@ def and_i_see_the_vaccination_details end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_vaccination diff --git a/spec/features/td_ipv_vaccination_administered_spec.rb b/spec/features/td_ipv_vaccination_administered_spec.rb index d10d1e5d04..7aa6cbbe8e 100644 --- a/spec/features/td_ipv_vaccination_administered_spec.rb +++ b/spec/features/td_ipv_vaccination_administered_spec.rb @@ -201,7 +201,7 @@ def and_i_see_the_vaccination_details end def when_vaccination_confirmations_are_sent - VaccinationConfirmationsJob.perform_now + SendVaccinationConfirmationsJob.perform_now end def then_an_email_is_sent_to_the_parent_confirming_the_vaccination diff --git a/spec/jobs/vaccination_confirmations_job_spec.rb b/spec/jobs/send_vaccination_confirmations_job_spec.rb similarity index 97% rename from spec/jobs/vaccination_confirmations_job_spec.rb rename to spec/jobs/send_vaccination_confirmations_job_spec.rb index 237a04ad46..dc339f2747 100644 --- a/spec/jobs/vaccination_confirmations_job_spec.rb +++ b/spec/jobs/send_vaccination_confirmations_job_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe VaccinationConfirmationsJob do +describe SendVaccinationConfirmationsJob do let(:job) { described_class.new } describe "#perform" do From ce27f76cc34042519e908eacdaaae7d552b515de Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Sun, 6 Apr 2025 15:04:32 +0100 Subject: [PATCH 2/6] Rename BulkUpdatePatientsFromPDSJob To `EnqueueUpdatePatientsFromPDSJob` as this is the naming convention we'll use for jobs which do nothing but queue up other jobs. --- ..._job.rb => enqueue_update_patients_from_pds_job.rb} | 2 +- config/environments/production.rb | 10 +++++----- ...rb => enqueue_update_patients_from_pds_job_spec.rb} | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename app/jobs/{bulk_update_patients_from_pds_job.rb => enqueue_update_patients_from_pds_job.rb} (95%) rename spec/jobs/{bulk_update_patients_from_pds_job_spec.rb => enqueue_update_patients_from_pds_job_spec.rb} (97%) diff --git a/app/jobs/bulk_update_patients_from_pds_job.rb b/app/jobs/enqueue_update_patients_from_pds_job.rb similarity index 95% rename from app/jobs/bulk_update_patients_from_pds_job.rb rename to app/jobs/enqueue_update_patients_from_pds_job.rb index 6979276ef8..96812afd81 100644 --- a/app/jobs/bulk_update_patients_from_pds_job.rb +++ b/app/jobs/enqueue_update_patients_from_pds_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class BulkUpdatePatientsFromPDSJob < ApplicationJob +class EnqueueUpdatePatientsFromPDSJob < ApplicationJob include GoodJob::ActiveJobExtensions::Concurrency queue_as :pds diff --git a/config/environments/production.rb b/config/environments/production.rb index bf2303e0b4..80785a37bf 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -97,11 +97,6 @@ config.good_job.enable_cron = true config.good_job.cron = { - bulk_update_patients_from_pds: { - cron: "every day at 6:00 and 18:00", - class: "BulkUpdatePatientsFromPDSJob", - description: "Keep patient details up to date with PDS." - }, clinic_invitation: { cron: "every day at 9am", class: "ClinicSessionInvitationsJob", @@ -145,6 +140,11 @@ class: "TrimActiveRecordSessionsJob", description: "Remove ActiveRecord sessions older than 30 days" }, + update_patients_from_pds: { + cron: "every day at 6:00 and 18:00", + class: "EnqueueUpdatePatientsFromPDSJob", + description: "Keep patient details up to date with PDS." + }, vaccination_confirmations: { cron: "every day at 7pm", class: "SendVaccinationConfirmationsJob", diff --git a/spec/jobs/bulk_update_patients_from_pds_job_spec.rb b/spec/jobs/enqueue_update_patients_from_pds_job_spec.rb similarity index 97% rename from spec/jobs/bulk_update_patients_from_pds_job_spec.rb rename to spec/jobs/enqueue_update_patients_from_pds_job_spec.rb index 10eacbab66..ce52491324 100644 --- a/spec/jobs/bulk_update_patients_from_pds_job_spec.rb +++ b/spec/jobs/enqueue_update_patients_from_pds_job_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe BulkUpdatePatientsFromPDSJob do +describe EnqueueUpdatePatientsFromPDSJob do subject(:perform_now) { described_class.perform_now } let!(:invalidated_patient) { create(:patient, :invalidated) } From 0f0bf92301ffc8d726da33a493e578a29f7d8d48 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Sun, 6 Apr 2025 15:06:48 +0100 Subject: [PATCH 3/6] Rename ClinicSessionInvitationsJob To `EnqueueClinicSessionInvitationsJob` as this better reflects that the job is only responsible for enqueing other jobs. --- ...tions_job.rb => enqueue_clinic_session_invitations_job.rb} | 4 ++-- config/environments/production.rb | 4 ++-- ...spec.rb => enqueue_clinic_session_invitations_job_spec.rb} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/jobs/{clinic_session_invitations_job.rb => enqueue_clinic_session_invitations_job.rb} (86%) rename spec/jobs/{clinic_session_invitations_job_spec.rb => enqueue_clinic_session_invitations_job_spec.rb} (99%) diff --git a/app/jobs/clinic_session_invitations_job.rb b/app/jobs/enqueue_clinic_session_invitations_job.rb similarity index 86% rename from app/jobs/clinic_session_invitations_job.rb rename to app/jobs/enqueue_clinic_session_invitations_job.rb index 237a771b2e..1bcaf303e7 100644 --- a/app/jobs/clinic_session_invitations_job.rb +++ b/app/jobs/enqueue_clinic_session_invitations_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ClinicSessionInvitationsJob < ApplicationJob +class EnqueueClinicSessionInvitationsJob < ApplicationJob queue_as :notifications def perform @@ -9,7 +9,7 @@ def perform .includes(:programmes) .joins(:location) .merge(Location.clinic) - .each do |session| + .find_each do |session| # We're only inviting patients who don't have a school. # Patients who have a school are sent invitations manually by the # nurse when they're finished at a school. diff --git a/config/environments/production.rb b/config/environments/production.rb index 80785a37bf..1f86ee6e11 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -97,9 +97,9 @@ config.good_job.enable_cron = true config.good_job.cron = { - clinic_invitation: { + clinic_session_invitations: { cron: "every day at 9am", - class: "ClinicSessionInvitationsJob", + class: "EnqueueClinicSessionInvitationsJob", description: "Send school clinic invitation emails to parents" }, consent_request: { diff --git a/spec/jobs/clinic_session_invitations_job_spec.rb b/spec/jobs/enqueue_clinic_session_invitations_job_spec.rb similarity index 99% rename from spec/jobs/clinic_session_invitations_job_spec.rb rename to spec/jobs/enqueue_clinic_session_invitations_job_spec.rb index 1bda396823..1f53fbfba1 100644 --- a/spec/jobs/clinic_session_invitations_job_spec.rb +++ b/spec/jobs/enqueue_clinic_session_invitations_job_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe ClinicSessionInvitationsJob do +describe EnqueueClinicSessionInvitationsJob do subject(:perform_now) { described_class.perform_now } let(:programmes) { [create(:programme, :hpv)] } From 9ad050e0baade06776eb46a577e1f59e8477b568 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Sun, 6 Apr 2025 15:07:24 +0100 Subject: [PATCH 4/6] Rename SchoolSessionRemindersJob To `SendSchoolSessionRemindersJob` to reflect that this job actually sends the communications rather than just enqueing other jobs. --- ..._reminders_job.rb => send_school_session_reminders_job.rb} | 4 ++-- config/environments/production.rb | 4 ++-- ..._job_spec.rb => send_school_session_reminders_job_spec.rb} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/jobs/{school_session_reminders_job.rb => send_school_session_reminders_job.rb} (91%) rename spec/jobs/{school_session_reminders_job_spec.rb => send_school_session_reminders_job_spec.rb} (98%) diff --git a/app/jobs/school_session_reminders_job.rb b/app/jobs/send_school_session_reminders_job.rb similarity index 91% rename from app/jobs/school_session_reminders_job.rb rename to app/jobs/send_school_session_reminders_job.rb index 744ec30fdf..8461dd6d97 100644 --- a/app/jobs/school_session_reminders_job.rb +++ b/app/jobs/send_school_session_reminders_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class SchoolSessionRemindersJob < ApplicationJob +class SendSchoolSessionRemindersJob < ApplicationJob queue_as :notifications def perform @@ -16,7 +16,7 @@ def perform .merge(Session.has_date(date)) .notification_not_sent(date) - patient_sessions.each do |patient_session| + patient_sessions.find_each do |patient_session| next unless should_send_notification?(patient_session:) SessionNotification.create_and_send!( diff --git a/config/environments/production.rb b/config/environments/production.rb index 1f86ee6e11..072a23949f 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -120,9 +120,9 @@ description: "Invalidate all self-consents and associated triage for the previous day" }, - session_reminder: { + school_session_reminders: { cron: "every day at 9am", - class: "SchoolSessionRemindersJob", + class: "SendSchoolSessionRemindersJob", description: "Send school session reminder emails to parents" }, remove_import_csv: { diff --git a/spec/jobs/school_session_reminders_job_spec.rb b/spec/jobs/send_school_session_reminders_job_spec.rb similarity index 98% rename from spec/jobs/school_session_reminders_job_spec.rb rename to spec/jobs/send_school_session_reminders_job_spec.rb index dca63fceff..9dd76f29c9 100644 --- a/spec/jobs/school_session_reminders_job_spec.rb +++ b/spec/jobs/send_school_session_reminders_job_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe SchoolSessionRemindersJob do +describe SendSchoolSessionRemindersJob do subject(:perform_now) { described_class.perform_now } let(:programmes) { [create(:programme)] } From eeaeae5352348652f516d02bb5ccae8cb92af877 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Sun, 6 Apr 2025 17:59:30 +0100 Subject: [PATCH 5/6] Refactor SchoolConsentRequestsJob This splits the job in to two separate jobs, one which runs on a schedule and for each session queues up a separate job which handles the process of sending the communications for a particular session. We're finding that the current job is unable to handle a high number of sessions and runs out of resources. Instead, by queuing a job for each session we can avoid the individual jobs being too resource hungry and gives us more flexibility in terms of running the jobs manually. --- .../enqueue_school_consent_requests_job.rb | 14 +++++ ...rb => send_school_consent_requests_job.rb} | 21 +++---- config/environments/production.rb | 8 +-- .../scheduled_consent_requests_spec.rb | 6 +- spec/features/td_ipv_already_had_spec.rb | 3 +- ...nqueue_school_consent_requests_job_spec.rb | 56 +++++++++++++++++++ ... send_school_consent_requests_job_spec.rb} | 43 ++------------ 7 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 app/jobs/enqueue_school_consent_requests_job.rb rename app/jobs/{school_consent_requests_job.rb => send_school_consent_requests_job.rb} (71%) create mode 100644 spec/jobs/enqueue_school_consent_requests_job_spec.rb rename spec/jobs/{school_consent_requests_job_spec.rb => send_school_consent_requests_job_spec.rb} (78%) diff --git a/app/jobs/enqueue_school_consent_requests_job.rb b/app/jobs/enqueue_school_consent_requests_job.rb new file mode 100644 index 0000000000..b0190639c0 --- /dev/null +++ b/app/jobs/enqueue_school_consent_requests_job.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class EnqueueSchoolConsentRequestsJob < ApplicationJob + queue_as :notifications + + def perform + sessions = + Session.send_consent_requests.joins(:location).merge(Location.school) + + sessions.find_each do |session| + SendSchoolConsentRequestsJob.perform_later(session) + end + end +end diff --git a/app/jobs/school_consent_requests_job.rb b/app/jobs/send_school_consent_requests_job.rb similarity index 71% rename from app/jobs/school_consent_requests_job.rb rename to app/jobs/send_school_consent_requests_job.rb index b5be0ebcf1..1fb905f5c3 100644 --- a/app/jobs/school_consent_requests_job.rb +++ b/app/jobs/send_school_consent_requests_job.rb @@ -1,20 +1,16 @@ # frozen_string_literal: true -class SchoolConsentRequestsJob < ApplicationJob +class SendSchoolConsentRequestsJob < ApplicationJob queue_as :notifications - def perform - sessions = - Session - .send_consent_requests - .joins(:location) - .includes(:session_dates, :programmes, :patient_sessions, :location) - .merge(Location.school) + def perform(session) + return unless session.school? && session.open_for_consent? - sessions.find_each(batch_size: 1) do |session| - next unless session.open_for_consent? - - session.patient_sessions.each do |patient_session| + session + .patient_sessions + .includes_programmes + .includes(patient: %i[consent_notifications consents vaccination_records]) + .find_each do |patient_session| ProgrammeGrouper .call(patient_session.programmes) .each_value do |programmes| @@ -30,7 +26,6 @@ def perform ) end end - end end def should_send_notification?(patient:, programmes:) diff --git a/config/environments/production.rb b/config/environments/production.rb index 072a23949f..a04003ed16 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -102,15 +102,15 @@ class: "EnqueueClinicSessionInvitationsJob", description: "Send school clinic invitation emails to parents" }, - consent_request: { + school_consent_requests: { cron: "every day at 4pm", - class: "SchoolConsentRequestsJob", + class: "EnqueueSchoolConsentRequestsJob", description: "Send school consent request emails to parents for each session" }, consent_reminder: { - cron: "every day at 4pm", - class: "SchoolConsentRemindersJob", + cron: "every day at 9am", + class: "SendSchoolConsentReminderJob", description: "Send school consent reminder emails to parents for each session" }, diff --git a/spec/features/scheduled_consent_requests_spec.rb b/spec/features/scheduled_consent_requests_spec.rb index 0088f5f862..42f617ed7b 100644 --- a/spec/features/scheduled_consent_requests_spec.rb +++ b/spec/features/scheduled_consent_requests_spec.rb @@ -102,14 +102,16 @@ def when_1_more_day_passes end def then_no_consent_requests_have_been_sent - SchoolConsentRequestsJob.perform_now + EnqueueSchoolConsentRequestsJob.perform_now + perform_enqueued_jobs expect(email_deliveries).to be_empty expect(sms_deliveries).to be_empty end def then_all_four_parents_received_consent_requests - SchoolConsentRequestsJob.perform_now + EnqueueSchoolConsentRequestsJob.perform_now + perform_enqueued_jobs expect_email_to( "parent1.child1@example.com", diff --git a/spec/features/td_ipv_already_had_spec.rb b/spec/features/td_ipv_already_had_spec.rb index fafd402c48..956bce2179 100644 --- a/spec/features/td_ipv_already_had_spec.rb +++ b/spec/features/td_ipv_already_had_spec.rb @@ -186,7 +186,8 @@ def and_i_cannot_record_the_patient_as_already_vaccinated end def and_the_consent_requests_are_sent - SchoolConsentRequestsJob.perform_now + EnqueueSchoolConsentRequestsJob.perform_now + perform_enqueued_jobs end def then_the_parent_doesnt_receive_a_consent_request diff --git a/spec/jobs/enqueue_school_consent_requests_job_spec.rb b/spec/jobs/enqueue_school_consent_requests_job_spec.rb new file mode 100644 index 0000000000..8259c3ecf9 --- /dev/null +++ b/spec/jobs/enqueue_school_consent_requests_job_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +describe EnqueueSchoolConsentRequestsJob do + subject(:perform_now) { described_class.perform_now } + + context "when session is unscheduled" do + let(:session) { create(:session, :unscheduled) } + + it "doesn't queue any jobs" do + expect { perform_now }.not_to have_enqueued_job( + SendSchoolConsentRequestsJob + ) + end + end + + context "when requests should be sent in the future" do + let(:session) do + create(:session, send_consent_requests_at: 2.days.from_now) + end + + it "doesn't queue any jobs" do + expect { perform_now }.not_to have_enqueued_job( + SendSchoolConsentRequestsJob + ) + end + end + + context "when requests should be sent today" do + let(:session) do + create( + :session, + date: 3.weeks.from_now.to_date, + send_consent_requests_at: Date.current + ) + end + + it "queues a job for the session" do + expect { perform_now }.to have_enqueued_job( + SendSchoolConsentRequestsJob + ).with(session) + end + + context "when location is a generic clinic" do + let(:location) { create(:generic_clinic) } + let(:session) do + create(:session, location:, send_consent_requests_at: Date.current) + end + + it "doesn't queue any jobs" do + expect { perform_now }.not_to have_enqueued_job( + SendSchoolConsentRequestsJob + ) + end + end + end +end diff --git a/spec/jobs/school_consent_requests_job_spec.rb b/spec/jobs/send_school_consent_requests_job_spec.rb similarity index 78% rename from spec/jobs/school_consent_requests_job_spec.rb rename to spec/jobs/send_school_consent_requests_job_spec.rb index 13930fb3a2..54a84b279e 100644 --- a/spec/jobs/school_consent_requests_job_spec.rb +++ b/spec/jobs/send_school_consent_requests_job_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -describe SchoolConsentRequestsJob do - subject(:perform_now) { described_class.perform_now } +describe SendSchoolConsentRequestsJob do + subject(:perform_now) { described_class.perform_now(session) } let(:programmes) { [create(:programme)] } @@ -38,23 +38,7 @@ end end - context "when requests should be sent in the future" do - let(:session) do - create( - :session, - patients:, - programmes:, - send_consent_requests_at: 2.days.from_now - ) - end - - it "doesn't send any notifications" do - expect(ConsentNotification).not_to receive(:create_and_send!) - perform_now - end - end - - context "when requests should be sent today" do + context "when session is scheduled" do let(:session) do create( :session, @@ -75,17 +59,6 @@ perform_now end - context "when triaged as do not vaccinate" do - let(:patient_not_sent_request) do - create(:patient, :triage_do_not_vaccinate, parents:, programmes:) - end - - it "doesn't send any notifications" do - expect(ConsentNotification).not_to receive(:create_and_send!) - perform_now - end - end - context "with Td/IPV and MenACWY" do let(:programmes) do [create(:programme, :menacwy), create(:programme, :td_ipv)] @@ -151,15 +124,7 @@ context "when location is a generic clinic" do let(:organisation) { create(:organisation, programmes:) } let(:location) { create(:generic_clinic, organisation:) } - let(:session) do - create( - :session, - patients:, - programmes:, - send_consent_requests_at: Date.current, - organisation: - ) - end + let(:session) { create(:session, patients:, programmes:, organisation:) } it "doesn't send any notifications" do expect(ConsentNotification).not_to receive(:create_and_send!) From 330bd08014ab9a34f7e906a15fa0cf655e3a24d1 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Sun, 6 Apr 2025 18:01:18 +0100 Subject: [PATCH 6/6] Refactor SchoolConsentRemindersJob This splits the job in to two separate jobs, one which runs on a schedule and for each session queues up a separate job which handles the process of sending the communications for a particular session. We're finding that the current job is unable to handle a high number of sessions and runs out of resources. Instead, by queuing a job for each session we can avoid the individual jobs being too resource hungry and gives us more flexibility in terms of running the jobs manually. --- .../enqueue_school_consent_reminders_job.rb | 14 ++++ ...b => send_school_consent_reminders_job.rb} | 21 ++---- ...queue_school_consent_reminders_job_spec.rb | 75 +++++++++++++++++++ ...send_school_consent_reminders_job_spec.rb} | 4 +- 4 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 app/jobs/enqueue_school_consent_reminders_job.rb rename app/jobs/{school_consent_reminders_job.rb => send_school_consent_reminders_job.rb} (84%) create mode 100644 spec/jobs/enqueue_school_consent_reminders_job_spec.rb rename spec/jobs/{school_consent_reminders_job_spec.rb => send_school_consent_reminders_job_spec.rb} (97%) diff --git a/app/jobs/enqueue_school_consent_reminders_job.rb b/app/jobs/enqueue_school_consent_reminders_job.rb new file mode 100644 index 0000000000..b64a3c599a --- /dev/null +++ b/app/jobs/enqueue_school_consent_reminders_job.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class EnqueueSchoolConsentRemindersJob < ApplicationJob + queue_as :notifications + + def perform + sessions = + Session.send_consent_reminders.joins(:location).merge(Location.school) + + sessions.find_each do |session| + SendSchoolConsentRemindersJob.perform_later(session) + end + end +end diff --git a/app/jobs/school_consent_reminders_job.rb b/app/jobs/send_school_consent_reminders_job.rb similarity index 84% rename from app/jobs/school_consent_reminders_job.rb rename to app/jobs/send_school_consent_reminders_job.rb index 13cd637170..c5b31d8e91 100644 --- a/app/jobs/school_consent_reminders_job.rb +++ b/app/jobs/send_school_consent_reminders_job.rb @@ -1,20 +1,16 @@ # frozen_string_literal: true -class SchoolConsentRemindersJob < ApplicationJob +class SendSchoolConsentRemindersJob < ApplicationJob queue_as :notifications - def perform - sessions = - Session - .send_consent_reminders - .joins(:location) - .includes(:session_dates, :programmes, :patient_sessions, :location) - .merge(Location.school) + def perform(session) + return unless session.school? && session.open_for_consent? - sessions.find_each(batch_size: 1) do |session| - next unless session.open_for_consent? - - session.patient_sessions.each do |patient_session| + session + .patient_sessions + .includes_programmes + .includes(patient: %i[consent_notifications consents vaccination_records]) + .find_each do |patient_session| ProgrammeGrouper .call(patient_session.programmes) .each_value do |programmes| @@ -39,7 +35,6 @@ def perform ) end end - end end def should_send_notification?(patient_session:, programmes:) diff --git a/spec/jobs/enqueue_school_consent_reminders_job_spec.rb b/spec/jobs/enqueue_school_consent_reminders_job_spec.rb new file mode 100644 index 0000000000..1e7c53bf68 --- /dev/null +++ b/spec/jobs/enqueue_school_consent_reminders_job_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +describe EnqueueSchoolConsentRemindersJob do + subject(:perform_now) { described_class.perform_now } + + let(:programmes) { [create(:programme)] } + let(:organisation) { create(:organisation, programmes:) } + let(:location) { create(:school, organisation:) } + + let(:dates) { [Date.new(2024, 1, 12), Date.new(2024, 1, 15)] } + + let!(:session) do + create( + :session, + dates:, + send_consent_requests_at: dates.first - 3.weeks, + days_before_consent_reminders: 7, + location:, + programmes:, + organisation: + ) + end + + around { |example| travel_to(today) { example.run } } + + context "two weeks before the first session" do + let(:today) { dates.first - 2.weeks } + + it "doesn't queue any jobs" do + expect { perform_now }.not_to have_enqueued_job( + SendSchoolConsentRemindersJob + ) + end + end + + context "one week before the first session" do + let(:today) { dates.first - 1.week } + + it "queues a job for the session" do + expect { perform_now }.to have_enqueued_job( + SendSchoolConsentRemindersJob + ).with(session) + end + + context "when location is a generic clinic" do + let(:location) { create(:generic_clinic, organisation:) } + + it "doesn't queue any jobs" do + expect { perform_now }.not_to have_enqueued_job( + SendSchoolConsentRemindersJob + ) + end + end + end + + context "one week before the second session" do + let(:today) { dates.last - 1.week } + + it "queues a job for the session" do + expect { perform_now }.to have_enqueued_job( + SendSchoolConsentRemindersJob + ).with(session) + end + + context "when location is a generic clinic" do + let(:location) { create(:generic_clinic, organisation:) } + + it "doesn't queue any jobs" do + expect { perform_now }.not_to have_enqueued_job( + SendSchoolConsentRemindersJob + ) + end + end + end +end diff --git a/spec/jobs/school_consent_reminders_job_spec.rb b/spec/jobs/send_school_consent_reminders_job_spec.rb similarity index 97% rename from spec/jobs/school_consent_reminders_job_spec.rb rename to spec/jobs/send_school_consent_reminders_job_spec.rb index c74a1125fa..7e5eee2404 100644 --- a/spec/jobs/school_consent_reminders_job_spec.rb +++ b/spec/jobs/send_school_consent_reminders_job_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -describe SchoolConsentRemindersJob do - subject(:perform_now) { described_class.perform_now } +describe SendSchoolConsentRemindersJob do + subject(:perform_now) { described_class.perform_now(session) } let(:programmes) { [create(:programme)] }