sqsrun 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog ADDED
@@ -0,0 +1,20 @@
1
+
2
+ == 2011-07-18 version 0.5.0
3
+
4
+ * Added respectable timeout implementation
5
+ * Added --create command: creates a new queue
6
+ * Added --delete command: deletes a queue
7
+ * Added --attr command: updates attributes of a queue
8
+ * --list command shows attributes of queues
9
+
10
+
11
+ == 2011-07-11 version 0.4.0
12
+
13
+ * Splits single bin file into multiple files
14
+ * Added test codes
15
+
16
+
17
+ == 2011-07-10 version 0.3.0
18
+
19
+ * Initial release
20
+
data/README.rdoc CHANGED
@@ -20,6 +20,9 @@ A Generics Worker Executor Service for Amazon SQS. It polls SQS queue and run co
20
20
  -s, --secret-key KEY AWS Secret Access Key
21
21
  -q, --queue NAME SQS queue name
22
22
  -t, --timeout SEC SQS visibility timeout (default: 30)
23
+ --create Create new queue
24
+ --delete Delete a queue
25
+ --attr Key=Value Set attribute
23
26
  --push MESSAGE Push maessage to the queue
24
27
  --list List queues
25
28
  --configure PATH.yaml Write configuration file
@@ -27,21 +30,57 @@ A Generics Worker Executor Service for Amazon SQS. It polls SQS queue and run co
27
30
  --run SCRIPT.rb Run method named 'run' defined in the script
28
31
  -e, --extend-timeout SEC Threashold time before extending visibility timeout (default: timeout * 3/4)
29
32
  -x, --kill-timeout SEC Threashold time before killing process (default: timeout * 5)
33
+ -X, --kill-retry SEC Threashold time before retrying killing process (default: 60)
30
34
  -i, --interval SEC Polling interval (default: 1)
31
35
  -d, --daemon PIDFILE Daemonize (default: foreground)
32
36
  -f, --file PATH.yaml Read configuration file
33
37
 
34
- One of --push, --list, --configure, --exec or --run is required. The behavior of the commands is described below:
38
+ One of --list, --create, --delete, --attr, --push, --configure, --exec or --run is required. The behavior of the commands is described below:
39
+
40
+
41
+ === list
42
+
43
+ Show list of queues. -k and -s options are required.
44
+
45
+ _Example:_
46
+
47
+ $ sqsrun -k KEY_ID -s SEC_KEY --list
48
+
49
+
50
+ === create
51
+
52
+ Create new queue. -k, -s and -q options are required.
53
+
54
+ _Example:_
55
+
56
+ $ sqsrun -k KEY_ID -s SEC_KEY --create -q myqueue
57
+
58
+
59
+ === delete
60
+
61
+ Delete a queue. -k, -s and -q options are required. Note that messages in the queue will be lost.
62
+
63
+ _Example:_
64
+
65
+ $ sqsrun -k KEY_ID -s SEC_KEY --delete -q myqueue
66
+
67
+
68
+ === attr
69
+
70
+ Set a attribute on a queue. -k, -s and -q options are required.
71
+
72
+ _Example:_
73
+
74
+ $ sqsrun -k KEY_ID -s SEC_KEY -q myqueue --attr MessageRetentionPeriod=3600
35
75
 
36
76
 
37
77
  === push
38
78
 
39
79
  Push a message to the queue. -k, -s and -q options are required.
40
80
 
81
+ _Example:_
41
82
 
42
- === list
43
-
44
- Show list of queues. -k and -s options are required.
83
+ $ sqsrun -k KEY_ID -s SEC_KEY -q myqueue --push '{"a":"b"}'
45
84
 
46
85
 
47
86
  === configure
@@ -66,12 +105,15 @@ Execute a command when a message is received. Body of the message is passed to t
66
105
  _Example:_
67
106
 
68
107
  #!/usr/bin/env ruby
108
+
69
109
  require 'json'
70
110
  js = JSON.load(STDIN.read)
71
111
  puts "received: #{js.inspect}"
72
112
 
73
113
  # $ sqsrun -k AWS_KEY_ID -s AWS_SEC_KEY -q SQS_NAME --exec ./this_file
74
114
 
115
+ When the kill timeout (-x, --klill-timeout) is elapsed, SIGTERM signal will be sent to the child process. The signal will be repeated every few seconds (-X, --kill-retry).
116
+
75
117
 
76
118
  === run
77
119
 
@@ -80,10 +122,19 @@ This is same as 'exec' except that this calls a method named 'run' defined in th
80
122
  _Example:_
