pgq 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ namespace :pgq do
2
+
3
+ desc "Start worker params: QUEUES, LOGGER, WATCH_FILE"
4
+ task :worker => [:environment] do
5
+ queues = ENV['QUEUES']
6
+ logger_file = ENV['LOGGER'] || Rails.root.join("log/pgq_#{queues}.log")
7
+ watch_file = ENV['WATCH_FILE'] || Rails.root.join("tmp/pgq_#{queues}.stop")
8
+ logger = Logger.new(logger_file)
9
+ w = Pgq::Worker.new(:logger => logger, :queues => queues, :watch_file => watch_file)
10
+ w.run
11
+ end
12
+
13
+ desc "Install PgQ to database RAILS_ENV"
14
+ task :install do
15
+ Dir["#{Rails.root}/config/pgq_#{Rails.env}*.ini"].each do |conf|
16
+ ENV['PGQFILE']=conf
17
+ Rake::Task['pgq:install_from_file'].invoke
18
+ end
19
+ end
20
+
21
+ desc "Install PgQ from file ENV['PGQFILE'] to database RAILS_ENV"
22
+ task :install_from_file do
23
+ puts "No file specified" and return if !ENV['PGQFILE'] || ENV['PGQFILE'].empty?
24
+
25
+ conf = ENV['PGQFILE']
26
+
27
+ puts "installing pgq, running: pgqadm.py #{conf} install"
28
+
29
+ output = `which pgqadm.py && pgqadm.py #{conf} install 2>&1 || which pgqadm && pgqadm #{conf} install 2>&1`
30
+ puts output
31
+ if output =~ /pgq is installed/ || output =~ /Reading from.*?pgq.sql$/
32
+ puts "PgQ installed successfully"
33
+ else
34
+ raise "Something went wrong(see above)... Check that you install skytools package and create #{conf}"
35
+ end
36
+ end
37
+
38
+
39
+ namespace :ticker do
40
+
41
+ desc "Start PgQ ticker daemon"
42
+ task :start do
43
+ conf = Rails.root.join("config/pgq_#{Rails.env}.ini")
44
+ output = `which pgqadm.py && pgqadm.py #{conf} -d ticker 2>&1 || which pgqadm && pgqadm #{conf} -d ticker 2>&1`
45
+ if output.empty?
46
+ puts "ticker daemon started"
47
+ else
48
+ puts output
49
+ end
50
+ end
51
+
52
+ desc "Stop PgQ ticker daemon"
53
+ task :stop do
54
+ conf = Rails.root.join("config/pgq_#{Rails.env}.ini")
55
+ output = `which pgqadm.py && pgqadm.py #{conf} -s 2>&1 || which pgqadm && pgqadm #{conf} -s 2>&1`
56
+ if output.empty?
57
+ puts "ticker daemon stoped"
58
+ else
59
+ puts output
60
+ end
61
+ end
62
+
63
+ end
64
+ end
data/pgq.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.dirname(__FILE__) + "/lib/pgq/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{pgq}
7
+ s.version = Pgq::VERSION
8
+
9
+ s.authors = ["Makarchev Konstantin"]
10
+ s.autorequire = %q{init}
11
+
12
+ s.description = %q{Queues system for AR/Rails based on PgQ Skytools for PostgreSQL, like Resque on Redis. Rails 3! only tested.}
13
+ s.summary = %q{Queues system for AR/Rails based on PgQ Skytools for PostgreSQL, like Resque on Redis. Rails 3! only tested.}
14
+
15
+ s.email = %q{kostya27@gmail.com}
16
+ s.homepage = %q{http://github.com/kostya/pgq}
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency 'activesupport', ">=2.3.2"
24
+ s.add_dependency 'activerecord', ">=2.3.2"
25
+
26
+ s.add_development_dependency "rspec"
27
+ s.add_development_dependency "rake"
28
+ end
@@ -0,0 +1,149 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class PgqTata < Pgq::ConsumerBase
4
+ end
5
+
6
+ class PgqTata2 < Pgq::ConsumerBase
7
+ @queue_name = "huhu"
8
+ end
9
+
10
+ class PgqTata3 < Pgq::ConsumerBase
11
+ set_queue_name 'hehu'
12
+
13
+ def self.next_queue_name
14
+ 'tutturu'
15
+ end
16
+
17
+ def perform(name, a, b)
18
+ $a = a
19
+ $b = b
20
+ 10
21
+ end
22
+
23
+ end
24
+
25
+ describe Pgq::ConsumerBase do
26
+ before :each do
27
+ @consumer = PgqTata.new
28
+ @consumer2 = PgqTata2.new
29
+ @consumer3 = PgqTata3.new
30
+
31
+ @coder = @consumer.coder
32
+ end
33
+
34
+ it "queue_name" do
35
+ PgqTata.queue_name.should == 'tata'
36
+ @consumer.queue_name.should == 'tata'
37
+
38
+ PgqTata2.queue_name.should == 'huhu'
39
+ @consumer2.queue_name.should == 'huhu'
40
+
41
+ PgqTata3.queue_name.should == 'hehu'
42
+ @consumer3.queue_name.should == 'hehu'
43
+ end
44
+
45
+ it "default encode/decode" do
46
+ data = {:a => [1, 2, 3, {:b => 'c'}]}
47
+ s = @coder.dump(data)
48
+ @coder.load(s).should == data
49
+ end
50
+
51
+ it "enqueue all cases" do
52
+ PgqTata.database.should_receive(:pgq_insert_event).with('tata', 'method1', @coder.dump([1,2,3]))
53
+ PgqTata.enqueue(:method1, 1, 2, 3)
54
+
55
+ PgqTata2.database.should_receive(:pgq_insert_event).with('huhu', 'method1', @coder.dump([[1,2,3]]))
56
+ PgqTata2.enqueue(:method1, [1, 2, 3])
57
+
58
+ PgqTata3.database.should_receive(:pgq_insert_event).with('tutturu', 'method1', @coder.dump([]))
59
+ PgqTata3.enqueue(:method1)
60
+ end
61
+
62
+
63
+ describe "consuming" do
64
+ before :each do
65
+ @data = [1, 2, 3]
66
+ @event = {'ev_type' => 'bla', 'ev_data' => @coder.dump(@data)}
67
+ end
68
+
69
+ it "perform_batch should_receive perform_events" do
70
+ @consumer.should_receive(:get_batch_events).and_return([@event])
71
+ @consumer.should_not_receive(:all_events_failed)
72
+ @consumer.should_receive(:perform_events) do |events|
73
+ events.size.should == 1
74
+ ev = events.first
75
+ ev.type.should == 'bla'
76
+ ev.data.should == @data
77
+ end
78
+ @consumer.should_receive(:finish_batch).with(1)
79
+ @consumer.perform_batch.should == 1
80
+ end
81
+
82
+ it "perform_batch raises" do
83
+ @consumer.should_receive(:get_batch_events).and_return([@event])
84
+ @consumer.should_receive(:perform_events).and_raise(:wow)
85
+ @consumer.should_receive(:all_events_failed)
86
+ @consumer.should_receive(:finish_batch).with(1)
87
+ @consumer.perform_batch.should == 1
88
+ end
89
+
90
+ it "perform_batch empty" do
91
+ @consumer.should_receive(:get_batch_events).and_return([])
92
+ @consumer.should_not_receive(:all_events_failed)
93
+ @consumer.should_not_receive(:perform_events)
94
+ @consumer.should_receive(:finish_batch).with(0)
95
+ @consumer.perform_batch.should == 0
96
+ end
97
+
98
+ end
99
+
100
+ describe "actions with events" do
101
+ before :each do
102
+ @data = [1, 2, 3]
103
+ @event = Pgq::Event.new(@consumer, {'ev_type' => 'bla', 'ev_data' => @coder.dump(@data)})
104
+ @events = [@event]
105
+ end
106
+
107
+ it "all_events_failed" do
108
+ @event.should_receive(:failed!).with(an_instance_of(String))
109
+ @consumer.all_events_failed(@events, Exception.new('wow'))
110
+ end
111
+
112
+ it "perform_events" do
113
+ @consumer.should_receive(:perform_event).with(@event)
114
+ @consumer.perform_events(@events)
115
+ end
116
+
117
+ it "perform_event ok" do
118
+ @consumer.should_receive(:perform).with('bla', *@data)
119
+ @consumer.perform_event(@event)
120
+ end
121
+
122
+ it "perform_event raised" do
123
+ @consumer.should_receive(:perform).with('bla', *@data).and_throw(:wow)
124
+ @event.should_receive(:failed!).with(an_instance_of(String))
125
+ @consumer.perform_event(@event)
126
+ end
127
+
128
+ end
129
+
130
+ describe "migration" do
131
+ it "up" do
132
+ Pgq::ConsumerBase.database.should_receive(:pgq_add_queue).with('super', Pgq::ConsumerBase.consumer_name)
133
+ Pgq::ConsumerBase.add_queue("super")
134
+ end
135
+
136
+ it "down" do
137
+ Pgq::ConsumerBase.database.should_receive(:pgq_remove_queue).with('super', Pgq::ConsumerBase.consumer_name)
138
+ Pgq::ConsumerBase.remove_queue("super")
139
+ end
140
+ end
141
+
142
+ it "should proxy consumer" do
143
+ PgqTata3.proxy(:ptest)
144
+ PgqTata3.ptest(111, 'abc').should == 10
145
+ $a.should == 111
146
+ $b.should == 'abc'
147
+ end
148
+
149
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class PgqGr < Pgq::ConsumerGroup
4
+ def perform_group opts; end
5
+ def hahah; end
6
+ def bla; end
7
+ end
8
+
9
+ describe Pgq::ConsumerGroup do
10
+ before :each do
11
+ @consumer = PgqGr.new
12
+ @consumer.stub!(:get_next_batch).and_return ''
13
+ @consumer.stub!(:finish_batch).and_return ''
14
+ @coder = @consumer.coder
15
+ end
16
+
17
+ it "queue_name" do
18
+ PgqGr.queue_name.should == 'gr'
19
+ end
20
+
21
+ it "should call consume" do
22
+ @consumer.should_receive(:perform_group) do |h|
23
+ ev1 = h['hahah']
24
+ ev1.size.should == 2
25
+ ev1.first.data.should == '1'
26
+ ev1.second.data.should == '3'
27
+
28
+ ev2 = h['bla']
29
+ ev2.size.should == 1
30
+ ev2.first.data.should == '2'
31
+ end
32
+ @consumer.should_receive(:get_batch_events).and_return([{'ev_type' => 'hahah', 'ev_data' => @coder.dump('1')},
33
+ {'ev_type' => 'bla', 'ev_data' => @coder.dump('2')}, {'ev_type' => 'hahah', 'ev_data' => @coder.dump('3')}])
34
+ @consumer.perform_batch
35
+ end
36
+
37
+ end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class PgqHaha < Pgq::Consumer
4
+
5
+ def ptest2(a, b)
6
+ $a = a
7
+ $b = b
8
+ 10
9
+ end
10
+
11
+ end
12
+
13
+ describe PgqHaha do
14
+ before :each do
15
+ @consumer = PgqHaha.new
16
+ @coder = @consumer.coder
17
+ @data = [1,2,[3,4], nil]
18
+
19
+ @consumer.stub!(:get_next_batch).and_return ''
20
+ @consumer.stub!(:finish_batch).and_return ''
21
+ end
22
+
23
+ it "queue name" do
24
+ @consumer.queue_name.should == 'haha'
25
+ end
26
+
27
+ it "missing method should insert event" do
28
+ PgqHaha.should_receive(:enqueue).with(:bla, 1,2,[3,4], nil)
29
+ PgqHaha.bla 1, 2, [3, 4], nil
30
+ end
31
+
32
+ it "should enqueue with add_event" do
33
+ PgqHaha.should_receive(:enqueue).with(:bla, 1,2,[3,4], nil)
34
+ PgqHaha.add_event :bla, 1, 2, [3, 4], nil
35
+ end
36
+
37
+ it "magick perform" do
38
+ @consumer.should_receive(:bla) do |*h|
39
+ h.should == @data
40
+ end
41
+
42
+ @consumer.should_receive(:get_batch_events).and_return([{'ev_type' => 'bla', 'ev_data' => @coder.dump(@data)}])
43
+ @consumer.perform_batch
44
+ end
45
+
46
+ it "should proxy consumer" do
47
+ PgqHaha.proxy(:ptest2)
48
+ PgqHaha.ptest2(111, 'abc').should == 10
49
+ $a.should == 111
50
+ $b.should == 'abc'
51
+ end
52
+
53
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Pgq::Event do
4
+ before :each do
5
+ kl = Class.new(Pgq::ConsumerGroup) do
6
+ end
7
+ @consumer = kl.new
8
+ @consumer.stub!(:get_next_batch).and_return ''
9
+ @consumer.stub!(:finish_batch).and_return ''
10
+ @coder = @consumer.coder
11
+
12
+ @ev = Pgq::Event.new(@consumer, {'ev_type' => 'haha', 'ev_data' => @coder.dump('aaaaaaa'), 'ev_id' => 123})
13
+ end
14
+
15
+ it "parse data" do
16
+ @ev.type.should == 'haha'
17
+ @ev.data.should == 'aaaaaaa'
18
+ @ev.id.should == 123
19
+ @ev.consumer.should == @consumer
20
+ end
21
+
22
+ it "should failed!" do
23
+ @consumer.should_receive(:event_failed).with(123, an_instance_of(String))
24
+ @ev.failed!
25
+ end
26
+
27
+ it "should retry!" do
28
+ @consumer.should_receive(:event_retry).with(123)
29
+ @ev.retry!
30
+ end
31
+
32
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Pgq::Marshal64Coder do
4
+ before :each do
5
+ @coder = Pgq::Marshal64Coder
6
+ end
7
+
8
+ it "work" do
9
+ data = {:a => [1, 2, 3, {:b => 'c'}]}
10
+ s = @coder.dump(data)
11
+ @coder.load(s).should == data
12
+ end
13
+
14
+ it "work on large data" do
15
+ data = {}
16
+ 10000.times{|i| data["#{i}".to_sym] = [i, "i_#{i}"]}
17
+
18
+ s = @coder.dump(data)
19
+ @coder.load(s).should == data
20
+ end
21
+
22
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require "bundler/setup"
3
+
4
+ Bundler.require
5
+
6
+ class Pgq::Consumer
7
+ # rspec fuckup
8
+ def self.to_ary
9
+ end
10
+ end
11
+
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Pgq::Worker do
4
+ class PgqBla < Pgq::Consumer
5
+ end
6
+
7
+ it "should find class not in hash" do
8
+ Pgq::Worker.predict_queue_class('bla').should == PgqBla
9
+ Pgq::Worker.predict_queue_class('bla_1').should == PgqBla
10
+ Pgq::Worker.predict_queue_class('bla_2').should == PgqBla
11
+ Pgq::Worker.predict_queue_class('bla3').should == PgqBla
12
+ Pgq::Worker.predict_queue_class('bla_').should == PgqBla
13
+ Pgq::Worker.predict_queue_class('pgq_bla_22').should == PgqBla
14
+
15
+ Pgq::Worker.predict_queue_class('blah').should == nil
16
+ Pgq::Worker.predict_queue_class('').should == nil
17
+ Pgq::Worker.predict_queue_class(nil).should == nil
18
+ end
19
+
20
+ def process_batch
21
+ processed_count = 0
22
+
23
+ @consumers.each do |consumer|
24
+ processed_count += consumer.perform_batch
25
+
26
+ if @watch_file && File.exists?(@watch_file)
27
+ logger.info "Found file #{@watch_file}, now exiting"
28
+ File.unlink(@watch_file)
29
+ return
30
+ end
31
+ end
32
+
33
+ processed_count
34
+ end
35
+
36
+ it "initialize" do
37
+ @w = Pgq::Worker.new :queues => ['bla']
38
+
39
+ @w.consumers.size.should == 1
40
+ cons = @w.consumers.first
41
+ cons.class.should == PgqBla
42
+ cons.queue_name.should == 'bla'
43
+ end
44
+
45
+ it "process_batch" do
46
+ @w = Pgq::Worker.new :queues => ['bla']
47
+ cons = @w.consumers.first
48
+
49
+ cons.should_receive(:perform_batch).and_return(10)
50
+ @w.process_batch.should == 10
51
+ end
52
+
53
+ end