sqsrun 0.4.0 → 0.5.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 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