81
123
 
82
124
  require 'json'
125
+
83
126
  def run(msg)
84
127
  js = JSON.load(msg)
85
128
  puts "received: #{js.inspect}"
86
129
  end
87
130
 
131
+ # optionally you can define terminate method
132
+ # which will be called when kill timeout is elapsed.
133
+ def terminate
134
+ end
135
+
88
136
  # $ sqsrun -k AWS_KEY_ID -s AWS_SEC_KEY -q SQS_NAME --run ./this_file.rb
89
137
 
138
+
139
+ When the kill timeout (-x, --klill-timeout) is elapsed, terminate() method will be called (if it is defined). It will be repeated every few seconds (-X, --kill-retry).
140
+
@@ -8,11 +8,13 @@ op.banner += " [-- <ARGV-for-exec-or-run>]"
8
8
 
9
9
  type = nil
10
10
  message = nil
11
+ attrkv = nil
11
12
  confout = nil
12
13
 
13
14
  defaults = {
14
15
  :timeout => 30,
15
16
  :interval => 1,
17
+ :kill_retry => 60,
16
18
  }
17
19
 
18
20
  conf = { }
@@ -33,6 +35,20 @@ op.on('-t', '--timeout SEC', 'SQS visibility timeout (default: 30)', Integer) {|
33
35
  conf[:timeout] = i
34
36
  }
35
37
 
