sad 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source "http://ruby.taobao.org"
3
3
  gem 'jeweler'
4
4
 
5
5
  gem 'activesupport'
6
+ gem 'eventmachine'
6
7
  gem 'em-hiredis'
7
8
  gem 'daemons'
8
- gem "json"
9
+ gem "json"
10
+ gem 'pry'
data/Gemfile.lock CHANGED
@@ -4,9 +4,11 @@ GEM
4
4
  activesupport (3.2.13)
5
5
  i18n (= 0.6.1)
6
6
  multi_json (~> 1.0)
7
+ coderay (1.0.9)
7
8
  daemons (1.1.9)
8
9
  em-hiredis (0.2.1)
9
10
  hiredis (~> 0.4.0)
11
+ eventmachine (1.0.3)
10
12
  git (1.2.5)
11
13
  hiredis (0.4.5)
12
14
  i18n (0.6.1)
@@ -16,10 +18,16 @@ GEM
16
18
  rake
17
19
  rdoc
18
20
  json (1.7.7)
21
+ method_source (0.8.1)
19
22
  multi_json (1.7.2)
23
+ pry (0.9.12)
24
+ coderay (~> 1.0.5)
25
+ method_source (~> 0.8)
26
+ slop (~> 3.4)
20
27
  rake (10.0.4)
21
28
  rdoc (4.0.0)
22
29
  json (~> 1.4)
30
+ slop (3.4.4)
23
31
 
24
32
  PLATFORMS
25
33
  ruby
@@ -28,5 +36,7 @@ DEPENDENCIES
28
36
  activesupport
29
37
  daemons
30
38
  em-hiredis
39
+ eventmachine
31
40
  jeweler
32
41
  json
42
+ pry
data/README.md CHANGED
@@ -41,7 +41,6 @@ EM.run{
41
41
 
42
42
  两个运行环境都要有SadJob这个测试类的定义存在
43
43
 
44
- --------
45
44
 
46
45
  ## 在项目中使用
47
46
 
@@ -90,6 +89,40 @@ Sad::Config.namespace = 'MyBackgroundJobQueue'
90
89
  6181 ?? S 0:02.61 Sad-2
91
90
  6522 s002 R+ 0:00.00 grep Sad
92
91
 
92
+
93
+ ## 测试
94
+
95
+ 启动一个队列
96
+
97
+ ```sh
98
+ bundle exec rake sad:restart COUNT=1 QUEUE=MySadJob DIR=./tmp/pids
99
+ ```
100
+
101
+ 执行最简单的测试
102
+
103
+ ```sh
104
+ bundle exec ruby ./test/test_sad.rb
105
+ ```
106
+
107
+ 执行异常任务测试,异常是指Klass.perform函数执行时出现异常,如果是异步任务,没办法知道异常,因为执行完perform后,代码运行环境已经脱离sad了,需要人工自己处理.
108
+
109
+ 当Klass.perform出错时,会延时重试,重试三次后,如果还有错误,则放弃该任务.
110
+
111
+ ```sh
112
+ bundle exec ruby ./test/test_perform_with_exception.rb
113
+ ```
114
+
115
+ 在异步模式的代码中,异常如果被抛给top level的运行时的话,则代码无法控制,所以有异常的时候一定要捕获到,写在类似errback的回调中,对于Sad的重试,自动重试只能保证perform执行没问题,如果在callback中出错,则需要用户手工将该任务重新enqueue来实现重试的机制。
116
+
117
+ ## 任务配置
118
+
119
+ ```ruby
120
+ SadJob.enqueue(1,2,3,4,5){|payload|
121
+ # delay -> 当sad进程得到该任务时并不立即执行,而是延迟多干秒后执行
122
+ payload.sad_args['delay'] = 5
123
+ }
124
+ ```
125
+
93
126
  ## Contributing to sad
94
127
 
95
128
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
data/Rakefile CHANGED
@@ -43,3 +43,6 @@ Rake::RDocTask.new do |rdoc|
43
43
  rdoc.rdoc_files.include('README*')
44
44
  rdoc.rdoc_files.include('lib/**/*.rb')
45
45
  end
46
+
47
+ require File.join(File.dirname(__FILE__), 'lib', 'sad')
48
+ require File.join(File.dirname(__FILE__), 'lib', 'sad', 'tasks')
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
data/lib/sad/config.rb CHANGED
@@ -9,6 +9,10 @@ module Sad
9
9
  @_namespace || 'SadQueue'
10
10
  end
11
11
 
12
+ def queue(q)
13
+ [Sad::Config.namespace, q].join ':'
14
+ end
15
+
12
16
  def redis=(uri)
13
17
  @_redis_url = uri
14
18
  end
@@ -16,6 +20,22 @@ module Sad
16
20
  def redis
17
21
  @_redis ||= EM::Hiredis.connect(@_redis_url)
18
22
  end
23
+
24
+ def interval=(int)
25
+ @_interval = int.to_i
26
+ end
27
+
28
+ def interval
29
+ @_interval ||= 3
30
+ end
31
+
32
+ def max_retry=(int)
33
+ @_max_retry = int.to_i
34
+ end
35
+
36
+ def max_retry
37
+ @_max_retry ||= 3
38
+ end
19
39
  end
20
40
  end
21
41
  end
data/lib/sad/payload.rb CHANGED
@@ -1,22 +1,55 @@
1
+ require "json"
2
+
1
3
  module Sad
2
4
  class Payload
3
- attr_accessor :klass, :args
5
+ attr_accessor :klass, :args, :sad_args
4
6
 
5
- def initialize(klass, args)
7
+ def initialize(klass, args = [], sad_args = {})
6
8
  @klass = klass
7
9
  @args = args
10
+ @sad_args = {
11
+ 'retry' => 0,
12
+ 'delay' => 0
13
+ }.update(sad_args)
8
14
  end
9
15
 
10
16
  def encode
11
17
  {
12
18
  'klass' => @klass,
13
- 'args' => @args
19
+ 'args' => @args,
20
+ 'sad_args' => @sad_args
14
21
  }.to_json
15
22
  end
16
23
 
24
+ # 执行任务
25
+ # 当执行任务的perform出错时
26
+ # 重试1至::Sad::Config.max_retry次
27
+ # 每次重试时,延迟重试次数*::Sad::Config.interval的时长后,再enqueue
28
+ def perform
29
+ begin
30
+ @klass.constantize.send :perform, *@args
31
+ rescue Exception => e
32
+ if self.sad_args['retry'] and (self.sad_args['retry'].to_i < ::Sad::Config.max_retry)
33
+ self.sad_args['retry'] = self.sad_args['retry'].to_i + 1
34
+ self.sad_args['delay'] = ::Sad::Config.interval * self.sad_args['retry']
35
+ self.enqueue
36
+ else
37
+ STDERR.puts "Payload perform error for #{self.sad_args['retry']} retrys:\n#{self.inspect}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def enqueue
43
+ ::Sad::Config.redis.rpush(self.sad_args['queue'], self.encode)
44
+ end
45
+
17
46
  def self.decode(json)
18
47
  h = JSON.parse(json)
19
- self.new(h['klass'], h['args'])
48
+ if h['sad_args'] or h['sad_args'] != ''
49
+ self.new(h['klass'], h['args'], h['sad_args'])
50
+ else
51
+ self.new(h['klass'], h['args'])
52
+ end
20
53
  end
21
54
  end
22
55
  end
data/lib/sad/server.rb CHANGED
@@ -4,7 +4,7 @@ module Sad
4
4
  def run(queue)
5
5
  @_shutdown = false
6
6
  register_signal
7
- fetch([Sad::Config.namespace, queue].join ':')
7
+ fetch(Sad::Config.queue(queue))
8
8
  end
9
9
 
10
10
  def fetch(queue)
@@ -13,17 +13,34 @@ module Sad
13
13
  if data
14
14
  STDOUT.puts '-'*15 + data.inspect + '-'*15
15
15
  payload = Payload.decode(data)
16
- EM.defer{perform(payload.klass, payload.args)}
16
+ payload_call(payload)
17
17
  end
18
- fetch(queue) unless shutdown?
18
+ fetch_with_interval(queue)
19
19
  }
