/
tokyo_store.rb
138 lines (115 loc) · 4.06 KB
/
tokyo_store.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
require 'rufus/tokyo/tyrant'
# require 'tokyocabinet'
module ActiveSupport
module Cache
# A cache store implementation which stores data in Tokyo Cabinet
#
# Special features:
# - Clustering and load balancing. TODO
# - Time-based expiry support. TODO (Lua)
# - Per-request in memory cache for all communication with the Tokyo server(s).
class TokyoStore < Store
def self.build_tokyo(*store)
store = store.flatten
options = store.extract_options!
#TODO: multiple instances
store = store.empty? ? ["localhost", 45001] : store[0].split(":")
#TODO: Auto choice between tyrant ffi x tyrant pure ruby x cabinet C
# Tyrant FFI
Rufus::Tokyo::Tyrant.new(store[0], store[1].to_i)
# Cabinet C
#hdb = HDB.new
# if !hdb.open(store[0], HDB::OWRITER | HDB::OCREAT)
# ecode = hdb.ecode
# STDERR.printf("open error: %s\n", hdb.errmsg(ecode))
# end
# hdb
end
# Creates a new TokyoStore object, with the given tyrant server
# addresses. Each address is either a host name, or a host-with-port string
# in the form of "host_name:port". For example:
#
# ActiveSupport::Cache::TokyoStore.new("localhost", "server-downstairs.localnetwork:8229")
#
# If no addresses are specified, then TokyoStore will connect to
# localhost port 45001 (the default memcached port).
def initialize(*store)
if store.first.respond_to?(:get)
@data = store.first
else
@data = self.class.build_tokyo(*store)
end
extend Strategy::LocalCache
end
# Reads multiple keys from the cache.
def read_multi(*keys)
#keys.inject({ }){ |h,k| h.merge({ k => read(k)}) }
@data.lget(keys).inject({ }) { |h, k| h.merge({ k[0] => Marshal.load(k[1])})} #
end
def read(key, options = nil) # :nodoc:
# TODO: benchmark [key] vs .get(key)
super
return nil unless val = @data[key]
val = Marshal.load(val) unless raw?(options)
val
# if str = @data.get(key)
# Marshal.load str
# else
# STDERR.printf("get error: %s\n", @data.errmsg(@data.ecode))
# end
# logger.error("TokyoError (#{e}): #{e.message}")
# nil
end
# Writes a value to the cache.
#
# Possible options:
# - +:unless_exist+ - set to true if you don't want to update the cache
# if the key is already set.
def write(key, value, options = nil)
super
method = options && options[:unless_exist] ? :add : :set
# will break the connection if you send it an integer
# in raw mode, so we convert it to a string to be sure it continues working.
value = raw?(options) ? value.to_s : Marshal.dump(value) # if value.instance_of? Hash
@data[key] = value
###response = @data.put(key, value) || STDERR.printf("get error: %s\n", @data.errmsg(@data.ecode))#, expires_in(options), raw?(options))
# logger.error("TokyoError (#{e}): #{e.message}")
# false
end
def delete(key, options = nil) # :nodoc:
super
@data.delete(key) #= nil #, expires_in(options))
end
def exist?(key, options = nil) # :nodoc:
# Local cache is checked first?
!@data[key].nil?
end
def increment(key, amount = 1) # :nodoc:
#NATIVE breaks...rufus integer prob?
# @data.incr(key, amount)
@data[key] = (@data[key].to_i + amount).to_s
end
def decrement(key, amount = 1) # :nodoc:
# @data.incr(key, -amount)
increment(key, -amount)
end
def delete_matched(matcher, options = nil) # :nodoc:
#TODO @data.ldelete?
end
def clear
@data.clear
end
def stats
@data.stat
end
private
#TODO
# def expires_in(options)
# (options && options[:expires_in]) || 0
# end
def raw?(options)
options && options[:raw]
end
end
end
end