class Unleash::ToggleFetcher

Attributes

etag[RW]
retry_count[RW]
toggle_cache[RW]
toggle_lock[RW]
toggle_resource[RW]

Public Class Methods

new() click to toggle source
# File lib/unleash/toggle_fetcher.rb, line 9
def initialize
  self.etag = nil
  self.toggle_cache = nil
  self.toggle_lock = Mutex.new
  self.toggle_resource = ConditionVariable.new
  self.retry_count = 0

  # start by fetching synchronously, and failing back to reading the backup file.
  begin
    fetch
  rescue StandardError => e
    Unleash.logger.warn "ToggleFetcher was unable to fetch from the network, attempting to read from backup file."
    Unleash.logger.debug "Exception Caught: #{e}"
    read!
  end

  # once initialized, somewhere else you will want to start a loop with fetch()
end

Public Instance Methods

fetch() click to toggle source

rename to refresh_from_server! ??

# File lib/unleash/toggle_fetcher.rb, line 37
def fetch
  Unleash.logger.debug "fetch()"
  response = Unleash::Util::Http.get(Unleash.configuration.fetch_toggles_url, etag)

  if response.code == '304'
    Unleash.logger.debug "No changes according to the unleash server, nothing to do."
    return
  elsif response.code != '200'
    raise IOError, "Unleash server returned a non 200/304 HTTP result."
  end

  self.etag = response['ETag']
  response_hash = JSON.parse(response.body)

  if response_hash['version'] >= 1
    features = response_hash['features']
  else
    raise NotImplemented, "Version of features provided by unleash server" \
      " is unsupported by this client."
  end

  # always synchronize with the local cache when fetching:
  synchronize_with_local_cache!(features)

  update_running_client!
  save!
end
save!() click to toggle source
# File lib/unleash/toggle_fetcher.rb, line 65
def save!
  Unleash.logger.debug "Will save toggles to disk now"
  begin
    backup_file = Unleash.configuration.backup_file
    backup_file_tmp = "#{backup_file}.tmp"

    self.toggle_lock.synchronize do
      file = File.open(backup_file_tmp, "w")
      file.write(self.toggle_cache.to_json)
      file.close
      File.rename(backup_file_tmp, backup_file)
    end
  rescue StandardError => e
    # This is not really the end of the world. Swallowing the exception.
    Unleash.logger.error "Unable to save backup file. Exception thrown #{e.class}:'#{e}'"
    Unleash.logger.error "stacktrace: #{e.backtrace}"
  ensure
    file&.close if defined?(file)
    self.toggle_lock.unlock if self.toggle_lock.locked?
  end
end
toggles() click to toggle source
# File lib/unleash/toggle_fetcher.rb, line 28
def toggles
  self.toggle_lock.synchronize do
    # wait for resource, only if it is null
    self.toggle_resource.wait(self.toggle_lock) if self.toggle_cache.nil?
    return self.toggle_cache
  end
end

Private Instance Methods

read!() click to toggle source
# File lib/unleash/toggle_fetcher.rb, line 107
def read!
  Unleash.logger.debug "read!()"
  return nil unless File.exist?(Unleash.configuration.backup_file)

  begin
    file = File.new(Unleash.configuration.backup_file, "r")
    file_content = file.read

    backup_as_hash = JSON.parse(file_content)
    synchronize_with_local_cache!(backup_as_hash)
    update_running_client!
  rescue IOError => e
    Unleash.logger.error "Unable to read the backup_file: #{e}"
  rescue JSON::ParserError => e
    Unleash.logger.error "Unable to parse JSON from existing backup_file: #{e}"
  rescue StandardError => e
    Unleash.logger.error "Unable to extract valid data from backup_file. Exception thrown: #{e}"
  ensure
    file&.close
  end
end
synchronize_with_local_cache!(features) click to toggle source
# File lib/unleash/toggle_fetcher.rb, line 89
def synchronize_with_local_cache!(features)
  if self.toggle_cache != features
    self.toggle_lock.synchronize do
      self.toggle_cache = features
    end

    # notify all threads waiting for this resource to no longer wait
    self.toggle_resource.broadcast
  end
end
update_running_client!() click to toggle source
# File lib/unleash/toggle_fetcher.rb, line 100
def update_running_client!
  if Unleash.toggles != self.toggles
    Unleash.logger.info "Updating toggles to main client, there has been a change in the server."
    Unleash.toggles = self.toggles
  end
end