kim-toms-starling 1.0.0
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/CHANGELOG +60 -0
- data/LICENSE +20 -0
- data/README.rdoc +108 -0
- data/Rakefile +76 -0
- data/bin/starling +6 -0
- data/bin/starling_top +57 -0
- data/etc/sample-config.yml +9 -0
- data/etc/starling.redhat +66 -0
- data/etc/starling.ubuntu +71 -0
- data/lib/starling/handler.rb +239 -0
- data/lib/starling/persistent_queue.rb +156 -0
- data/lib/starling/queue_collection.rb +147 -0
- data/lib/starling/server.rb +125 -0
- data/lib/starling/server_runner.rb +317 -0
- data/lib/starling.rb +139 -0
- data/starling.gemspec +22 -0
- metadata +116 -0
@@ -0,0 +1,317 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'server')
|
2
|
+
require 'erb'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'optparse'
|
5
|
+
require 'yaml'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
module StarlingServer
|
9
|
+
class Runner
|
10
|
+
|
11
|
+
attr_accessor :options
|
12
|
+
private :options, :options=
|
13
|
+
|
14
|
+
def self.run
|
15
|
+
new
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.shutdown
|
19
|
+
@@instance.shutdown
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@@instance = self
|
24
|
+
parse_options
|
25
|
+
|
26
|
+
@process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
|
27
|
+
|
28
|
+
pid = @process.running?
|
29
|
+
if pid
|
30
|
+
STDERR.puts "There is already a Starling process running (pid #{pid}), exiting."
|
31
|
+
exit(1)
|
32
|
+
elsif pid.nil?
|
33
|
+
STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
|
34
|
+
end
|
35
|
+
|
36
|
+
start
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_config_file(filename)
|
40
|
+
config = YAML.load(ERB.new(File.read(filename)).result)
|
41
|
+
|
42
|
+
unless config.is_a?(Hash)
|
43
|
+
STDERR.puts "Config file does not contain a hash: #{filename}, exiting."
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
if config['starling'].nil?
|
48
|
+
STDERR.puts "Missing starling section in config file: #{filename}, exiting."
|
49
|
+
exit(1)
|
50
|
+
end
|
51
|
+
|
52
|
+
config['starling'].each do |key, value|
|
53
|
+
# alias some keys
|
54
|
+
case key
|
55
|
+
when "queue_path" then key = "path"
|
56
|
+
when "log_file" then key = "logger"
|
57
|
+
end
|
58
|
+
|
59
|
+
if %w(logger path pid_file).include?(key)
|
60
|
+
value = File.expand_path(value)
|
61
|
+
end
|
62
|
+
|
63
|
+
options[key.to_sym] = value
|
64
|
+
|
65
|
+
if options[:log_level].instance_of?(String)
|
66
|
+
options[:log_level] = Logger.const_get(options[:log_level])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_options
|
72
|
+
self.options = { :host => '127.0.0.1',
|
73
|
+
:port => 22122,
|
74
|
+
:path => File.join('', 'var', 'spool', 'starling'),
|
75
|
+
:log_level => Logger::INFO,
|
76
|
+
:daemonize => false,
|
77
|
+
:timeout => 0,
|
78
|
+
:pid_file => File.join('', 'var', 'run', 'starling.pid') }
|
79
|
+
|
80
|
+
OptionParser.new do |opts|
|
81
|
+
opts.summary_width = 25
|
82
|
+
|
83
|
+
opts.banner = "Starling (#{StarlingServer::VERSION})\n\n",
|
84
|
+
"usage: starling [options...]\n",
|
85
|
+
" starling --help\n",
|
86
|
+
" starling --version\n"
|
87
|
+
|
88
|
+
opts.separator ""
|
89
|
+
opts.separator "Configuration:"
|
90
|
+
|
91
|
+
opts.on("-f", "--config FILENAME",
|
92
|
+
"Config file (yaml) to load") do |filename|
|
93
|
+
load_config_file(filename)
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on("-q", "--queue_path PATH",
|
97
|
+
:REQUIRED,
|
98
|
+
"Path to store Starling queue logs", "(default: #{options[:path]})") do |queue_path|
|
99
|
+
options[:path] = File.expand_path(queue_path)
|
100
|
+
end
|
101
|
+
|
102
|
+
opts.separator ""; opts.separator "Network:"
|
103
|
+
|
104
|
+
opts.on("-hHOST", "--host HOST", "Interface on which to listen (default: #{options[:host]})") do |host|
|
105
|
+
options[:host] = host
|
106
|
+
end
|
107
|
+
|
108
|
+
opts.on("-pHOST", "--port PORT", Integer, "TCP port on which to listen (default: #{options[:port]})") do |port|
|
109
|
+
options[:port] = port
|
110
|
+
end
|
111
|
+
|
112
|
+
opts.separator ""; opts.separator "Process:"
|
113
|
+
|
114
|
+
opts.on("-d", "Run as a daemon.") do
|
115
|
+
options[:daemonize] = true
|
116
|
+
end
|
117
|
+
|
118
|
+
opts.on("-PFILE", "--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
|
119
|
+
options[:pid_file] = File.expand_path(pid_file)
|
120
|
+
end
|
121
|
+
|
122
|
+
opts.on("-u", "--user USER", "User to run as") do |user|
|
123
|
+
options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
|
124
|
+
end
|
125
|
+
|
126
|
+
opts.on("-gGROUP", "--group GROUP", "Group to run as") do |group|
|
127
|
+
options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
|
128
|
+
end
|
129
|
+
|
130
|
+
opts.separator ""; opts.separator "Logging:"
|
131
|
+
|
132
|
+
opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
|
133
|
+
options[:logger] = File.expand_path(log_path)
|
134
|
+
end
|
135
|
+
|
136
|
+
begin
|
137
|
+
require 'syslog_logger'
|
138
|
+
|
139
|
+
opts.on("-l", "--syslog CHANNEL", "Write logs to the syslog instead of a log file.") do |channel|
|
140
|
+
options[:syslog_channel] = channel
|
141
|
+
end
|
142
|
+
rescue LoadError
|
143
|
+
end
|
144
|
+
|
145
|
+
opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
|
146
|
+
options[:log_level] -= 1
|
147
|
+
end
|
148
|
+
|
149
|
+
opts.on("-t", "--timeout [SECONDS]", Integer,
|
150
|
+
"Time in seconds before disconnecting inactive clients (0 to disable).",
|
151
|
+
"(default: #{options[:timeout]})") do |timeout|
|
152
|
+
options[:timeout] = timeout
|
153
|
+
end
|
154
|
+
|
155
|
+
opts.separator ""; opts.separator "Miscellaneous:"
|
156
|
+
|
157
|
+
opts.on_tail("-?", "--help", "Display this usage information.") do
|
158
|
+
puts "#{opts}\n"
|
159
|
+
exit
|
160
|
+
end
|
161
|
+
|
162
|
+
opts.on_tail("-V", "--version", "Print version number and exit.") do
|
163
|
+
puts "Starling #{StarlingServer::VERSION}\n\n"
|
164
|
+
exit
|
165
|
+
end
|
166
|
+
end.parse!
|
167
|
+
end
|
168
|
+
|
169
|
+
def start
|
170
|
+
drop_privileges
|
171
|
+
|
172
|
+
@process.daemonize if options[:daemonize]
|
173
|
+
|
174
|
+
setup_signal_traps
|
175
|
+
@process.write_pid_file
|
176
|
+
|
177
|
+
STDOUT.puts "Starting at #{options[:host]}:#{options[:port]}."
|
178
|
+
@server = StarlingServer::Base.new(options)
|
179
|
+
@server.run
|
180
|
+
|
181
|
+
@process.remove_pid_file
|
182
|
+
end
|
183
|
+
|
184
|
+
def drop_privileges
|
185
|
+
Process.egid = options[:group] if options[:group]
|
186
|
+
Process.euid = options[:user] if options[:user]
|
187
|
+
end
|
188
|
+
|
189
|
+
def shutdown
|
190
|
+
begin
|
191
|
+
STDOUT.puts "Shutting down."
|
192
|
+
StarlingServer::Base.logger.info "Shutting down."
|
193
|
+
@server.stop
|
194
|
+
rescue Object => e
|
195
|
+
STDERR.puts "There was an error shutting down: #{e}"
|
196
|
+
exit(70)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def setup_signal_traps
|
201
|
+
Signal.trap("INT") { shutdown }
|
202
|
+
Signal.trap("TERM") { shutdown }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class ProcessHelper
|
207
|
+
|
208
|
+
def initialize(log_file = nil, pid_file = nil, user = nil, group = nil)
|
209
|
+
@log_file = log_file
|
210
|
+
@pid_file = pid_file
|
211
|
+
@user = user
|
212
|
+
@group = group
|
213
|
+
end
|
214
|
+
|
215
|
+
def safefork
|
216
|
+
begin
|
217
|
+
if pid = fork
|
218
|
+
return pid
|
219
|
+
end
|
220
|
+
rescue Errno::EWOULDBLOCK
|
221
|
+
sleep 5
|
222
|
+
retry
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def daemonize
|
227
|
+
sess_id = detach_from_terminal
|
228
|
+
exit if pid = safefork
|
229
|
+
|
230
|
+
Dir.chdir("/")
|
231
|
+
File.umask 0000
|
232
|
+
|
233
|
+
close_io_handles
|
234
|
+
redirect_io
|
235
|
+
|
236
|
+
return sess_id
|
237
|
+
end
|
238
|
+
|
239
|
+
def detach_from_terminal
|
240
|
+
srand
|
241
|
+
safefork and exit
|
242
|
+
|
243
|
+
unless sess_id = Process.setsid
|
244
|
+
raise "Couldn't detach from controlling terminal."
|
245
|
+
end
|
246
|
+
|
247
|
+
trap 'SIGHUP', 'IGNORE'
|
248
|
+
|
249
|
+
sess_id
|
250
|
+
end
|
251
|
+
|
252
|
+
def close_io_handles
|
253
|
+
ObjectSpace.each_object(IO) do |io|
|
254
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
255
|
+
begin
|
256
|
+
io.close unless io.closed?
|
257
|
+
rescue Exception
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def redirect_io
|
264
|
+
begin; STDIN.reopen('/dev/null'); rescue Exception; end
|
265
|
+
|
266
|
+
if @log_file
|
267
|
+
begin
|
268
|
+
STDOUT.reopen(@log_file, "a")
|
269
|
+
STDOUT.sync = true
|
270
|
+
rescue Exception
|
271
|
+
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
272
|
+
end
|
273
|
+
else
|
274
|
+
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
275
|
+
end
|
276
|
+
|
277
|
+
begin; STDERR.reopen(STDOUT); rescue Exception; end
|
278
|
+
STDERR.sync = true
|
279
|
+
end
|
280
|
+
|
281
|
+
def rescue_exception
|
282
|
+
begin
|
283
|
+
yield
|
284
|
+
rescue Exception
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def write_pid_file
|
289
|
+
return unless @pid_file
|
290
|
+
FileUtils.mkdir_p(File.dirname(@pid_file))
|
291
|
+
File.open(@pid_file, "w") { |f| f.write(Process.pid) }
|
292
|
+
File.chmod(0644, @pid_file)
|
293
|
+
end
|
294
|
+
|
295
|
+
def remove_pid_file
|
296
|
+
return unless @pid_file
|
297
|
+
File.unlink(@pid_file) if File.exists?(@pid_file)
|
298
|
+
end
|
299
|
+
|
300
|
+
def running?
|
301
|
+
return false unless @pid_file
|
302
|
+
|
303
|
+
pid = File.read(@pid_file).chomp.to_i rescue nil
|
304
|
+
pid = nil if pid == 0
|
305
|
+
return false unless pid
|
306
|
+
|
307
|
+
begin
|
308
|
+
Process.kill(0, pid)
|
309
|
+
return pid
|
310
|
+
rescue Errno::ESRCH
|
311
|
+
return nil
|
312
|
+
rescue Errno::EPERM
|
313
|
+
return pid
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
data/lib/starling.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'memcache'
|
2
|
+
|
3
|
+
class Starling < MemCache
|
4
|
+
|
5
|
+
WAIT_TIME = 0.25
|
6
|
+
alias_method :_original_get, :get
|
7
|
+
alias_method :_original_delete, :delete
|
8
|
+
|
9
|
+
##
|
10
|
+
# fetch an item from a queue.
|
11
|
+
|
12
|
+
def get(*args)
|
13
|
+
loop do
|
14
|
+
response = _original_get(*args)
|
15
|
+
return response unless response.nil?
|
16
|
+
sleep WAIT_TIME
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# will return the next item or nil
|
22
|
+
|
23
|
+
def fetch(*args)
|
24
|
+
_original_get(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Delete the key (queue) from all Starling servers. This is necessary
|
29
|
+
# because the random way a server is chosen in #get_server_for_key
|
30
|
+
# implies that the queue could easily be spread across the entire
|
31
|
+
# Starling cluster.
|
32
|
+
|
33
|
+
def delete(key, expiry = 0)
|
34
|
+
with_servers do
|
35
|
+
_original_delete(key, expiry)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Provides a way to work with a specific list of servers by
|
41
|
+
# forcing all calls to #get_server_for_key to use a specific
|
42
|
+
# server, and changing that server each time that the call
|
43
|
+
# yields to the block provided. This helps work around the
|
44
|
+
# normally random nature of the #get_server_for_key method.
|
45
|
+
#
|
46
|
+
def with_servers(my_servers = self.servers.dup)
|
47
|
+
return unless block_given?
|
48
|
+
my_servers.each do |server|
|
49
|
+
yield
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# insert +value+ into +queue+.
|
55
|
+
#
|
56
|
+
# +expiry+ is expressed as a UNIX timestamp
|
57
|
+
#
|
58
|
+
# If +raw+ is true, +value+ will not be Marshalled. If +raw+ = :yaml, +value+
|
59
|
+
# will be serialized with YAML, instead.
|
60
|
+
|
61
|
+
def set(queue, value, expiry = 0, raw = false)
|
62
|
+
retries = 0
|
63
|
+
begin
|
64
|
+
if raw == :yaml
|
65
|
+
value = YAML.dump(value)
|
66
|
+
raw = true
|
67
|
+
end
|
68
|
+
|
69
|
+
super(queue, value, expiry, raw)
|
70
|
+
rescue MemCache::MemCacheError => e
|
71
|
+
retries += 1
|
72
|
+
sleep WAIT_TIME
|
73
|
+
retry unless retries > 3
|
74
|
+
raise e
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# returns the number of items in +queue+. If +queue+ is +:all+, a hash of all
|
80
|
+
# queue sizes will be returned.
|
81
|
+
|
82
|
+
def sizeof(queue, statistics = nil)
|
83
|
+
statistics ||= stats
|
84
|
+
|
85
|
+
if queue == :all
|
86
|
+
queue_sizes = {}
|
87
|
+
available_queues(statistics).each do |queue|
|
88
|
+
queue_sizes[queue] = sizeof(queue, statistics)
|
89
|
+
end
|
90
|
+
return queue_sizes
|
91
|
+
end
|
92
|
+
|
93
|
+
statistics.inject(0) { |m,(k,v)| m + v["queue_#{queue}_items"].to_i }
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# returns a list of available (currently allocated) queues.
|
98
|
+
|
99
|
+
def available_queues(statistics = nil)
|
100
|
+
statistics ||= stats
|
101
|
+
|
102
|
+
res = []
|
103
|
+
# Each server returns its own statistics
|
104
|
+
statistics.each_pair { |k,v| res = res + v.keys }
|
105
|
+
# Here we rely on the name of one of the statistic names to find the queue.
|
106
|
+
# The name of the queue is embedded in the statistic name
|
107
|
+
res.collect! {|k| /^queue_(.*)_total_items/.match(k)[1] rescue nil }.compact!
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# iterator to flush +queue+. Each element will be passed to the provided
|
112
|
+
# +block+
|
113
|
+
|
114
|
+
def flush(queue)
|
115
|
+
sizeof(queue).times do
|
116
|
+
v = get(queue)
|
117
|
+
yield v if block_given?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def get_server_for_key(key)
|
124
|
+
raise ArgumentError, "illegal character in key #{key.inspect}" if key =~ /\s/
|
125
|
+
raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
|
126
|
+
raise MemCacheError, "No servers available" if servers.empty?
|
127
|
+
|
128
|
+
# Ignores server weights, oh well
|
129
|
+
srvs = servers.dup
|
130
|
+
srvs.size.times do |try|
|
131
|
+
n = rand(srvs.size)
|
132
|
+
server = srvs[n]
|
133
|
+
return server if server.alive?
|
134
|
+
srvs.delete_at(n)
|
135
|
+
end
|
136
|
+
|
137
|
+
raise MemCacheError, "No servers available (all dead)"
|
138
|
+
end
|
139
|
+
end
|
data/starling.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# this file is automatically generated
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "starling"
|
4
|
+
s.version = "1.0.0"
|
5
|
+
s.authors = ["Blaine Cook", "Chris Wanstrath", "Britt Selvitelle", "Glenn Rempe", "Abdul-Rahman Advany", "Seth Fitzsimmons", "Haarm Arts", "Chris Gaffney", "Kim Toms"]
|
6
|
+
s.email = ["blaine@twitter.com", "chris@ozmm.org", "abdulrahman@advany.com", "starlingmq@groups.google.com", "harmaarts@gmail.com", "gaffneyc@gmail.com", "kim.toms@gmail.com"]
|
7
|
+
s.homepage = "http://github.com/starling/starling/"
|
8
|
+
s.summary = "Starling is a lightweight, transactional, distributed queue server"
|
9
|
+
s.description = s.summary
|
10
|
+
|
11
|
+
s.files = ["./CHANGELOG", "./LICENSE", "./README.rdoc", "./Rakefile", "./bin/starling", "./bin/starling_top", "./etc/sample-config.yml", "./etc/starling.redhat", "./etc/starling.ubuntu", "./lib/starling.rb", "./lib/starling/handler.rb", "./lib/starling/persistent_queue.rb", "./lib/starling/queue_collection.rb", "./lib/starling/server.rb", "./lib/starling/server_runner.rb", "./starling.gemspec"]
|
12
|
+
|
13
|
+
s.executables = ["starling", "starling_top"]
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.rdoc_options = ["--quiet", "--title", "starling documentation", "--opname", "index.html", "--line-numbers", "--main", "README.rdoc", "--inline-source"]
|
18
|
+
s.extra_rdoc_files = ["README.rdoc", "CHANGELOG", "LICENSE"]
|
19
|
+
|
20
|
+
s.add_dependency 'fiveruns-memcache-client'
|
21
|
+
s.add_dependency 'eventmachine', [">= 0.12.0"]
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kim-toms-starling
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Blaine Cook
|
8
|
+
- Chris Wanstrath
|
9
|
+
- Britt Selvitelle
|
10
|
+
- Glenn Rempe
|
11
|
+
- Abdul-Rahman Advany
|
12
|
+
- Seth Fitzsimmons
|
13
|
+
- Haarm Arts
|
14
|
+
- Chris Gaffney
|
15
|
+
- Kim Toms
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2009-04-08 00:00:00 -07:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: fiveruns-memcache-client
|
25
|
+
type: :runtime
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: eventmachine
|
35
|
+
type: :runtime
|
36
|
+
version_requirement:
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.12.0
|
42
|
+
version:
|
43
|
+
description: Starling is a lightweight, transactional, distributed queue server
|
44
|
+
email:
|
45
|
+
- blaine@twitter.com
|
46
|
+
- chris@ozmm.org
|
47
|
+
- abdulrahman@advany.com
|
48
|
+
- starlingmq@groups.google.com
|
49
|
+
- harmaarts@gmail.com
|
50
|
+
- gaffneyc@gmail.com
|
51
|
+
- kim.toms@gmail.com
|
52
|
+
executables:
|
53
|
+
- starling
|
54
|
+
- starling_top
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files:
|
58
|
+
- README.rdoc
|
59
|
+
- CHANGELOG
|
60
|
+
- LICENSE
|
61
|
+
files:
|
62
|
+
- ./CHANGELOG
|
63
|
+
- ./LICENSE
|
64
|
+
- ./README.rdoc
|
65
|
+
- ./Rakefile
|
66
|
+
- ./bin/starling
|
67
|
+
- ./bin/starling_top
|
68
|
+
- ./etc/sample-config.yml
|
69
|
+
- ./etc/starling.redhat
|
70
|
+
- ./etc/starling.ubuntu
|
71
|
+
- ./lib/starling.rb
|
72
|
+
- ./lib/starling/handler.rb
|
73
|
+
- ./lib/starling/persistent_queue.rb
|
74
|
+
- ./lib/starling/queue_collection.rb
|
75
|
+
- ./lib/starling/server.rb
|
76
|
+
- ./lib/starling/server_runner.rb
|
77
|
+
- ./starling.gemspec
|
78
|
+
- README.rdoc
|
79
|
+
- CHANGELOG
|
80
|
+
- LICENSE
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://github.com/starling/starling/
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options:
|
85
|
+
- --quiet
|
86
|
+
- --title
|
87
|
+
- starling documentation
|
88
|
+
- --opname
|
89
|
+
- index.html
|
90
|
+
- --line-numbers
|
91
|
+
- --main
|
92
|
+
- README.rdoc
|
93
|
+
- --inline-source
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
version:
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: "0"
|
107
|
+
version:
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.2.0
|
112
|
+
signing_key:
|
113
|
+
specification_version: 2
|
114
|
+
summary: Starling is a lightweight, transactional, distributed queue server
|
115
|
+
test_files: []
|
116
|
+
|