merged cont.
[opensuse:yast-rest-service.git] / webservice / lib / yast_cache.rb
1 #--
2 # Webyast Webservice framework
3 #
4 # Copyright (C) 2009, 2010 Novell, Inc. 
5 #   This library is free software; you can redistribute it and/or modify
6 # it only under the terms of version 2.1 of the GNU Lesser General Public
7 # License as published by the Free Software Foundation. 
8 #
9 #   This library is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 
12 # details. 
13 #
14 #   You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software 
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 #++
18
19 require 'digest/md5'
20
21 class YastCache
22
23   include Singleton
24
25
26
27   def YastCache.active; @active ||= false; end
28   def YastCache.active= a; @active = a; end
29   def YastCache.job_queue_enabled?; YastCache.active; end
30
31   def YastCache.find_key(model_name, key = :all)
32     model_name.capitalize!
33     object = Object.const_get((model_name).classify) rescue $!
34     if object.class == NameError && model_name.end_with?("s")
35       #trying real "s" like "dn" -> "dns", "kerbero" -> "kerberos",...
36       model_name = (model_name).classify + "s"
37       object = Object.const_get(model_name) rescue $!
38     else
39       model_name = (model_name).classify
40     end
41     model_name.downcase!
42     if object.class != NameError && object.respond_to?(:find)
43       if object.method(:find).arity != 0 
44         #has :all parameter
45         return "#{model_name}:find:#{key.inspect}"
46       else
47         return "#{model_name}:find"
48       end
49     end    
50     return nil
51   end
52
53   def YastCache.reset(cache_key, delay = 0, delete_cache = true)
54     unless YastCache.active
55 #      Rails.logger.debug "YastCache.reset: Cache is not active"
56       return
57     end
58     #finding involved keys e.g. user:find:<id> includes user:find::all
59     function_array = cache_key.split(":")
60     raise "Invalid job entry: #{cache_key}" if function_array.size < 2
61     keys = [cache_key]
62     unless (function_array.size == 2 ||
63             (function_array.size >= 4 && function_array[3] == "all")) 
64       #add general <module>:find to the list
65       keys << YastCache.find_key(function_array.shift)
66     end
67
68     keys.each { |key|
69       Rails.cache.delete(key) if delete_cache
70       jobs = Delayed::Job.find(:all)
71       found = false
72       jobs.each { |job|
73         if key == job.handler.split("\n")[1].split[1]
74           found = true 
75           if delete_cache
76             job.run_at = Time.now #set starttime to now in order to fill cache as fast as possible
77             job.save 
78           end
79         end
80       } unless jobs.blank?
81       if found
82         Rails.logger.info("Job #{key} already inserted")
83       else
84         Rails.logger.info("Inserting job #{key}")
85         Delayed::Job.enqueue(PluginJob.new(key),0, (delay).seconds.from_now )
86       end
87     }
88   end
89
90   def YastCache.delete(cache_key)
91     unless YastCache.active
92 #      Rails.logger.debug "YastCache.delete: Cache is not active"
93       return
94     end
95     Rails.cache.delete(cache_key)
96
97     #finding involved keys e.g. user:find:<id> includes user:find::all
98     function_array = cache_key.split(":")
99     raise "Invalid job entry: #{cache_key}" if function_array.size < 2
100     YastCache.reset(YastCache.find_key(function_array[0]))
101   end
102     
103   def YastCache.fetch(key, options = {})
104     unless YastCache.active
105 #      Rails.logger.debug "YastCache.fetch: Cache is not active"
106       if  block_given?
107         return yield
108       else
109         Rails.logger.error "YastCache.fetch: No block is given"       
110         return nil
111       end
112     end
113
114     job_delay = 3
115     raised_exception = nil
116     re_load = Rails.cache.exist?(key) ?  true : false
117     if  block_given?
118       ret = Rails.cache.fetch(key, options) {
119         block_ret = nil
120         begin
121           block_ret = yield
122           if block_ret.blank?
123             #no data found -> remove entry from the cache table
124             cache_data = DataCache.find_by_path key
125             cache_data.each { |cache|
126               cache.destroy
127             } unless cache_data.blank? 
128           else
129             #update MD5 if needed
130             md5 = Digest::MD5.hexdigest(block_ret.to_json)
131             cache_data = DataCache.find_by_path key
132             cache_data.each { |cache|
133               if cache.refreshed_md5.blank? || cache.refreshed_md5 != md5
134                 cache.refreshed_md5 = md5
135                 cache.picked_md5 = md5 if cache.picked_md5.blank?
136                 cache.save
137               end
138             } unless cache_data.blank? 
139           end
140         rescue Exception => raised_exception
141           Rails.logger.error "YastCache.fetch(#{key}) failed: #{raised_exception.inspect}"        
142           Rails.logger.error "Trying again in #{job_delay} seconds" if re_load
143         end
144         block_ret
145       }
146       if ret.blank?
147         Rails.cache.delete(key)
148         Rails.logger.debug "deleting empty cache #{key} #{!Rails.cache.exist?(key)}"
149       end
150     else
151       ret = Rails.cache.fetch(key, options)
152       md5 = Digest::MD5.hexdigest(ret.to_json)
153       cache_data = DataCache.find_by_path key
154       cache_data.each { |cache|
155         if cache.picked_md5.blank? || cache.picked_md5 != md5
156           cache.picked_md5 = md5
157           cache.save
158         end
159       } unless cache_data.blank? 
160     end
161     delete_cache = false
162     YastCache.reset(key,job_delay,delete_cache) if re_load #add reload into the job queue
163     return ret if ret.nil?
164     ret.dup #has to be dup cause the cache value is frozen
165   end
166 end
167