kongnomal 0.1.3

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.
@@ -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
+