20
20
  request.errback{
21
+ STDERR.puts 'error with redis request.'
22
+ fetch_with_interval(queue)
23
+ }
24
+ end
25
+
26
+ def fetch_with_interval(queue)
27
+ EM.add_timer(::Sad::Config.interval){
21
28
  fetch(queue) unless shutdown?
22
29
  }
23
30
  end
24
31
 
25
- def perform(klass, args)
26
- klass.constantize.send :perform, *args
32
+ def payload_call(payload)
33
+ # 如果该任务有延时执行要求,
34
+ # 则在定时器执行时将其延时的key删掉,
35
+ # 再重新入队
36
+ if payload.sad_args['delay'] and payload.sad_args['delay'] != '' and payload.sad_args['delay'] != 0
37
+ EM.add_timer(payload.sad_args['delay'].to_i){
38
+ payload.sad_args.delete('delay')
39
+ payload.enqueue
40
+ }
41
+ else
42
+ payload.perform
43
+ end
27
44
  end
28
45
 
29
46
  def register_signal
@@ -43,6 +60,7 @@ module Sad
43
60
 
44
61
  def shutdown
45
62
  @_shutdown = true
63
+ EM.stop
46
64
  end
47
65
  end
48
66
  end
data/lib/sad/worker.rb CHANGED
@@ -6,12 +6,14 @@ module Sad
6
6
  else
7
7
  nil
8
8
  end
9
- [Sad::Config.namespace, name].join ':'
9
+ Sad::Config.queue(name)
10
10
  end
11
11
 
12
12
  def enqueue(*args)
13
13
  payload = ::Sad::Payload.new(self.to_s, args)
