coderrr-memcache_queue 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +0 -0
- data/lib/memcache_queue.rb +112 -0
- data/memcache_queue.gemspec +15 -0
- metadata +64 -0
data/README.markdown
ADDED
File without changes
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'memcache'
|
3
|
+
|
4
|
+
# It's your job to catch any signals relating to process termination
|
5
|
+
# (INT, TERM, etc.) and call the shutdown method on each worker. If
|
6
|
+
# this isn't done you run the risk of losing messages.
|
7
|
+
class MemcacheQueue
|
8
|
+
class ShutdownError < StandardError; end
|
9
|
+
|
10
|
+
attr_accessor :add_timeout
|
11
|
+
|
12
|
+
def initialize(name, *args)
|
13
|
+
@name = name or raise ArgumentError, "Must specify a unique name for each worker"
|
14
|
+
@client = MemCache.new *args
|
15
|
+
@first_key = state(:get, name)
|
16
|
+
@add_timeout = 10
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_queue
|
20
|
+
@client.add('latest_added', '0', 0, true)
|
21
|
+
@client.add('latest_read', '0', 0, true)
|
22
|
+
end
|
23
|
+
|
24
|
+
# ! lost message potential !
|
25
|
+
# If it takes more than add_timeout seconds from the time incr
|
26
|
+
# returns to the time the message is add()ed to memcache the message _could_
|
27
|
+
# be lost.
|
28
|
+
def add_msg(msg)
|
29
|
+
raise ShutdownError if @shutdown
|
30
|
+
|
31
|
+
begin
|
32
|
+
latest_msg ||= @client.incr('latest_added')
|
33
|
+
@client.add(latest_msg.to_s, msg)
|
34
|
+
@client.set("added_#{latest_msg}", true)
|
35
|
+
rescue Exception
|
36
|
+
warn "Error on add: #{$!.inspect}, retrying"
|
37
|
+
sleep 1
|
38
|
+
retry
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# ! lost message potential !
|
43
|
+
# If we die between incr and state(:set) the message _will_ be
|
44
|
+
# lost. This would have to be due to hard process kill or other like events.
|
45
|
+
def get_msg
|
46
|
+
raise ShutdownError if @shutdown
|
47
|
+
|
48
|
+
if key = @first_key
|
49
|
+
@first_key = nil
|
50
|
+
else
|
51
|
+
key = @client.incr('latest_read').to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
start_time = Time.now
|
55
|
+
loop do
|
56
|
+
begin
|
57
|
+
if msg = @client.get(key)
|
58
|
+
begin
|
59
|
+
@client.delete(key)
|
60
|
+
rescue Exception
|
61
|
+
warn "Error while deleting key #{$!.inspect}"
|
62
|
+
end
|
63
|
+
return msg
|
64
|
+
end
|
65
|
+
rescue Exception
|
66
|
+
warn "Error on get, #{$!.inspect}, retrying"
|
67
|
+
end
|
68
|
+
state(:set, key) unless @state_set
|
69
|
+
raise ShutdownError if @shutdown
|
70
|
+
return nil if (Time.now - start_time) > @add_timeout and failed_add?(key)
|
71
|
+
sleep 1
|
72
|
+
start_time = Time.now if msgs_left(true) < 0
|
73
|
+
end
|
74
|
+
ensure
|
75
|
+
state(:delete, key) if @state_set and ! @shutdown
|
76
|
+
end
|
77
|
+
|
78
|
+
def msgs_left(neg_value = false)
|
79
|
+
latest_added = @client.get('latest_added', true).to_i
|
80
|
+
latest_read = @client.get('latest_read', true).to_i
|
81
|
+
|
82
|
+
# check for 64bit boundary crossing
|
83
|
+
if latest_added > 2**63 and latest_read < 2**62
|
84
|
+
latest_read += 2**64
|
85
|
+
elsif latest_read > 2**63 and latest_added < 2**62
|
86
|
+
latest_added += 2**64
|
87
|
+
end
|
88
|
+
|
89
|
+
diff = latest_added - latest_read
|
90
|
+
neg_value ? diff : [0, diff].max
|
91
|
+
end
|
92
|
+
|
93
|
+
def shutdown
|
94
|
+
@shutdown = true
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def failed_add?(key)
|
100
|
+
msgs_left(true) >= 0 and ! @client.get("added_#{key}")
|
101
|
+
end
|
102
|
+
|
103
|
+
def state(action, key = nil)
|
104
|
+
return @client.get("state_#@name") if action == :get
|
105
|
+
@client.send(action, "state_#@name", key)
|
106
|
+
@state_set = (action == :set)
|
107
|
+
rescue Exception
|
108
|
+
warn "Error on #{action} state, #{$!.inspect}, retrying"
|
109
|
+
sleep 1
|
110
|
+
retry
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "memcache_queue"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "memcached based queue"
|
5
|
+
s.email = "coderrr.contact@gmail.com"
|
6
|
+
s.homepage = "http://github.com/coderrr/memcache_queue"
|
7
|
+
s.description = "simple message queue which requires only this client and a memcached server"
|
8
|
+
s.authors = ["coderrr"]
|
9
|
+
s.files = [
|
10
|
+
"README.markdown",
|
11
|
+
"memcache_queue.gemspec",
|
12
|
+
"lib/memcache_queue.rb"
|
13
|
+
]
|
14
|
+
s.add_dependency("memcache-client", [">= 1.5.0"])
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: coderrr-memcache_queue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- coderrr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-13 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: memcache-client
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.5.0
|
24
|
+
version:
|
25
|
+
description: simple message queue which requires only this client and a memcached server
|
26
|
+
email: coderrr.contact@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- README.markdown
|
35
|
+
- memcache_queue.gemspec
|
36
|
+
- lib/memcache_queue.rb
|
37
|
+
has_rdoc: false
|
38
|
+
homepage: http://github.com/coderrr/memcache_queue
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.2.0
|
60
|
+
signing_key:
|
61
|
+
specification_version: 2
|
62
|
+
summary: memcached based queue
|
63
|
+
test_files: []
|
64
|
+
|