kongnomal 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "optparse"
4
+ require "ostruct"
5
+ require "socket"
6
+ require "kongnomal"
7
+
8
+ options = OpenStruct.new
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = "USAGE: kongnomal [options] job_event_handler_file"
12
+
13
+ opts.on("-c", "--connect-to [beanstalk_address]", "address of beanstalk process to connect to, in form of ip_address:port_number") do |config|
14
+ options.ip_address, options.port_number = config.split(":")
15
+ puts("beanstalk_address #{config} should be in form of ip_address:port_number") || exit unless options.ip_address and options.port_number
16
+ end
17
+
18
+ opts.on_tail("-h", "--help", "Show this message") do
19
+ puts opts
20
+ exit
21
+ end
22
+ end.parse!
23
+ options.job_event_handler = ARGV[0]
24
+
25
+ require File.expand_path(options.job_event_handler) if options.job_event_handler
26
+ unless options.ip_address and options.port_number
27
+ port_number, ip = begin
28
+ (dummy_server = TCPServer.new("127.0.0.1", 0)).addr[1..2]
29
+ ensure
30
+ dummy_server.close
31
+ end
32
+ puts "starting beanstalk server with new beanstalkd process at #{ip}:#{port_number}"
33
+ Kongnomal::BeanstalkServer.new.start(ip, port_number)
34
+ else
35
+ puts "starting beanstalk server with existing beanstalkd process at #{options.ip_address}:#{options.port_number}"
36
+ Kongnomal::BeanstalkServer.new.connect_to(options.ip_address, options.port_number)
37
+ end
@@ -0,0 +1,21 @@
1
+ Kongnomal::Job.bind do |job|
2
+ job.when_touched do |event|
3
+ #do something when worker has given feedback
4
+ end
5
+
6
+ job.when_put do |event|
7
+ #do something when job is put into queue
8
+ end
9
+
10
+ job.when_reserved do |event|
11
+ #do something when job is reserved by a worker
12
+ end
13
+
14
+ job.when_deleted do |event|
15
+ #do something when job is deleted from tube
16
+ end
17
+
18
+ job.when_released do |event|
19
+ #do something when job is released
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "kongnomal"
4
+
5
+ beanstalk_address = ARGV[0]
6
+ tube = ARGV[1]
7
+
8
+ jobs = Kongnomal::JobQueue.new(beanstalk_address, tube)
9
+ until jobs.peek_ready.nil? do
10
+ begin
11
+ job = jobs.reserve(1)
12
+ job.report_back({:state => "start"})
13
+ job.report_back({:state => "in_progress"})
14
+ job.report_back({:state => "success"})
15
+ job.delete
16
+ rescue Exception => e
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "kongnomal"
4
+
5
+ ip_address = ARGV[0]
6
+ port_number = ARGV[1]
7
+
8
+ jobs = Kongnomal::JobQueue.new("#{ip_address}:#{port_number}", "jobs")
9
+
10
+ 300.times do |i|
11
+ @pid = Process.spawn("./worker.rb '#{ip_address}:#{port_number}' 'jobs'")
12
+ Process.detach(@pid)
13
+ end
14
+
15
+ 100000.times do |i|
16
+ jobs.put("job#{i}")
17
+ end
@@ -0,0 +1,7 @@
1
+ require "forwardable"
2
+ require "beanstalk-client"
3
+ require "json"
4
+ require "yaml"
5
+ require File.join(File.dirname(__FILE__), "kongnomal", "beanstalk_server")
6
+ require File.join(File.dirname(__FILE__), "kongnomal", "job_queue")
7
+ require File.join(File.dirname(__FILE__), "kongnomal", "job")
@@ -0,0 +1,74 @@
1
+ module Kongnomal
2
+ class BeanstalkServer
3
+ def initialize(&blk)
4
+ @on_startup = block_given? ? blk : Proc.new { puts "beanstalk server is up and running..." }
5
+ at_exit {stop_beanstalkd}
6
+ self
7
+ end
8
+
9
+ def start(ip, port_number)
10
+ start_beanstalkd(ip, port_number)
11
+ connect_to(ip, port_number)
12
+ end
13
+
14
+ def connect_to(ip, port_number)
15
+ job_event_queue = job_event_queue("#{ip}:#{port_number}", Kongnomal::JobQueue::JOB_EVENT_TUBE)
16
+ @on_startup.call
17
+
18
+ loop do
19
+ job = begin
20
+ job_event_queue.reserve
21
+ rescue Exception => e
22
+ puts "Failed to retrieve job event from #{ip}:#{port_number}[#{Kongnomal::JobQueue::JOB_EVENT_TUBE}]"
23
+ raise e
24
+ end
25
+
26
+ job_evented = JSON.parse(job.body)
27
+ begin
28
+ Kongnomal::Job.send(job_evented["state"]).call(job_evented)
29
+ rescue Exception => e
30
+ job.release(nil, 60)
31
+ puts "Failed to process job event: #{job_evented.inspect}, retrying 60 sec later..."
32
+ puts e.message
33
+ puts e.backtrace
34
+ next
35
+ end
36
+
37
+ begin
38
+ job.delete
39
+ rescue Exception => e
40
+ puts "Failed to delete job event: #{job_evented.inspect} from #{ip}:#{port_number}[#{Kongnomal::JobQueue::JOB_EVENT_TUBE}], ignoring for now to be reprocessed after natural beanstalk job timeout"
41
+ puts e.message
42
+ puts e.backtrace
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+ def start_beanstalkd(ip, port_number)
49
+ stop_beanstalkd
50
+ @pid = Process.spawn("beanstalkd -l #{ip} -p #{port_number}", :unsetenv_others => true, :close_others => true)
51
+ Process.detach(@pid)
52
+ puts "beanstalkd process(#{@pid}) has started at #{ip}:#{port_number}"
53
+ end
54
+
55
+ def stop_beanstalkd
56
+ puts "beanstalkd process(#{@pid}) has been stopped" if @pid and system("kill #{@pid}")
57
+ end
58
+
59
+ def job_event_queue(beanstalk_server_address, tube)
60
+ job_event_queue = 3.times do |i|
61
+ begin
62
+ puts "connecting to #{tube} tube at #{beanstalk_server_address}"
63
+ break Beanstalk::Connection.new(beanstalk_server_address, tube)
64
+ rescue Exception => e
65
+ raise e if i + 1 == 3
66
+ puts "reconnecting in 1 second..."
67
+ sleep(1)
68
+ end
69
+ end
70
+ puts "listening for #{tube} tube at #{beanstalk_server_address}"
71
+ job_event_queue
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,72 @@
1
+ module Kongnomal
2
+ class Job
3
+ PUT = "put".freeze
4
+ RESERVED = "reserved".freeze
5
+ DELETED = "deleted".freeze
6
+ TOUCHED = "touched".freeze
7
+ RELEASED = "released".freeze
8
+
9
+ def self.when_put(&blk)
10
+ @put_handler = blk
11
+ end
12
+
13
+ def self.when_reserved(&blk)
14
+ @reserved_handler = blk
15
+ end
16
+
17
+ def self.when_touched(&blk)
18
+ @touched_handler = blk
19
+ end
20
+
21
+ def self.when_deleted(&blk)
22
+ @deleted_handler = blk
23
+ end
24
+
25
+ def self.when_released(&blk)
26
+ @released_handler = blk
27
+ end
28
+
29
+ class << self
30
+ [PUT, RESERVED, TOUCHED, DELETED, RELEASED].each do |event|
31
+ define_method(event.to_sym) do
32
+ Proc.new do |job|
33
+ event_handler = instance_variable_get("@#{event}_handler")
34
+ event_handler.call(job) if event_handler
35
+ end
36
+ end
37
+ end
38
+
39
+ def bind(&blk)
40
+ yield self
41
+ end
42
+ end
43
+
44
+ extend Forwardable
45
+ def_delegators :@beanstalk_job, :id, :body, :ybody, :put_back, :bury, :stats, :timeouts, :time_left, :age, :state, :delay, :pri, :ttr, :server, :decay, :to_s, :inspect
46
+
47
+ def initialize(event_tube, beanstalk_job, tube)
48
+ @event_tube = event_tube
49
+ @beanstalk_job = beanstalk_job
50
+ @tube = tube
51
+ end
52
+
53
+ def touch(data)
54
+ as_msg = data.respond_to?(:to_hash) ? data.to_hash : data.to_s
55
+ touched = @beanstalk_job.touch
56
+ @event_tube.put({"state" => Kongnomal::Job::TOUCHED, "id" => id, "body" => body, "tube" => @tube, "msg" => as_msg}.to_json) if touched == :ok
57
+ touched
58
+ end
59
+
60
+ def release(*args, &blk)
61
+ released = @beanstalk_job.release(*args, &blk)
62
+ @event_tube.put({"state" => Kongnomal::Job::RELEASED, "id" => id, "body" => body, "tube" => @tube}.to_json) unless released.nil?
63
+ released
64
+ end
65
+
66
+ def delete(*args, &blk)
67
+ deleted = @beanstalk_job.delete(*args, &blk)
68
+ @event_tube.put({"state" => Kongnomal::Job::DELETED, "id" => id, "body" => body, "tube" => @tube}.to_json) unless deleted.nil?
69
+ deleted
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,39 @@
1
+ module Kongnomal
2
+ class JobQueue
3
+ JOB_EVENT_TUBE = "job.events"
4
+
5
+ extend Forwardable
6
+ def_delegators :@job_tube, :peek_job, :peek_ready, :peek_delayed, :peek_buried, :bury, :kick, :stats, :job_stats, :stats_tube, :list_tubes, :list_tube_used, :list_tubes_watched
7
+
8
+ def initialize(beanstalk_server_address, tube = "default")
9
+ @tube = tube
10
+ @job_tube = Beanstalk::Connection.new(beanstalk_server_address, tube)
11
+ @event_tube = Beanstalk::Connection.new(beanstalk_server_address, JOB_EVENT_TUBE)
12
+ end
13
+
14
+ def put(body, pri=65536, delay=0, ttr=120)
15
+ job_id = @job_tube.put(body, pri, delay, ttr)
16
+ @event_tube.put({"state" => Kongnomal::Job::PUT, "tube" => @tube, "id" => job_id, "body" => body}.to_json)
17
+ job_id
18
+ end
19
+
20
+ def yput(obj, pri=65536, delay=0, ttr=120)
21
+ job_id = @job_tube.yput(obj, pri, delay, ttr)
22
+ @event_tube.put({"state" => Kongnomal::Job::PUT, "tube" => @tube, "id" => job_id, "body" => YAML.dump(obj)}.to_json)
23
+ job_id
24
+ end
25
+
26
+ def reserve(timeout=nil)
27
+ beanstalk_job = @job_tube.reserve(timeout)
28
+ @event_tube.put({"state" => Kongnomal::Job::RESERVED, "tube" => @tube, "id" => beanstalk_job.id, "body" => beanstalk_job.body}.to_json)
29
+ Kongnomal::Job.new(@event_tube, beanstalk_job, @tube)
30
+ end
31
+
32
+ def delete(id)
33
+ beanstalk_job = @job_tube.peek_job(id)
34
+ deleted = @job_tube.delete(id)
35
+ @event_tube.put({"state" => Kongnomal::Job::DELETED, "tube" => @tube, "id" => beanstalk_job.id, "body" => beanstalk_job.body}.to_json) if deleted == :ok
36
+ deleted
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kongnomal
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 3
9
+ version: 0.1.3
10
+ platform: ruby
11
+ authors:
12
+ - Jae Lee
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-02-05 00:00:00 +00:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: beanstalk-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 1
31
+ - 0
32
+ version: 1.1.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: agent
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :development
60
+ version_requirements: *id003
61
+ description: Register your custom job event handler that gets notified upon job event triggered from job.report_back or job.delete_then_report_back
62
+ email: jlee@yetitrails.com
63
+ executables:
64
+ - kongnomal
65
+ extensions: []
66
+
67
+ extra_rdoc_files: []
68
+
69
+ files:
70
+ - bin/kongnomal
71
+ - lib/kongnomal.rb
72
+ - lib/kongnomal/beanstalk_server.rb
73
+ - lib/kongnomal/job.rb
74
+ - lib/kongnomal/job_queue.rb
75
+ - example/job_event_handler.rb
76
+ - example/worker.rb
77
+ - example/workers.rb
78
+ has_rdoc: true
79
+ homepage: https://github.com/forward/kongnomal
80
+ licenses: []
81
+
82
+ post_install_message:
83
+ rdoc_options: []
84
+
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirements: []
104
+
105
+ rubyforge_project:
106
+ rubygems_version: 1.3.7
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: beanstalk server wrapper that allows event handler registration on job event queue
110
+ test_files: []
111
+