Browse files

Merge pull request #436 from leonid-shevtsov/master

Fix for expiration intervals longer than 30 days
  • Loading branch information...
2 parents 66e9266 + 1108e65 commit dfd1bfb12769c4bd12f5dba7c316098ff8bd303f @mperham mperham committed Feb 27, 2014
Showing with 31 additions and 2 deletions.
  1. +1 −0 History.md
  2. +18 −2 lib/dalli/server.rb
  3. +12 −0 test/test_server.rb
View
1 History.md
@@ -7,6 +7,7 @@ HEAD
- Rack session will check if servers are up on initialization (arthurnn, #423)
- Add support for IPv6 addresses in hex form, ie: "[::1]:11211" (dplummer, #428)
- Add symbol support for namespace (jingkai #431)
+- Support expiration intervals longer than 30 days (leonid-shevtsov #436)
2.7.0
==========
View
20 lib/dalli/server.rb
@@ -268,6 +268,7 @@ def send_multiget(keys)
def set(key, value, ttl, cas, options)
(value, flags) = serialize(key, value, options)
+ ttl = sanitize_ttl(ttl)
guard_max_value(key, value) do
req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:set])
@@ -278,6 +279,7 @@ def set(key, value, ttl, cas, options)
def add(key, value, ttl, options)
(value, flags) = serialize(key, value, options)
+ ttl = sanitize_ttl(ttl)
guard_max_value(key, value) do
req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:add])
@@ -288,6 +290,7 @@ def add(key, value, ttl, options)
def replace(key, value, ttl, cas, options)
(value, flags) = serialize(key, value, options)
+ ttl = sanitize_ttl(ttl)
guard_max_value(key, value) do
req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:replace])
@@ -309,7 +312,7 @@ def flush(ttl)
end
def decr(key, count, ttl, default)
- expiry = default ? ttl : 0xFFFFFFFF
+ expiry = default ? sanitize_ttl(ttl) : 0xFFFFFFFF
default ||= 0
(h, l) = split(count)
(dh, dl) = split(default)
@@ -320,7 +323,7 @@ def decr(key, count, ttl, default)
end
def incr(key, count, ttl, default)
- expiry = default ? ttl : 0xFFFFFFFF
+ expiry = default ? sanitize_ttl(ttl) : 0xFFFFFFFF
default ||= 0
(h, l) = split(count)
(dh, dl) = split(default)
@@ -376,6 +379,7 @@ def version
end
def touch(key, ttl)
+ ttl = sanitize_ttl(ttl)
write_generic [REQUEST, OPCODES[:touch], key.bytesize, 4, 0, 0, key.bytesize + 4, 0, 0, ttl, key].pack(FORMAT[:touch])
end
@@ -456,6 +460,18 @@ def guard_max_value(key, value)
end
end
+ # https://code.google.com/p/memcached/wiki/NewCommands#Standard_Protocol
+ # > An expiration time, in seconds. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date.
+ MAX_ACCEPTABLE_EXPIRATION_INTERVAL = 30*24*60*60 # 30 days
+ def sanitize_ttl(ttl)
+ if ttl > MAX_ACCEPTABLE_EXPIRATION_INTERVAL
+ Dalli.logger.debug "Expiration interval too long for Memcached, converting to an expiration timestamp"
+ Time.now.to_i + ttl
+ else
+ ttl
+ end
+ end
+
def generic_response(unpack=false)
(extras, _, status, count) = read_header.unpack(NORMAL_HEADER)
data = read(count) if count > 0
View
12 test/test_server.rb
@@ -65,4 +65,16 @@
assert_equal 2, s.weight
end
end
+
+ describe 'ttl translation' do
+ it 'does not translate ttls under 30 days' do
+ s = Dalli::Server.new('localhost')
+ assert_equal s.send(:sanitize_ttl, 30*24*60*60), 30*24*60*60
+ end
+
+ it 'translates ttls over 30 days into timestamps' do
+ s = Dalli::Server.new('localhost')
+ assert_equal s.send(:sanitize_ttl, 30*24*60*60 + 1), Time.now.to_i + 30*24*60*60+1
+ end
+ end
end

0 comments on commit dfd1bfb

Please sign in to comment.