mack-caching 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/gems/cachetastic-2.0.0/bin/cachetastic_drb_server +115 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/base.rb +78 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/drb.rb +51 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/file.rb +49 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/file_base.rb +86 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/html_file.rb +68 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/local_memory.rb +68 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/memcache.rb +114 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/store_object.rb +28 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/cacheable.rb +196 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/base.rb +238 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/mack_session_cache.rb +3 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/page_cache.rb +6 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/rails_session_cache.rb +12 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/connection.rb +24 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/errors/unsupported_adapter.rb +7 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/logger.rb +49 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/rails_extensions/active_record_base.rb +24 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/rails_extensions/cgi_session_store.rb +59 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/ruby_extensions/object.rb +8 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic.rb +70 -0
- data/lib/gems/memcache-client-1.5.0/lib/memcache.rb +805 -0
- data/lib/gems/memcache-client-1.5.0/lib/memcache_util.rb +90 -0
- data/lib/gems.rb +13 -0
- data/lib/mack-caching.rb +2 -1
- data/lib/mack-caching_tasks.rb +2 -0
- metadata +51 -16
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
require 'thread'
|
3
|
+
require 'drb'
|
4
|
+
require 'singleton'
|
5
|
+
require 'optparse'
|
6
|
+
require 'optparse/time'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
# require the necessary gems:
|
10
|
+
require 'rubygems'
|
11
|
+
gem 'mack-facets'
|
12
|
+
require 'mack-facets'
|
13
|
+
gem 'configatron'
|
14
|
+
require 'configatron'
|
15
|
+
gem 'cachetastic'
|
16
|
+
require 'cachetastic'
|
17
|
+
|
18
|
+
$SAFE = 1
|
19
|
+
|
20
|
+
SERVER_OPTIONS = OpenStruct.new
|
21
|
+
SERVER_OPTIONS.host = "127.0.0.1"
|
22
|
+
SERVER_OPTIONS.port = "61676"
|
23
|
+
SERVER_OPTIONS.config_file = nil
|
24
|
+
SERVER_OPTIONS.verbose = false
|
25
|
+
SERVER_OPTIONS.really_verbose = false
|
26
|
+
|
27
|
+
opts = OptionParser.new do |opts|
|
28
|
+
|
29
|
+
opts.on("-h [HOST]") do |v|
|
30
|
+
SERVER_OPTIONS.host = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-p [PORT]", Integer) do |v|
|
34
|
+
SERVER_OPTIONS.port = v
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-c [CONFIG_FILE]") do |v|
|
38
|
+
SERVER_OPTIONS.config_file = v
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-v") do |v|
|
42
|
+
SERVER_OPTIONS.verbose = true
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("-rv") do |v|
|
46
|
+
SERVER_OPTIONS.verbose = true
|
47
|
+
SERVER_OPTIONS.really_verbose = true
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.parse!(ARGV)
|
53
|
+
|
54
|
+
if SERVER_OPTIONS.config_file
|
55
|
+
configatron.configure_from_yaml(SERVER_OPTIONS.config_file)
|
56
|
+
end
|
57
|
+
|
58
|
+
module Cachetastic
|
59
|
+
module Drb
|
60
|
+
class Server
|
61
|
+
include Singleton
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
DRb.start_service("druby://#{SERVER_OPTIONS.host}:#{SERVER_OPTIONS.port}", self)
|
65
|
+
puts "Cachetastic::Drb::Server listening for connection..."
|
66
|
+
DRb.thread.join
|
67
|
+
end # initialize
|
68
|
+
|
69
|
+
def get(ns, key)
|
70
|
+
puts "get: #{ns}.#{key}" if SERVER_OPTIONS.verbose
|
71
|
+
puts cache_for_namespace(ns).inspect if SERVER_OPTIONS.really_verbose
|
72
|
+
value = cache_for_namespace(ns).get(key)
|
73
|
+
puts "\tvalue.class: #{value.class}" if SERVER_OPTIONS.verbose
|
74
|
+
value
|
75
|
+
end
|
76
|
+
|
77
|
+
def set(ns, key, value, expiry = 0)
|
78
|
+
puts "set: #{ns}.#{key}: #{value}" if SERVER_OPTIONS.verbose
|
79
|
+
cache_for_namespace(ns).set(key, value)
|
80
|
+
puts cache_for_namespace(ns).inspect if SERVER_OPTIONS.really_verbose
|
81
|
+
value
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete(ns, key, delay = 0)
|
85
|
+
puts "delete: #{ns}.#{key}" if SERVER_OPTIONS.verbose
|
86
|
+
cache_for_namespace(ns).delete(key, delay = 0)
|
87
|
+
puts cache_for_namespace(ns).inspect if SERVER_OPTIONS.really_verbose
|
88
|
+
end
|
89
|
+
|
90
|
+
def expire_all(ns)
|
91
|
+
puts "expire_all: #{ns}" if SERVER_OPTIONS.verbose
|
92
|
+
instance_variable_set("@store_for_#{ns}", Cachetastic::Adapters::LocalMemory.new("store_for_#{ns}"))
|
93
|
+
end
|
94
|
+
|
95
|
+
def ping
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def cache_for_namespace(ns)
|
101
|
+
# puts "@store_for_#{ns}"
|
102
|
+
c = instance_variable_get("@store_for_#{ns}")
|
103
|
+
if c.nil?
|
104
|
+
instance_variable_set("@store_for_#{ns}", Cachetastic::Adapters::LocalMemory.new("store_for_#{ns}"))
|
105
|
+
c = instance_variable_get("@store_for_#{ns}")
|
106
|
+
end
|
107
|
+
c
|
108
|
+
end
|
109
|
+
|
110
|
+
end # Server
|
111
|
+
end # Drb
|
112
|
+
end # Cachetastic
|
113
|
+
|
114
|
+
# let's start `er up!
|
115
|
+
Cachetastic::Drb::Server.instance
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# This class is the interface used to develop adapters for stores.
|
2
|
+
# Stores are where the data is actually held. These could be local memory,
|
3
|
+
# memcached, a database, the file system, etc...
|
4
|
+
# If you implement this API, then you should be able to plug in different
|
5
|
+
# stores for your caches without having to change any of your code.
|
6
|
+
#
|
7
|
+
# === Methods that need to be implemented:
|
8
|
+
# * setup - used to setup the implementation of the adapter.
|
9
|
+
# * set(key, object, expiry) - sets an object into the store using the given key and the expiry time.
|
10
|
+
# * get(key) - returns an object from the store for a given key.
|
11
|
+
# * delete(key, delay) - deletes an object from the store for a given key. If the store supports it, a delay can be used.
|
12
|
+
# * expire_all - expires all objects in the store for a given cache.
|
13
|
+
# * stats - returns statistics for the store.
|
14
|
+
# * valid? - used to test whether or not the store is still valid. If this returns false a new instance of the adapter is created by Cachetastic::Connection
|
15
|
+
class Cachetastic::Adapters::Base
|
16
|
+
|
17
|
+
# attr_reader :all_options
|
18
|
+
# attr_reader :store_options
|
19
|
+
# attr_reader :servers
|
20
|
+
attr_reader :name
|
21
|
+
# attr_reader :logging
|
22
|
+
attr_reader :logger
|
23
|
+
|
24
|
+
def initialize(name)
|
25
|
+
@name = name
|
26
|
+
@logger = Cachetastic::Logger.new(configuration.retrieve(:logger, ::Logger.new(STDOUT)))
|
27
|
+
setup
|
28
|
+
if self.debug?
|
29
|
+
self.logger.debug(self.name, :self, self.inspect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
needs_method :setup
|
34
|
+
needs_method :set
|
35
|
+
needs_method :get
|
36
|
+
needs_method :delete
|
37
|
+
needs_method :expire_all
|
38
|
+
needs_method :stats
|
39
|
+
needs_method :valid?
|
40
|
+
|
41
|
+
# Returns true/or falsed based on whether or not the debug setting is set to true in the
|
42
|
+
# configuration file. If the config setting is set, then false is returned.
|
43
|
+
def debug?
|
44
|
+
ivar_cache(:debug) do
|
45
|
+
configuration.retrieve(:debug, false)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stats
|
50
|
+
cache_name = self.name.to_s.camelize
|
51
|
+
adapter_type = self.class.to_s.gsub('Cachetastic::Adapters::', '')
|
52
|
+
s = "Cache: #{cache_name}\nStore Type: #{adapter_type}\n"
|
53
|
+
if self.servers
|
54
|
+
servers = self.servers.join(',')
|
55
|
+
s += "Servers: #{servers}"
|
56
|
+
end
|
57
|
+
puts s
|
58
|
+
end
|
59
|
+
|
60
|
+
def configuration
|
61
|
+
Cachetastic::Adapters::Base.configuration(self.name)
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
# Returns either the options
|
66
|
+
# Options need to be specified in configatrion as the methodized name of the cache with
|
67
|
+
# _options attached at the end.
|
68
|
+
# Examples:
|
69
|
+
# Cachetastic::Caches::PageCache # => cachetastic_caches_page_cache_options
|
70
|
+
# MyAwesomeCache # => my_awesome_cache_options
|
71
|
+
def configuration(name)
|
72
|
+
name = "#{name}_options"
|
73
|
+
conf = configatron.retrieve(name, configatron.cachetastic_default_options)
|
74
|
+
conf
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# This adapter uses Cachetastic::Drb::Server as it's backing.
|
2
|
+
# The configuration for this should look something like this:
|
3
|
+
# my_awesome_cache_options:
|
4
|
+
# debug: false
|
5
|
+
# adapter: drb
|
6
|
+
# store_options:
|
7
|
+
# host: druby://127.0.0.1:61676
|
8
|
+
class Cachetastic::Adapters::Drb < Cachetastic::Adapters::Base
|
9
|
+
|
10
|
+
attr_accessor :drb_store
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
begin
|
14
|
+
return self.drb_store.ping
|
15
|
+
rescue Exception => e
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
# self.all_options["marshall_method"] = "ruby"
|
22
|
+
self.drb_store = DRbObject.new_with_uri(configuration.servers)
|
23
|
+
end
|
24
|
+
|
25
|
+
def expire_all
|
26
|
+
self.drb_store.expire_all(self.name)
|
27
|
+
end
|
28
|
+
|
29
|
+
# See Cachetastic::Adapters::Base
|
30
|
+
def get(key)
|
31
|
+
Cachetastic::Caches::Base.unmarshall(self.drb_store.get(self.name, key))
|
32
|
+
end
|
33
|
+
|
34
|
+
def set(key, value, expiry = 0)
|
35
|
+
self.drb_store.set(self.name, key, Cachetastic::Caches::Base.marshall(value), expiry)
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(key, delay = 0)
|
39
|
+
self.drb_store.delete(self.name, key)
|
40
|
+
end
|
41
|
+
|
42
|
+
def stats
|
43
|
+
super
|
44
|
+
begin
|
45
|
+
self.drb_store.stats if self.drb_store.respond_to? 'stats'
|
46
|
+
rescue Exception => e
|
47
|
+
puts "Calling stats on the DRb store raised this exception: #{e.message}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# This adapter uses the file system as it's backing.
|
2
|
+
# The configuration for this should look something like this:
|
3
|
+
# my_awesome_cache_options:
|
4
|
+
# debug: false
|
5
|
+
# adapter: file
|
6
|
+
# marshall_method: none
|
7
|
+
# default_expiry: <%= 24.hours %>
|
8
|
+
# store_options:
|
9
|
+
# dir: /usr/local/caches/
|
10
|
+
# logging:
|
11
|
+
# logger_1:
|
12
|
+
# type: file
|
13
|
+
# file: log/file_store_cache.log
|
14
|
+
class Cachetastic::Adapters::File < Cachetastic::Adapters::FileBase
|
15
|
+
|
16
|
+
def get(key)
|
17
|
+
full_path = full_path_from_dir(get_key_directoy(key, false))
|
18
|
+
return nil unless File.exists?(full_path)
|
19
|
+
so = YAML::load(File.open(full_path).read)
|
20
|
+
if so
|
21
|
+
if so.invalid?
|
22
|
+
self.delete(key)
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
if so.value.is_a?(YAML::Object)
|
26
|
+
require so.value.class.underscore
|
27
|
+
so = YAML::load(File.open(full_path).read)
|
28
|
+
end
|
29
|
+
return so.value
|
30
|
+
end
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def set(key, value, expiry = 0)
|
35
|
+
so = Cachetastic::Adapters::StoreObject.new(key.to_s, value, expiry)
|
36
|
+
File.open(full_path_from_key(key), "w") do |f|
|
37
|
+
f.puts YAML.dump(so)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
def store_file_name
|
43
|
+
return STORE_FILE_NAME
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
STORE_FILE_NAME = "cache.yml"
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'base64'
|
3
|
+
class Cachetastic::Adapters::FileBase < Cachetastic::Adapters::Base
|
4
|
+
|
5
|
+
attr_reader :directory
|
6
|
+
attr_reader :hashed_keys
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@directory = File.join(self.configuration.store_options.dir, self.name.to_s)
|
10
|
+
FileUtils.mkdir_p(self.directory, :verbose => self.debug?)
|
11
|
+
@hashed_keys = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
File.exists?(self.directory)
|
16
|
+
end
|
17
|
+
|
18
|
+
def stats
|
19
|
+
super
|
20
|
+
num_files = num_directories = file_size = 0
|
21
|
+
everything = Dir.glob("#{self.directory}/**/*")
|
22
|
+
everything.reject{|x| x =~ /^\./}.each do |entry|
|
23
|
+
if ::File.directory?(entry)
|
24
|
+
num_directories += 1
|
25
|
+
else
|
26
|
+
file_size += ::File.size(entry)
|
27
|
+
num_files += 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
puts "Number of Files: #{num_files}\nNumber of Directories: #{num_directories}\nTotal Size on Disk: #{file_size/1024.to_f} KB\n\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(key, delay = 0)
|
34
|
+
if delay <= 0
|
35
|
+
FileUtils.rm_rf(get_key_directoy(key), :verbose => self.debug?)
|
36
|
+
else
|
37
|
+
so = self.get(key)
|
38
|
+
if so
|
39
|
+
self.set(so.key, so.value, delay)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def expire_all
|
45
|
+
FileUtils.rm_rf(self.directory, :verbose => self.debug?)
|
46
|
+
setup
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
def store_file_name
|
51
|
+
return "cachetastic.data"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def directory_from_key(key)
|
56
|
+
hkey = Base64.encode64(Digest::MD5.digest(key))
|
57
|
+
hkey.gsub!("==\n", "")
|
58
|
+
i = 0
|
59
|
+
path = ""
|
60
|
+
until i >= hkey.length do
|
61
|
+
path = File.join(path, hkey[i..i+2])
|
62
|
+
i += 3
|
63
|
+
end
|
64
|
+
path
|
65
|
+
end
|
66
|
+
|
67
|
+
def full_path_from_key(key)
|
68
|
+
full_path_from_dir(get_key_directoy(key))
|
69
|
+
end
|
70
|
+
|
71
|
+
def full_path_from_dir(dir)
|
72
|
+
File.join(dir, store_file_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_key_directoy(key, mkdir = true)
|
76
|
+
hkey = self.hashed_keys[key.to_sym]
|
77
|
+
if hkey.nil?
|
78
|
+
path = File.join(self.directory, directory_from_key(key))
|
79
|
+
self.hashed_keys[key.to_sym] = path
|
80
|
+
hkey = path
|
81
|
+
end
|
82
|
+
FileUtils.mkdir_p(hkey, :verbose => self.debug?) if mkdir
|
83
|
+
hkey
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# This adapter uses the file system as it's backing.
|
2
|
+
# Caches are stored in files called index.html.
|
3
|
+
# At the beginning of the html the following html comments are pre-pended:
|
4
|
+
# <!-- cachetastic: expires_at: Fri Feb 01 20:51:45 -0500 2008 -->
|
5
|
+
# <!-- cachetastic: key: /channels/1-arts-humanities -->
|
6
|
+
# Obviously the expires_at date will be the ACTUAL expire date,
|
7
|
+
# same goes for the key.
|
8
|
+
# The configuration for this should look something like this:
|
9
|
+
# my_awesome_cache_options:
|
10
|
+
# debug: false
|
11
|
+
# adapter: html_file
|
12
|
+
# marshall_method: none
|
13
|
+
# default_expiry: <%= 24.hours %>
|
14
|
+
# store_options:
|
15
|
+
# dir: /usr/local/caches/
|
16
|
+
# logging:
|
17
|
+
# logger_1:
|
18
|
+
# type: file
|
19
|
+
# file: log/file_store_cache.log
|
20
|
+
require 'time'
|
21
|
+
class Cachetastic::Adapters::HtmlFile < Cachetastic::Adapters::FileBase
|
22
|
+
|
23
|
+
def get(key)
|
24
|
+
full_path = full_path_from_dir(get_key_directoy(key, false))
|
25
|
+
return nil unless File.exists?(full_path)
|
26
|
+
so = html_to_store_object(File.open(full_path).read)
|
27
|
+
if so
|
28
|
+
if so.invalid?
|
29
|
+
self.delete(key)
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
return so.value
|
33
|
+
end
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(key, value, expiry = 0)
|
38
|
+
so = Cachetastic::Adapters::StoreObject.new(key.to_s, value, expiry)
|
39
|
+
File.open(full_path_from_key(key), "w") do |f|
|
40
|
+
f.puts store_object_to_html(so)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def store_file_name
|
46
|
+
return STORE_FILE_NAME
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def html_to_store_object(html)
|
51
|
+
key = html.match(/<!-- cachetastic: key: (.*) -->/).captures.first
|
52
|
+
expires_at = html.match(/<!-- cachetastic: expires_at: (.*) -->/).captures.first
|
53
|
+
html.gsub!(/<!-- cachetastic: key: .* -->/, '')
|
54
|
+
html.gsub!(/<!-- cachetastic: expires_at: .* -->/, '')
|
55
|
+
so = Cachetastic::Adapters::StoreObject.new(key, html, 0)
|
56
|
+
so.expires_at = Time.parse(expires_at)
|
57
|
+
so
|
58
|
+
end
|
59
|
+
|
60
|
+
def store_object_to_html(so)
|
61
|
+
x = so.value
|
62
|
+
x << "\n<!-- cachetastic: expires_at: #{so.expires_at} -->"
|
63
|
+
x << "\n<!-- cachetastic: key: #{so.key} -->"
|
64
|
+
end
|
65
|
+
|
66
|
+
STORE_FILE_NAME = "index.html"
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# An adapter/store that keeps objects in local memory. This is great for development/testing,
|
2
|
+
# but probably shouldn't be put into production.
|
3
|
+
# It's also a very good example of how to write a adapter.
|
4
|
+
class Cachetastic::Adapters::LocalMemory < Cachetastic::Adapters::Base
|
5
|
+
|
6
|
+
attr_accessor :local_store
|
7
|
+
|
8
|
+
def valid?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
self.local_store = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def expire_all
|
17
|
+
self.local_store = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# See Cachetastic::Adapters::Base
|
21
|
+
def get(key)
|
22
|
+
so = self.local_store[key.to_s]
|
23
|
+
if so
|
24
|
+
if so.invalid?
|
25
|
+
self.delete(key)
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
return so.value
|
29
|
+
end
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def set(key, value, expiry = 0)
|
34
|
+
self.local_store[key.to_s] = Cachetastic::Adapters::StoreObject.new(key.to_s, value, expiry)
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(key, delay = 0)
|
38
|
+
if delay <= 0
|
39
|
+
self.local_store.delete(key.to_s)
|
40
|
+
else
|
41
|
+
so = self.get(key)
|
42
|
+
if so
|
43
|
+
self.set(so.key, so.value, delay)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stats
|
49
|
+
super
|
50
|
+
num_keys = self.local_store.size
|
51
|
+
s = "Number of Entries: #{num_keys}\n"
|
52
|
+
if num_keys > 0
|
53
|
+
expiries = []
|
54
|
+
keys = []
|
55
|
+
self.local_store.each do |key,value|
|
56
|
+
keys << key
|
57
|
+
expiries << value.expires_at
|
58
|
+
end
|
59
|
+
expiries.sort! {|x, y| x <=> y}
|
60
|
+
oldest_expiry = expiries.first
|
61
|
+
newest_expiry = expiries.last
|
62
|
+
s += "Oldest Entry: #{oldest_expiry}\nNewest Entry: #{newest_expiry}\nKeys: #{keys.inspect}\n"
|
63
|
+
end
|
64
|
+
puts s + "\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# This adapter uses Memcache as it's backing.
|
2
|
+
# The configuration for this should look something like this:
|
3
|
+
# my_awesome_cache_options:
|
4
|
+
# debug: false
|
5
|
+
# adapter: memcache
|
6
|
+
# marshall_method: none
|
7
|
+
# default_expiry: <%= 24.hours %>
|
8
|
+
# store_options:
|
9
|
+
# c_threshold: 10_000
|
10
|
+
# compression: true
|
11
|
+
# debug: false
|
12
|
+
# readonly: false
|
13
|
+
# urlencode: false
|
14
|
+
# logging:
|
15
|
+
# logger_1:
|
16
|
+
# type: file
|
17
|
+
# file: log/memcached.log
|
18
|
+
# servers:
|
19
|
+
# - 127.0.0.1:11211
|
20
|
+
class Cachetastic::Adapters::Memcache < Cachetastic::Adapters::Base
|
21
|
+
|
22
|
+
def setup
|
23
|
+
self.conn = MemCache.new(configuration.servers, configuration.store_options.to_hash.merge({:namespace => self.namespace}))
|
24
|
+
self.version = self.get_version(self.name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def set(key, value, expiry = 0, raw = false)
|
28
|
+
self.conn.set(key, value, expiry, raw)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(key, delay = 0)
|
32
|
+
self.conn.delete(key, delay)
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(key, raw = false)
|
36
|
+
self.conn.get(key, raw)
|
37
|
+
end
|
38
|
+
|
39
|
+
def expire_all
|
40
|
+
self.increment_version(self.name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
self.conn.inspect + " <version: #{self.version}> #{self.conn.stats.inspect}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid?
|
48
|
+
begin
|
49
|
+
return (self.conn.active? && self.version == self.get_version(self.name))
|
50
|
+
rescue Exception => e
|
51
|
+
puts e.message
|
52
|
+
puts e.backtrace.join("\n")
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def stats
|
58
|
+
super
|
59
|
+
begin
|
60
|
+
puts "Memcache stats for all caches:"
|
61
|
+
memc = self.conn
|
62
|
+
puts Kernel.pp_to_s(memc.stats)
|
63
|
+
paths = `sh -c 'echo $PATH'`
|
64
|
+
paths = paths.split(':')
|
65
|
+
memcached_tool_found = false
|
66
|
+
paths.each do |path|
|
67
|
+
cmd_path = File.expand_path(File.join(path,'memcached_tool'))
|
68
|
+
if File.exists?(cmd_path)
|
69
|
+
memcached_tool_found = true
|
70
|
+
break
|
71
|
+
end
|
72
|
+
end
|
73
|
+
if memcached_tool_found
|
74
|
+
configuration.memcache_servers.each do |server|
|
75
|
+
puts `memcached_tool #{server}`
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue
|
79
|
+
end
|
80
|
+
puts ""
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
attr_accessor :conn
|
85
|
+
attr_accessor :version
|
86
|
+
|
87
|
+
def namespace
|
88
|
+
v = self.get_version(self.name)
|
89
|
+
return "#{name}.#{v}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def ns_versions
|
93
|
+
ivar_cache do
|
94
|
+
ns_conn = MemCache.new(configuration.servers, configuration.store_options.to_hash.merge({:namespace => :namespace_versions}))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def increment_version(name)
|
99
|
+
name = name.to_s
|
100
|
+
v = get_version(name)
|
101
|
+
self.ns_versions.set(name, v + 1)
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_version(name)
|
105
|
+
name = name.to_s
|
106
|
+
v = self.ns_versions.get(name)
|
107
|
+
if v.nil?
|
108
|
+
self.ns_versions.set(name, 1)
|
109
|
+
v = 1
|
110
|
+
end
|
111
|
+
v
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Cachetastic::Adapters::StoreObject #:nodoc:#
|
2
|
+
attr_accessor :key
|
3
|
+
attr_accessor :value
|
4
|
+
attr_accessor :expires_at
|
5
|
+
|
6
|
+
def initialize(key, value, expiry)
|
7
|
+
self.key = key
|
8
|
+
self.value = value
|
9
|
+
begin
|
10
|
+
self.expires_at = (Time.now + (expiry == 0 ? (31536000) : expiry)) # 31536000 = one year
|
11
|
+
rescue RangeError => e
|
12
|
+
self.expires_at = Time.at(expiry)
|
13
|
+
end
|
14
|
+
# puts "now: #{Time.now}"
|
15
|
+
# puts "expiry: #{expiry}"
|
16
|
+
# puts "expires_at: #{self.expires_at}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def size
|
20
|
+
return self.value.size if self.value.respond_to?(:size)
|
21
|
+
-1
|
22
|
+
end
|
23
|
+
|
24
|
+
def invalid?
|
25
|
+
Time.now >= self.expires_at
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|