Class: MongoDbSyncRestaurantDataWorker

Inherits:
ApplicationWorker show all
Defined in:
app/workers/mongo_db_sync_restaurant_data_worker.rb

Overview

typed: true frozen_string_literal: true

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationWorker

unlimited_retry

Class Method Details

.circuit_open?Boolean

Checks if the circuit breaker is open (too many errors have occurred) Returns true if more than 5 MongoDB errors have occurred in the last 5 minutes When true, MongoDB operations should be temporarily paused

Returns:

  • (Boolean)


28
29
30
31
32
33
34
35
36
37
# File 'app/workers/mongo_db_sync_restaurant_data_worker.rb', line 28

def self.circuit_open?
  $redis_lru.with do |redis|
    error_count = redis.get('mongodb_error_count').to_i
    error_count > 5 # Circuit opens if more than 5 errors in the time window
  end
rescue StandardError => e
  # If Redis fails, assume circuit is closed to allow operation
  APMErrorHandler.report('Failed to check MongoDB circuit breaker', error: e)
  false
end

.record_errorObject

Records a MongoDB error for the circuit breaker Increments the error counter in Redis and sets a 5-minute expiration This ensures the circuit will automatically reset after 5 minutes



42
43
44
45
46
47
48
49
50
# File 'app/workers/mongo_db_sync_restaurant_data_worker.rb', line 42

def self.record_error
  $redis_lru.with do |redis|
    redis.incr('mongodb_error_count')
    redis.expire('mongodb_error_count', 300) # 5-minute window
  end
rescue StandardError => e
  # Silently continue if Redis fails
  APMErrorHandler.report('Failed to record MongoDB error for circuit breaker', error: e)
end

.reset_circuitObject

Manually resets the circuit breaker by clearing the error count Called after successful operations to quickly restore normal operation



54
55
56
57
58
59
# File 'app/workers/mongo_db_sync_restaurant_data_worker.rb', line 54

def self.reset_circuit
  $redis_lru.with { |redis| redis.del('mongodb_error_count') }
rescue StandardError => e
  # Silently continue if Redis fails
  APMErrorHandler.report('Failed to reset MongoDB circuit breaker', error: e)
end

Instance Method Details

#perform(date_time = nil, restaurant_id = nil, first_sync = false, type = nil) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/workers/mongo_db_sync_restaurant_data_worker.rb', line 61

def perform(date_time = nil, restaurant_id = nil, first_sync = false, type = nil)
  # Check circuit breaker first
  if self.class.circuit_open?
    APMErrorHandler.report('MongoDB circuit breaker open, rescheduling job',
                           restaurant_id: restaurant_id,
                           date_time: date_time)
    # If circuit is open, reschedule the job for later
    return self.class.perform_in(5.minutes, date_time, restaurant_id, first_sync, type)
  end

  type = type || 'by_hour'

  restaurant = Restaurant.find_by(id: restaurant_id, active: true)
  return if restaurant.blank?

  if first_sync
    first_sync_by_month(restaurant)
    first_sync_by_date(restaurant)
    first_sync_by_hour(restaurant)
  else
    date_time = date_time.blank? ? Time.current : Time.zone.parse(date_time.to_s)
    send("sync_#{type}", date_time.in_time_zone(restaurant.time_zone), restaurant)
  end

  # If we get here, the job completed successfully, so reset the circuit
  self.class.reset_circuit
rescue Mongo::Error::NoServerAvailable, Mongo::Error::ConnectionCheckOutTimeout, Mongo::Error::OperationFailure => e
  # Record error for circuit breaker
  self.class.record_error

  # Log the specific error with context
  APMErrorHandler.report("MongoDB error in #{self.class}",
                         error: e,
                         restaurant_id: restaurant_id,
                         date_time: date_time,
                         first_sync: first_sync,
                         type: type)
  # Retry with exponential backoff
  retry_in = rand(1..60).minutes
  self.class.perform_in(retry_in, date_time, restaurant_id, first_sync, type)
ensure
  client.close_connection if client.present?
end