fix: captain auto sync scheduler config (#14336)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
class Internal::TriggerHourlyScheduledItemsJob < ApplicationJob
|
||||
queue_as :scheduled_jobs
|
||||
|
||||
def perform; end
|
||||
end
|
||||
|
||||
Internal::TriggerHourlyScheduledItemsJob.prepend_mod_with('Internal::TriggerHourlyScheduledItemsJob')
|
||||
@@ -40,11 +40,6 @@ Rails.application.reloader.to_prepare do
|
||||
if File.exist?(schedule_file) && Sidekiq.server?
|
||||
schedule = YAML.load_file(schedule_file)
|
||||
|
||||
# Merge enterprise-only cron entries when running an enterprise build.
|
||||
# Mirrors the conditional-load pattern already used for enterprise initializers.
|
||||
enterprise_schedule_file = Rails.root.join('enterprise/config/schedule.yml')
|
||||
schedule.merge!(YAML.load_file(enterprise_schedule_file)) if ChatwootApp.enterprise? && enterprise_schedule_file.exist?
|
||||
|
||||
# Cron entries removed from schedule.yml but possibly still in Redis
|
||||
# with source:'dynamic' (predating the source tag). load_from_hash!
|
||||
# only cleans up source:'schedule' entries, so these need explicit removal.
|
||||
|
||||
@@ -209,6 +209,12 @@
|
||||
description: 'The limits for the Captain AI service for different plans'
|
||||
value:
|
||||
type: code
|
||||
- name: CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS
|
||||
display_title: 'Captain Document Auto Sync Intervals'
|
||||
description: 'JSON map of plan-wise Captain document auto-sync intervals in hours. Use null to disable auto-sync for a plan.'
|
||||
value:
|
||||
locked: false
|
||||
type: code
|
||||
# End of Captain Config
|
||||
|
||||
# ------- Context.dev Config ------- #
|
||||
|
||||
@@ -14,6 +14,12 @@ trigger_scheduled_items_job:
|
||||
class: 'TriggerScheduledItemsJob'
|
||||
queue: scheduled_jobs
|
||||
|
||||
# executed hourly for scheduled jobs that do not need minute-level cadence
|
||||
trigger_hourly_scheduled_items_job:
|
||||
cron: '0 * * * *'
|
||||
class: 'Internal::TriggerHourlyScheduledItemsJob'
|
||||
queue: scheduled_jobs
|
||||
|
||||
# executed At every minute..
|
||||
trigger_imap_email_inboxes_job:
|
||||
cron: '*/1 * * * *'
|
||||
|
||||
@@ -7,6 +7,7 @@ class Captain::Documents::ScheduleSyncsJob < ApplicationJob
|
||||
|
||||
def perform
|
||||
@remaining_global_capacity = GLOBAL_HOURLY_CAP
|
||||
sync_intervals = Enterprise::Account.captain_document_sync_intervals
|
||||
stats = { accounts_scanned: 0, accounts_enabled: 0, accounts_scheduled: 0, documents_enqueued: 0 }
|
||||
|
||||
Account.joins(:captain_documents).distinct.find_each(batch_size: 100) do |account|
|
||||
@@ -16,7 +17,7 @@ class Captain::Documents::ScheduleSyncsJob < ApplicationJob
|
||||
next unless account.feature_enabled?('captain_document_auto_sync')
|
||||
|
||||
stats[:accounts_enabled] += 1
|
||||
interval = account.captain_document_sync_interval
|
||||
interval = account.captain_document_sync_interval(sync_intervals)
|
||||
next unless interval
|
||||
|
||||
stats[:accounts_scheduled] += 1
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
module Enterprise::Internal::TriggerHourlyScheduledItemsJob
|
||||
def perform
|
||||
super
|
||||
|
||||
Captain::Documents::ScheduleSyncsJob.perform_later
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,22 @@
|
||||
module Enterprise::Account
|
||||
CAPTAIN_SYNC_INTERVALS = {
|
||||
'hacker' => nil,
|
||||
'startups' => 7.days,
|
||||
'business' => 1.day,
|
||||
'enterprise' => 6.hours
|
||||
}.freeze
|
||||
class << self
|
||||
def captain_document_sync_intervals
|
||||
parse_captain_document_sync_intervals(InstallationConfig.find_by(name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS')&.value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_captain_document_sync_intervals(configured_intervals)
|
||||
return {} if configured_intervals.blank?
|
||||
|
||||
parsed_intervals = configured_intervals.is_a?(String) ? JSON.parse(configured_intervals) : configured_intervals
|
||||
return {} unless parsed_intervals.is_a?(Hash)
|
||||
|
||||
parsed_intervals.transform_keys { |plan| plan.to_s.downcase }
|
||||
rescue JSON::ParserError
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Remove this when we upgrade administrate gem to the latest version
|
||||
# this is a temporary method since current administrate doesn't support virtual attributes
|
||||
@@ -41,12 +53,15 @@ module Enterprise::Account
|
||||
custom_attributes.delete('marked_for_deletion_at') && custom_attributes.delete('marked_for_deletion_reason') && save
|
||||
end
|
||||
|
||||
def captain_document_sync_interval
|
||||
def captain_document_sync_interval(sync_intervals = Enterprise::Account.captain_document_sync_intervals)
|
||||
plan = custom_attributes['plan_name']
|
||||
plan = 'enterprise' if plan.blank? && ChatwootApp.self_hosted_enterprise?
|
||||
return nil if plan.blank?
|
||||
|
||||
CAPTAIN_SYNC_INTERVALS[plan.downcase]
|
||||
interval_hours = sync_intervals[plan.downcase]
|
||||
return nil unless interval_hours.is_a?(Integer) && interval_hours.positive?
|
||||
|
||||
interval_hours.hours
|
||||
end
|
||||
|
||||
def saml_enabled?
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# Enterprise-only Sidekiq cron schedule.
|
||||
# Loaded by config/initializers/sidekiq.rb only when ChatwootApp.enterprise? is true.
|
||||
# Add cron entries here when the referenced job class lives under enterprise/.
|
||||
|
||||
# Captain document auto-sync scheduler
|
||||
# Runs hourly, finds due documents based on plan sync intervals
|
||||
captain_documents_schedule_syncs_job:
|
||||
cron: '0 * * * *'
|
||||
class: 'Captain::Documents::ScheduleSyncsJob'
|
||||
queue: scheduled_jobs
|
||||
@@ -5,6 +5,7 @@ RSpec.describe Captain::Documents::ScheduleSyncsJob, type: :job do
|
||||
let(:assistant) { create(:captain_assistant, account: account) }
|
||||
|
||||
before do
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: { business: 24, hacker: nil }.to_json)
|
||||
account.enable_features!('captain_document_auto_sync')
|
||||
clear_enqueued_jobs
|
||||
end
|
||||
|
||||
@@ -225,46 +225,68 @@ RSpec.describe Account, type: :model do
|
||||
describe 'captain document sync cadence' do
|
||||
let(:account) { create(:account) }
|
||||
|
||||
it 'has no cadence on the hacker plan' do
|
||||
account.update!(custom_attributes: { plan_name: 'hacker' })
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
end
|
||||
|
||||
it 'syncs weekly on the startups plan' do
|
||||
account.update!(custom_attributes: { plan_name: 'startups' })
|
||||
expect(account.captain_document_sync_interval).to eq(7.days)
|
||||
end
|
||||
|
||||
it 'syncs daily on the business plan' do
|
||||
it 'has no cadence when installation config is missing' do
|
||||
account.update!(custom_attributes: { plan_name: 'business' })
|
||||
expect(account.captain_document_sync_interval).to eq(1.day)
|
||||
end
|
||||
|
||||
it 'syncs every six hours on the enterprise plan' do
|
||||
account.update!(custom_attributes: { plan_name: 'enterprise' })
|
||||
expect(account.captain_document_sync_interval).to eq(6.hours)
|
||||
end
|
||||
|
||||
it 'has no cadence when plan is missing' do
|
||||
account.update!(custom_attributes: {})
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
end
|
||||
|
||||
it 'has no cadence for unknown plans' do
|
||||
account.update!(custom_attributes: { plan_name: 'mystery' })
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
it 'uses configured plan intervals from installation config' do
|
||||
intervals = {
|
||||
business: 48,
|
||||
enterprise: 24
|
||||
}
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: intervals.to_json)
|
||||
account.update!(custom_attributes: { plan_name: 'business' })
|
||||
|
||||
expect(account.captain_document_sync_interval).to eq(2.days)
|
||||
end
|
||||
|
||||
it 'normalizes plan name casing' do
|
||||
it 'normalizes configured plan name casing' do
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: { business: 24 }.to_json)
|
||||
account.update!(custom_attributes: { plan_name: 'Business' })
|
||||
|
||||
expect(account.captain_document_sync_interval).to eq(1.day)
|
||||
end
|
||||
|
||||
it 'syncs every six hours on self-hosted enterprise installs without a plan_name' do
|
||||
it 'uses the enterprise cadence for self-hosted enterprise installs without a plan_name' do
|
||||
allow(ChatwootApp).to receive(:self_hosted_enterprise?).and_return(true)
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: { enterprise: 6 }.to_json)
|
||||
account.update!(custom_attributes: {})
|
||||
|
||||
expect(account.captain_document_sync_interval).to eq(6.hours)
|
||||
end
|
||||
|
||||
it 'allows installation config to disable a plan cadence' do
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: { business: nil }.to_json)
|
||||
account.update!(custom_attributes: { plan_name: 'business' })
|
||||
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
end
|
||||
|
||||
it 'has no cadence when installation config is invalid' do
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: 'invalid-json')
|
||||
account.update!(custom_attributes: { plan_name: 'business' })
|
||||
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
end
|
||||
|
||||
it 'treats invalid plan interval values as disabled' do
|
||||
intervals = {
|
||||
business: false,
|
||||
enterprise: { hours: 6 },
|
||||
startups: '168'
|
||||
}
|
||||
create(:installation_config, name: 'CAPTAIN_DOCUMENT_AUTO_SYNC_INTERVALS', value: intervals.to_json)
|
||||
|
||||
account.update!(custom_attributes: { plan_name: 'business' })
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
|
||||
account.update!(custom_attributes: { plan_name: 'enterprise' })
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
|
||||
account.update!(custom_attributes: { plan_name: 'startups' })
|
||||
expect(account.captain_document_sync_interval).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'account deletion' do
|
||||
|
||||
Reference in New Issue
Block a user