pgq 0.1

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,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