14
- ::Sad::Config.redis.rpush(queue_name, payload.encode)
14
+ payload.sad_args['queue'] = queue_name
15
+ yield payload if block_given?
16
+ payload.enqueue
15
17
  end
16
18
  end
17
19
  end
data/sad.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "sad"
8
- s.version = "1.3.0"
8
+ s.version = "1.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["\u{5d14}\u{5ce5}"]
12
- s.date = "2013-04-26"
12
+ s.date = "2013-04-27"
13
13
  s.description = "a simple em baseed background job worker."
14
14
  s.email = "zheng.cuizh@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
34
34
  "lib/tasks/sad.rake",
35
35
  "sad.gemspec",
36
36
  "test/helper.rb",
37
+ "test/test_perform_with_exception.rb",
37
38
  "test/test_sad.rb"
38
39
  ]
39
40
  s.homepage = "http://github.com/charlescui/sad"
@@ -48,22 +49,28 @@ Gem::Specification.new do |s|
48
49
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
50
  s.add_runtime_dependency(%q<jeweler>, [">= 0"])
50
51
  s.add_runtime_dependency(%q<activesupport>, [">= 0"])
52
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
51
53
  s.add_runtime_dependency(%q<em-hiredis>, [">= 0"])
52
54
  s.add_runtime_dependency(%q<daemons>, [">= 0"])
53
55
  s.add_runtime_dependency(%q<json>, [">= 0"])
56
+ s.add_runtime_dependency(%q<pry>, [">= 0"])
54
57
  else
55
58
  s.add_dependency(%q<jeweler>, [">= 0"])
56
59
  s.add_dependency(%q<activesupport>, [">= 0"])
60
+ s.add_dependency(%q<eventmachine>, [">= 0"])
57
61
  s.add_dependency(%q<em-hiredis>, [">= 0"])
58
62
  s.add_dependency(%q<daemons>, [">= 0"])
59
63
  s.add_dependency(%q<json>, [">= 0"])
64
+ s.add_dependency(%q<pry>, [">= 0"])
60
65
  end
61
66
  else
62
67
  s.add_dependency(%q<jeweler>, [">= 0"])
63
68
  s.add_dependency(%q<activesupport>, [">= 0"])
69
+ s.add_dependency(%q<eventmachine>, [">= 0"])
64
70
  s.add_dependency(%q<em-hiredis>, [">= 0"])
65
71
  s.add_dependency(%q<daemons>, [">= 0"])
66
72
  s.add_dependency(%q<json>, [">= 0"])
73
+ s.add_dependency(%q<pry>, [">= 0"])
67
74
  end
68
75
  end
69
76
 
@@ -0,0 +1,20 @@
1
+ $:.unshift(File.dirname __FILE__)
2
+ require 'helper'
3
+
4
+ class SadJob
5
+ extend ::Sad::Worker
6
+
7
+ def self.queue
8
+ 'MySadJob'
9
+ end
10
+
11
+ def self.perform(*args)
12
+ raise RuntimeError, 'Error for test!!!!'
13
+ end
14
+ end
15
+
16
+ EM.run {
17
+ EM::PeriodicTimer.new(3){
18
+ SadJob.enqueue('this is some args', {:hello => 'code'})
19
+ }
20
+ }
data/test/test_sad.rb CHANGED
@@ -1,3 +1,4 @@
1
+ $:.unshift(File.dirname __FILE__)
1
2
  require 'helper'
2
3
 
3
4
  class SadJob
@@ -13,6 +14,8 @@ class SadJob
13
14
  end
14
15
  end
15
16
 
16
- EM::PeriodicTimer.new(3){
17
- SadJob.enqueue('this is some args', {:hello => 'code'})
18
- }
17
+ EM.run{
18
+ EM::PeriodicTimer.new(3){
19
+ SadJob.enqueue('this is some args', {:hello => 'code'})
20
+ }
21
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sad
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-26 00:00:00.000000000 Z
12
+ date: 2013-04-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: jeweler
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: eventmachine
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
46
62
  - !ruby/object:Gem::Dependency
47
63
  name: em-hiredis
48
64
  requirement: !ruby/object:Gem::Requirement
@@ -91,6 +107,22 @@ dependencies:
91
107
  - - ! '>='
92
108
  - !ruby/object:Gem::Version
93
109
  version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
94
126
  description: a simple em baseed background job worker.
95
127
  email: zheng.cuizh@gmail.com
96
128
  executables: []
@@ -116,6 +148,7 @@ files:
116
148
  - lib/tasks/sad.rake
117
149
  - sad.gemspec
118
150
  - test/helper.rb
151
+ - test/test_perform_with_exception.rb
119
152
  - test/test_sad.rb
120
153
  homepage: http://github.com/charlescui/sad
121
154
  licenses:
@@ -132,7 +165,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
165
  version: '0'
133
166
  segments:
134
167
  - 0
135
- hash: 3515885531508128638
168
+ hash: -1386435798103601784
136
169
  required_rubygems_version: !ruby/object:Gem::Requirement
137
170
  none: false
138
171
  requirements: