dmantilla-delayed_job_sqs 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Daniel Mantilla
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
+ NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,6 @@
1
+ h1. Delayed Job for Amazon Simple Queue
2
+
3
+ h3. Versions
4
+
5
+ * 0.1.0: Initial release, based on delayed_job version 1.7.0
6
+ * 0.1.1, 0.1.3: Minor changes here and there
@@ -0,0 +1,28 @@
1
+ #version = File.read('README.textile').scan(/^\*\s+([\d\.]+)/).flatten
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "delayed_job_sqs"
5
+ s.version = "0.1.4"
6
+ s.date = "2009-07-17"
7
+ s.summary = "Asynchronous queue execution using Amazon SQS -- Most of the code was extracted from the delayed_job gem"
8
+ s.email = "daniel@celect.org"
9
+ s.homepage = "http://github.com/dmantilla/delayed_job_sqs/tree/master"
10
+ s.description = "delayed_job_sqs reuses most of the code from the delayed_job gem by Tobias Lütke. ActiveRecord was replaced by Amazon Simple Queue and other minor changes"
11
+ s.authors = ["Daniel Mantilla"]
12
+
13
+ s.has_rdoc = false
14
+ s.rdoc_options = ["--main", "README.textile"]
15
+ s.extra_rdoc_files = ["README.textile"]
16
+
17
+ # run git ls-files to get an updated list
18
+ s.files = %w[
19
+ MIT-LICENSE
20
+ README.textile
21
+ delayed_job_sqs.gemspec
22
+ init.rb
23
+ lib/delayed/job.rb
24
+ lib/delayed/message_sending.rb
25
+ lib/delayed/performable_method.rb
26
+ lib/delayed_job_sqs.rb
27
+ ]
28
+ end
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'right_aws'
2
+ require File.dirname(__FILE__) + '/lib/delayed_job_sqs'
3
+
@@ -0,0 +1,137 @@
1
+ module Delayed
2
+
3
+ class DeserializationError < StandardError
4
+ end
5
+
6
+ class Job
7
+ MAX_RUN_TIME = 4.hours
8
+
9
+ ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/
10
+
11
+ def initialize(message)
12
+ @message = message
13
+ @logger = defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT)
14
+ end
15
+
16
+ def payload_object
17
+ @payload_object ||= deserialize(@message.to_s)
18
+ end
19
+
20
+ def name
21
+ @name ||= begin
22
+ payload = payload_object
23
+ if payload.respond_to?(:display_name)
24
+ payload.display_name
25
+ else
26
+ payload.class.name
27
+ end
28
+ end
29
+ end
30
+
31
+ def run(max_run_time = MAX_RUN_TIME)
32
+ begin
33
+ runtime = Benchmark.realtime do
34
+ invoke_job # TODO: raise error if takes longer than max_run_time
35
+ end
36
+ # TODO : warn if runtime > max_run_time
37
+ @logger.info "* [JOB] #{name} completed after %.4f" % runtime
38
+ # The message is finally delete from the queue
39
+ @message.delete
40
+ true
41
+ rescue Exception => e
42
+ log_error(e)
43
+ false # work failed
44
+ end
45
+
46
+ end
47
+
48
+ # This is a good hook if you need to report job processing errors in additional or different ways
49
+ def log_error(error)
50
+ @logger.error "* [JOB] #{name} failed with #{error.class.name}: #{error.message}"
51
+ @logger.error error.backtrace.inspect
52
+ end
53
+
54
+ # Moved into its own method so that new_relic can trace it.
55
+ def invoke_job
56
+ payload_object.perform
57
+ end
58
+
59
+ # Add a job to the queue
60
+ def self.enqueue(*args, &block)
61
+ sqs_queue = args.shift
62
+ raise ArgumentError, 'SQS Queue was not provided' unless sqs_queue.is_a? RightAws::SqsGen2::Queue
63
+
64
+ object = block_given? ? EvaledJob.new(&block) : args.shift
65
+
66
+ unless object.respond_to?(:perform) || block_given?
67
+ raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
68
+ end
69
+
70
+ sqs_queue.send_message object.to_yaml
71
+ end
72
+
73
+ # Do num jobs and return stats on success/failure.
74
+ # Exit early if interrupted.
75
+ def self.work_off(sqs_queue)
76
+ exit_flag = false
77
+ until($exit)
78
+ success, failure = 0, 0
79
+
80
+ while(message = sqs_queue.receive)
81
+ if message.to_s == 'stop'
82
+ exit_flag = true
83
+ break
84
+ end
85
+
86
+ case Job.new(message).run
87
+ when true
88
+ success += 1
89
+ when false
90
+ failure += 1
91
+ else
92
+ break # leave if no work could be done
93
+ end
94
+ break if $exit # leave if we're exiting
95
+ end
96
+
97
+ break if exit_flag
98
+
99
+ puts "Success: #{success}, Failures: #{failure}"
100
+ sleep(60)
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def deserialize(source)
107
+ handler = YAML.load(source) rescue nil
108
+
109
+ unless handler.respond_to?(:perform)
110
+ if handler.nil? && source =~ ParseObjectFromYaml
111
+ handler_class = $1
112
+ end
113
+ attempt_to_load(handler_class || handler.class)
114
+ handler = YAML.load(source)
115
+ end
116
+
117
+ return handler if handler.respond_to?(:perform)
118
+
119
+ raise DeserializationError,
120
+ 'Job failed to load: Unknown handler. Try to manually require the appropiate file.'
121
+ rescue TypeError, LoadError, NameError => e
122
+ raise DeserializationError,
123
+ "Job failed to load: #{e.message}. Try to manually require the required file."
124
+ end
125
+
126
+ end
127
+
128
+ class EvaledJob
129
+ def initialize
130
+ @job = yield
131
+ end
132
+
133
+ def perform
134
+ eval(@job)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,25 @@
1
+ module Delayed
2
+ module MessageSending
3
+ def send_later(sqs_queue, method, *args)
4
+ delayed_method = Delayed::PerformableMethod.new(self, method.to_sym, args)
5
+
6
+ # If an actual queue was provided and the message size is less than 8K, it's appended to the queue
7
+ if sqs_queue.is_a?(RightAws::SqsGen2::Queue) && delayed_method.to_yaml.size > 8192
8
+ Delayed::Job.enqueue(sqs_queue, delayed_method)
9
+ else
10
+ # else the method is executed, no queueing
11
+ self.send method.to_sym, *args #.map{|a| a}
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ def handle_asynchronously(method)
17
+ without_name = "#{method}_without_send_later"
18
+ define_method("#{method}_with_send_later") do |*args|
19
+ send_later(without_name, *args)
20
+ end
21
+ alias_method_chain method, :send_later
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,55 @@
1
+ module Delayed
2
+ class PerformableMethod < Struct.new(:object, :method, :args)
3
+ CLASS_STRING_FORMAT = /^CLASS\:([A-Z][\w\:]+)$/
4
+ AR_STRING_FORMAT = /^AR\:([A-Z][\w\:]+)\:(\d+)$/
5
+
6
+ def initialize(object, method, args)
7
+ raise NoMethodError, "undefined method `#{method}' for #{self.inspect}" unless object.respond_to?(method)
8
+
9
+ self.object = dump(object)
10
+ self.args = args.map { |a| dump(a) }
11
+ self.method = method.to_sym
12
+ end
13
+
14
+ def display_name
15
+ case self.object
16
+ when CLASS_STRING_FORMAT then "#{$1}.#{method}"
17
+ when AR_STRING_FORMAT then "#{$1}##{method}"
18
+ else "Unknown##{method}"
19
+ end
20
+ end
21
+
22
+ def perform
23
+ load(object).send(method, *args.map{|a| load(a)})
24
+ #rescue ActiveRecord::RecordNotFound
25
+ # We cannot do anything about objects which were deleted in the meantime
26
+ true
27
+ end
28
+
29
+ private
30
+
31
+ def load(arg)
32
+ case arg
33
+ when CLASS_STRING_FORMAT then $1.constantize
34
+ when AR_STRING_FORMAT then $1.constantize.find($2)
35
+ else arg
36
+ end
37
+ end
38
+
39
+ def dump(arg)
40
+ case arg
41
+ when Class then class_to_string(arg)
42
+ when ActiveRecord::Base then ar_to_string(arg)
43
+ else arg
44
+ end
45
+ end
46
+
47
+ def ar_to_string(obj)
48
+ "AR:#{obj.class}:#{obj.id}"
49
+ end
50
+
51
+ def class_to_string(obj)
52
+ "CLASS:#{obj.name}"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/delayed/message_sending'
2
+ require File.dirname(__FILE__) + '/delayed/performable_method'
3
+ require File.dirname(__FILE__) + '/delayed/job'
4
+
5
+ Object.send(:include, Delayed::MessageSending)
6
+ Module.send(:include, Delayed::MessageSending::ClassMethods)
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dmantilla-delayed_job_sqs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Mantilla
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-17 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: "delayed_job_sqs reuses most of the code from the delayed_job gem by Tobias L\xC3\xBCtke. ActiveRecord was replaced by Amazon Simple Queue and other minor changes"
17
+ email: daniel@celect.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.textile
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.textile
27
+ - delayed_job_sqs.gemspec
28
+ - init.rb
29
+ - lib/delayed/job.rb
30
+ - lib/delayed/message_sending.rb
31
+ - lib/delayed/performable_method.rb
32
+ - lib/delayed_job_sqs.rb
33
+ has_rdoc: false
34
+ homepage: http://github.com/dmantilla/delayed_job_sqs/tree/master
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --main
38
+ - README.textile
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.2.0
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: Asynchronous queue execution using Amazon SQS -- Most of the code was extracted from the delayed_job gem
60
+ test_files: []
61
+