38
+ op.on('--create', 'Create new queue') {|s|
39
+ type = :create
40
+ }
41
+
42
+ op.on('--delete', 'Delete a queue') {|s|
43
+ type = :delete
44
+ }
45
+
46
+ op.on('--attr Key=Value', 'Set attribute') {|s|
47
+ type = :attr
48
+ k, v = s.split('=',2)
49
+ attrkv = [k.to_s, v.to_s]
50
+ }
51
+
36
52
  op.on('--push MESSAGE', 'Push maessage to the queue') {|s|
37
53
  type = :push
38
54
  message = s
@@ -65,6 +81,10 @@ op.on('-x', '--kill-timeout SEC', 'Threashold time before killing process (defau
65
81
  conf[:kill_timeout] = i
66
82
  }
67
83
 
84
+ op.on('-X', '--kill-retry SEC', 'Threashold time before retrying killing process (default: 60)', Integer) {|i|
85
+ conf[:kill_retry] = i
86
+ }
87
+
68
88
  op.on('-i', '--interval SEC', 'Polling interval (default: 1)', Integer) {|i|
69
89
  conf[:interval] = i
70
90
  }
@@ -122,7 +142,7 @@ begin
122
142
  elsif conf[:exec]
123
143
  type = :exec
124
144
  else
125
- raise "--push, --list, --configure, --exec or --run is required"
145
+ raise "--list, --create, --delete, --attr, --push, --configure, --exec or --run is required"
126
146
  end
127
147
  end
128
148
 
@@ -142,7 +162,7 @@ begin
142
162
  conf[:kill_timeout] = conf[:timeout] * 5
143
163
  end
144
164
 
145
- if !conf[:queue] && (type == :push || type == :exec || type == :run)
165
+ if !conf[:queue] && (type != :list && type != :conf)
146
166
  raise "-q, --queue NAME option is required"
147
167
  end
148
168
 
@@ -168,14 +188,54 @@ end
168
188
 
169
189
 
170
190
  case type
191
+ when :create
192
+ con = SQSRun::Controller.new(conf)
193
+ if con.create
194
+ puts "Queue #{conf[:queue].to_s.dump} is created."
195
+ puts "Note that it takes some seconds before the list is updated."
196
+ else
197
+ puts "queue #{conf[:queue].to_s.dump} already exists."
198
+ end
199
+
200
+ when :delete
201
+ con = SQSRun::Controller.new(conf)
202
+ if con.delete(true)
203
+ puts "Queue #{conf[:queue].to_s.dump} is deleted."
204
+ puts "Note that it takes some seconds before the list is updated."
205
+ else
206
+ puts "Queue #{conf[:queue].to_s.dump} does not exist."
207
+ end
208
+
209
+ when :attr
210
+ con = SQSRun::Controller.new(conf)
211
+ k, v = *attrkv
212
+ if con.set_attribute(k, v)
213
+ puts "#{k}=#{v.to_s.dump}"
214
+ else
215
+ puts "Queue #{conf[:queue].to_s.dump} does not exist."
216
+ end
217
+
171
218
  when :push
172
- pro = SQSRun::Controller.new(conf)
173
- pro.push(message)
219
+ con = SQSRun::Controller.new(conf)
220
+ con.push(message)
174
221
 
175
222
  when :list
176
- pro = SQSRun::Controller.new(conf)
177
- pro.list.each {|name|
178
- puts name
223
+ require 'json'
224
+ format = "%10s %5s %s"
225
+ con = SQSRun::Controller.new(conf)
226
+ list = con.list
227
+ puts format % ['name', 'size', 'attributes']
228
+ list.each {|queue|
229
+ begin
230
+ name = queue.name
231
+ size = queue.size
232
+ queue.get_attribute.each_pair {|k,v|
233
+ puts format % [name, size, "#{k}=#{v.to_s.dump}"]
234
+ name = ''
235
+ size = ''
236
+ }
237
+ rescue RightAws::AwsError
238
+ end
179
239
  }
180
240
 
181
241
  when :exec, :run
@@ -208,10 +268,18 @@ when :exec, :run
208
268
  if type == :run
209
269
  load File.expand_path(conf[:run])
210
270
  run_proc = method(:run)
271
+ if defined? terminate
272
+ kill_proc = method(:terminate)
273
+ else
274
+ kill_proc = Proc.new { }
275
+ end
211
276
  else
212
277
  run_proc = SQSRun::ExecRunner.new(conf[:exec])
278
+ kill_proc = run_proc.method(:terminate)
213
279
  end
214
280
 
215
- worker.run(run_proc)
281
+ worker.init_proc(run_proc, kill_proc)
282
+
283
+ worker.run
216
284
  end
217
285
 
@@ -17,9 +17,36 @@ class Controller
17
17
  @queue.send_message(body)
18
18
  end
19
19
 
20
+ def create
21
+ @sqs = RightAws::SqsGen2.new(@key_id, @secret_key)
22
+ @queue = @sqs.queue(@queue_name, false)
23
+ if @queue
24
+ return nil
25
+ end
26
+ @queue = @sqs.queue(@queue_name, true)
27
+ end
28
+
29
+ def delete(force=false)
30
+ @sqs = RightAws::SqsGen2.new(@key_id, @secret_key)
31
+ @queue = @sqs.queue(@queue_name, false)
32
+ unless @queue
33
+ return nil
34
+ end
35
+ @queue.delete(force)
36
+ end
37
+
20
38
  def list
21
39
  @sqs = RightAws::SqsGen2.new(@key_id, @secret_key)
22
- @sqs.queues.map {|q| q.name }
40
+ @sqs.queues
41
+ end
42
+
43
+ def set_attribute(k, v)
44
+ @sqs = RightAws::SqsGen2.new(@key_id, @secret_key)
45
+ @queue = @sqs.queue(@queue_name, false)
46
+ unless @queue
47
+ return nil
48
+ end
49
+ @queue.set_attribute(k, v)
23
50
  end
24
51
  end
25
52
 
@@ -1,5 +1,5 @@
1
1
  module SQSRun
2
2
 
3
- VERSION = '0.4.0'
3
+ VERSION = '0.5.0'
4
4
 
5
5
  end
data/lib/sqsrun/worker.rb CHANGED
@@ -12,10 +12,11 @@ class Worker
12
12
  @visibility_timeout = conf[:timeout]
13
13
  @extend_timeout = conf[:extend_timeout]
14
14
  @kill_timeout = conf[:kill_timeout]
15
+ @kill_retry = conf[:kill_retry]
15
16
  @interval = conf[:interval]
16
17
  @finished = false
17
18
 
18
- @extender = VisibilityExtender.new(@visibility_timeout, @extend_timeout)
19
+ @extender = TimerThread.new(@visibility_timeout, @extend_timeout, @kill_timeout, @kill_retry)
19
20
  @sqs = RightAws::SqsGen2.new(@key_id, @secret_key)
20
21
  @queue = @sqs.queue(@queue_name)
21
22
 
@@ -23,8 +24,12 @@ class Worker
23
24
  @cond = ConditionVariable.new
24
25
  end
25
26
 
26
- def run(run_proc)
27
+ def init_proc(run_proc, kill_proc)
27
28
  @run_proc = run_proc
29
+ @extender.init_proc(kill_proc)
30
+ end
31
+
32
+ def run
28
33
  @extender.start
29
34
  until @finished
30
35
  msg = @queue.receive(@visibility_timeout)
@@ -79,15 +84,8 @@ class Worker
79
84
 
80
85
  success = false
81
86
  begin
82
- joined = thread.join(@kill_timeout)
83
- if joined
84
- thread.value
85
- success = true
86
- puts "finished id=#{msg.id}"
87
- else
88
- thread.kill
89
- puts "killed id=#{msg.id}"
90
- end
87
+ thread.join
88
+ success = true
91
89
  rescue
92
90
  puts "failed id=#{msg.id}: #{$!}"
93
91
  $!.backtrace.each {|bt|
@@ -104,18 +102,26 @@ class Worker
104
102
  end
105
103
  end
106
104
 
107
- class VisibilityExtender
105
+ class TimerThread
108
106
  include MonitorMixin
109
107
 
110
- def initialize(visibility_timeout, extend_timeout)
108
+ def initialize(visibility_timeout, extend_timeout, kill_timeout, kill_retry)
111
109
  super()
112
110
  @visibility_timeout = visibility_timeout
113
111
  @extend_timeout = extend_timeout
112
+ @kill_timeout = kill_timeout
113
+ @kill_retry = kill_retry
114
+ @kill_time = nil
115
+ @kill_proc = nil
114
116
  @extend_time = nil
115
117
  @message = nil
116
118
  @finished = false
117
119
  end
118
120
 
121
+ def init_proc(kill_proc)
122
+ @kill_proc = kill_proc
123
+ end
124
+
119
125
  def start
120
126
  @thread = Thread.new(&method(:run))
121
127
  end
@@ -126,7 +132,9 @@ class Worker
126
132
 
127
133
  def set_message(msg)
128
134
  synchronize do
129
- @extend_time = Time.now.to_i + @extend_timeout
135
+ now = Time.now.to_i
136
+ @extend_time = now + @extend_timeout
137
+ @kill_time = now + @kill_timeout
130
138
  @message = msg
131
139
  end
132
140
  end
@@ -146,13 +154,16 @@ class Worker
146
154
  until @finished
147
155
  sleep 1
148
156
  synchronize do
149
- try_extend(@message) if @message
157
+ if @message
158
+ now = Time.now.to_i
159
+ try_kill(now, @message)
160
+ try_extend(now, @message)
161
+ end
150
162
  end
151
163
  end
152
164
  end
153
165
 
154
- def try_extend(msg)
155
- now = Time.now.to_i
166
+ def try_extend(now, msg)
156
167
  if now > @extend_time
157
168
  ntime = msg.visibility + @visibility_timeout
158
169
  puts "extending timeout=#{ntime} id=#{msg.id}"
@@ -160,6 +171,16 @@ class Worker
160
171
  @extend_time = now + @extend_timeout
161
172
  end
162
173
  end
174
+
175
+ def try_kill(now, msg)
176
+ if now > @kill_time
177
+ if @kill_proc
178
+ puts "killing #{msg.id}..."
179
+ @kill_proc.call rescue nil
180
+ end
181
+ @kill_time = now + @kill_retry
182
+ end
183
+ end
163
184
  end
164
185
  end
165
186
 
@@ -168,10 +189,13 @@ class ExecRunner
168
189
  def initialize(cmd)
169
190
  @cmd = cmd + ' ' + ARGV.map {|a| Shellwords.escape(a) }.join(' ')
170
191
  @iobuf = ''
192
+ @pid = nil
193
+ @next_kill = :TERM
171
194
  end
172
195
 
173
196
  def call(message)
174
197
  IO.popen(@cmd, "r+") {|io|
198
+ @pid = io.pid
175
199
  io.write(message) rescue nil
176
200
  io.close_write
177
201
  begin
@@ -186,6 +210,11 @@ class ExecRunner
186
210
  raise "Command failed"
187
211
  end
188
212
  end
213
+
214
+ def terminate
215
+ Process.kill(@next_kill, @pid)
216
+ @next_kill = :KILL
217
+ end
189
218
  end
190
219
 
191
220
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqsrun
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
8
+ - 5
9
9
  - 0
10
- version: 0.4.0
10
+ version: 0.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sadayuki Furuhashi
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-12 00:00:00 +09:00
18
+ date: 2011-07-18 00:00:00 +09:00
19
19
  default_executable: sqsrun
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -41,6 +41,7 @@ executables:
41
41
  extensions: []
42
42
 
43
43
  extra_rdoc_files:
44
+ - ChangeLog
44
45
  - README.rdoc
45
46
  files:
46
47
  - bin/sqsrun
@@ -50,6 +51,7 @@ files:
50
51
  - lib/sqsrun/worker.rb
51
52
  - test/exec_test.rb
52
53
  - test/test_helper.rb
54
+ - ChangeLog
53
55
  - README.rdoc
54
56
  - test/cat.sh
55
57
  - test/fail.sh