module RailsConnector class DynamoCmsBackend CACHE_PREFIX = 'revision' def initialize @query_counter = 0 end def begin_caching @editable_cache = Configuration.cache_editable_workspaces ? persistent_cache : Cache.new @read_only_cache = persistent_cache end def end_caching @editable_cache = @read_only_cache = nil end def find_workspace_data_by_id(id) WorkspaceDataFromDatabase.find(id) rescue Kvom::NotFound end def find_obj_data_by(workspace_data, index, keys) revision = workspace_data.revision raw_data = if caching? find_obj_data_from_cache_or_database_by(revision, index, keys) else find_obj_data_from_database_by(revision, index, keys) end raw_data.map do |raw_list| raw_list.map do |raw_obj_data| ObjDataFromDatabase.new(raw_obj_data, revision) end end end def caching? @read_only_cache && @editable_cache end def query_counter @query_counter end private def persistent_cache Cache.new(:fallback_backend => Rails.cache, :cache_prefix => CACHE_PREFIX) end def find_obj_data_from_database_by(revision, index, keys) return [] if keys.blank? @query_counter += 1 instrumenter = ActiveSupport::Notifications.instrumenter instrumenter.instrument( "cms_load.rails_connector", :name => "Obj Load", :index => index, :keys => keys ) do keys.map do |key| results = revision.chain.query(index, key) results.values.map { |row| extract_obj_data(row) } end end end def find_obj_data_from_cache_or_database_by(revision, index, keys) keys_from_database = [] # load results from cache results_from_cache = keys.map do |key| cache_for(revision).read(cache_key_for(revision, index, key)).tap do |objs| keys_from_database << key unless objs end end # load cache misses from database and store them in cache results_from_database = find_obj_data_from_database_by(revision, index, keys_from_database) keys_from_database.each_with_index do |key, key_number| store_obj_data_list_in_cache(revision, index, key, results_from_database[key_number]) end # combine the results results_from_cache.map do |objs_from_cache| objs_from_cache || results_from_database.shift end end UNIQUE_INDICES = [:id, :path, :permalink].freeze OBJ_PROPERTY_VALUE_TO_RANGE_VALUE_CONVERSIONS = { :path => PathConversion.method(:path_from_list) }.freeze def store_obj_data_list_in_cache(revision, index, key, obj_data_list) obj_data_list.each do |obj_data| values = obj_data["values"] UNIQUE_INDICES.each do |unique_index| index_value = values["_#{unique_index}"] if (converter = OBJ_PROPERTY_VALUE_TO_RANGE_VALUE_CONVERSIONS[unique_index]) index_value = converter.call(index_value) end store_item_in_cache(revision, unique_index, index_value, [obj_data]) end end unless UNIQUE_INDICES.include?(index) store_item_in_cache(revision, index, key, obj_data_list) end end def store_item_in_cache(revision, index, key, item) cache_for(revision).write(cache_key_for(revision, index, key), item) if caching? end def cache_key_for(revision, index, key) "#{revision.id}/obj/#{index}/#{key}" end def extract_obj_data(data) { "values" => data["values"].merge( "_id" => data["obj_id"], "_obj_type" => data["obj_type"], "_obj_class" => data["obj_class_name"] ), "rtc_ref" => data["rtc_ref"], "ext_ref" => data["ext_ref"], } end def cache_for(revision) revision.editable ? @editable_cache : @read_only_cache end end end