queue_classic 0.3.3.pre → 0.3.5.pre

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.
data/lib/queue_classic.rb CHANGED
@@ -1,5 +1,7 @@
1
- require 'json'
2
1
  require 'pg'
2
+
3
+ require 'logger'
4
+ require 'json'
3
5
  require 'uri'
4
6
 
5
7
  $: << File.expand_path(__FILE__, 'lib')
@@ -7,11 +9,13 @@ $: << File.expand_path(__FILE__, 'lib')
7
9
  require 'queue_classic/durable_array'
8
10
  require 'queue_classic/database'
9
11
  require 'queue_classic/worker'
12
+ require 'queue_classic/logger'
10
13
  require 'queue_classic/queue'
11
14
  require 'queue_classic/job'
12
15
 
13
16
  module QC
14
- VERBOSE = ENV["VERBOSE"] == true
17
+ VERBOSE = ENV["VERBOSE"] || ENV["QC_VERBOSE"]
18
+ Logger.puts("Logging enabled")
15
19
 
16
20
  def self.method_missing(sym, *args, &block)
17
21
  Queue.send(sym, *args, &block)
@@ -1,5 +1,6 @@
1
1
  module QC
2
2
  class Database
3
+
3
4
  @@connection = nil
4
5
 
5
6
  DATABASE_URL = (ENV["QC_DATABASE_URL"] || ENV["DATABASE_URL"])
@@ -7,53 +8,52 @@ module QC
7
8
  NOTIFY_TIMEOUT = (ENV["QC_NOTIFY_TIMEOUT"] || 10).to_i
8
9
  DEFAULT_QUEUE_NAME = "queue_classic_jobs"
9
10
 
10
- def self.create_queue(name)
11
- db = new(name)
12
- db.create_table
13
- db.disconnect
14
- true
15
- end
16
-
17
11
  attr_reader :table_name
18
12
 
19
13
  def initialize(queue_name=nil)
14
+ log("initialized")
15
+
20
16
  @top_boundry = MAX_TOP_BOUND
17
+ log("top_boundry=#{@top_boundry}")
18
+
21
19
  @table_name = queue_name || DEFAULT_QUEUE_NAME
20
+ log("table_name=#{@table_name}")
21
+
22
22
  @db_params = URI.parse(DATABASE_URL)
23
+ log("uri=#{DATABASE_URL}")
23
24
  end
24
25
 
25
- def init_db
26
- drop_table
27
- create_table
28
- load_functions
26
+ def set_application_name
27
+ execute("SET application_name = 'queue_classic'")
29
28
  end
30
29
 
31
30
  def listen
31
+ log("LISTEN")
32
32
  execute("LISTEN queue_classic_jobs")
33
33
  end
34
34
 
35
35
  def unlisten
36
+ log("UNLISTEN")
36
37
  execute("UNLISTEN queue_classic_jobs")
37
38
  end
38
39
 
39
40
  def wait_for_notify
41
+ log("waiting for notify timeout=#{NOTIFY_TIMEOUT}")
40
42
  connection.wait_for_notify(NOTIFY_TIMEOUT)
43
+ log("done waiting for notify")
41
44
  end
42
45
 
43
- def waiting_conns
44
- execute("SELECT * FROM pg_stat_activity WHERE datname = '#{@name}' AND waiting = 't' AND application_name = 'queue_classic'")
45
- end
46
-
47
- def all_conns
48
- execute("SELECT * FROM pg_stat_activity WHERE datname = '#{@name}' AND application_name = 'queue_classic'")
49
- end
50
-
51
- def silence_warnings
52
- execute("SET client_min_messages TO 'warning'")
46
+ def execute(sql)
47
+ log("executing=#{sql}")
48
+ begin
49
+ connection.exec(sql)
50
+ rescue PGError => e
51
+ log("execute exception=#{e.inspect}")
52
+ end
53
53
  end
54
54
 
55
- def execute(sql)
56
- connection.exec(sql)
55
+ def connection
56
+ @@connection ||= connect
57
57
  end
58
58
 
59
59
  def disconnect
@@ -61,30 +61,20 @@ module QC
61
61
  @@connection = nil
62
62
  end
63
63
 
64
- def connection
65
- unless @@connection
66
- @name = @db_params.path.gsub("/","")
67
- @@connection = PGconn.connect(
68
- @db_params.host,
69
- @db_params.port || 5432,
70
- nil, '',
71
- @name,
72
- @db_params.user,
73
- @db_params.password
74
- )
75
- @@connection.exec("SET application_name = 'queue_classic'")
76
- silence_warnings unless VERBOSE
64
+ def connect
65
+ log("establishing connection")
66
+ conn = PGconn.connect(
67
+ @db_params.host,
68
+ @db_params.port || 5432,
69
+ nil, '', #opts, tty
70
+ @db_params.path.gsub("/",""), # database name
71
+ @db_params.user,
72
+ @db_params.password
73
+ )
74
+ if conn.status != PGconn::CONNECTION_OK
75
+ log("connection error=#{conn.error}")
77
76
  end
78
- @@connection
79
- end
80
-
81
- def drop_table
82
- execute("DROP TABLE IF EXISTS #{@table_name} CASCADE")
83
- end
84
-
85
- def create_table
86
- execute("CREATE TABLE #{@table_name} (id serial, details text, locked_at timestamp)")
87
- execute("CREATE INDEX #{@table_name}_id_idx ON #{@table_name} (id)")
77
+ conn
88
78
  end
89
79
 
90
80
  def load_functions
@@ -141,5 +131,9 @@ module QC
141
131
  EOD
142
132
  end
143
133
 
134
+ def log(msg)
135
+ Logger.puts(["database", msg].join(" "))
136
+ end
137
+
144
138
  end
145
139
  end
@@ -7,7 +7,7 @@ module QC
7
7
  end
8
8
 
9
9
  def <<(details)
10
- execute("INSERT INTO #{@table_name} (details) VALUES ('#{details.to_json}')")
10
+ execute("INSERT INTO #{@table_name} (details) VALUES ('#{JSON.dump(details)}')")
11
11
  end
12
12
 
13
13
  def count
@@ -0,0 +1,17 @@
1
+ module QC
2
+ module Logger
3
+
4
+ extend self
5
+
6
+ def ruby_logger
7
+ @@logger ||= ::Logger.new(STDOUT)
8
+ end
9
+
10
+ def puts(msg)
11
+ if VERBOSE
12
+ ruby_logger.debug(msg)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -32,29 +32,12 @@ module QC
32
32
  end
33
33
  end
34
34
 
35
- module QC
36
- module ConnectionHelper
37
-
38
- def connection_status
39
- {:total => database.all_conns.count, :waiting => database.waiting_conns.count}
40
- end
41
-
42
- def disconnect
43
- database.disconnect
44
- end
45
-
46
- end
47
- end
48
-
49
35
  module QC
50
36
  class Queue
51
37
 
52
38
  include AbstractQueue
53
39
  extend AbstractQueue
54
40
 
55
- include ConnectionHelper
56
- extend ConnectionHelper
57
-
58
41
  def self.array
59
42
  if defined? @@array
60
43
  @@array
@@ -22,8 +22,4 @@ namespace :qc do
22
22
  db.disconnect
23
23
  end
24
24
 
25
- task :create_queue, :name, :needs => :environment do |t,args|
26
- QC::Database.create_queue(args[:name])
27
- end
28
-
29
25
  end
@@ -4,9 +4,16 @@ module QC
4
4
  MAX_LOCK_ATTEMPTS = (ENV["QC_MAX_LOCK_ATTEMPTS"] || 5).to_i
5
5
 
6
6
  def initialize
7
- @running = true
8
- @queue = QC::Queue.new(ENV["QUEUE"])
7
+ log("worker initialized")
8
+ log("worker running exp. backoff algorith max_attempts=#{MAX_LOCK_ATTEMPTS}")
9
+ @running = true
10
+
11
+ @queue = QC::Queue.new(ENV["QUEUE"])
12
+ log("worker table=#{@queue.database.table_name}")
13
+
9
14
  @fork_worker = ENV["QC_FORK_WORKER"] == "true"
15
+ log("worker fork=#{@fork_worker}")
16
+
10
17
  handle_signals
11
18
  end
12
19
 
@@ -23,6 +30,7 @@ module QC
23
30
  trap(sig) do
24
31
  if running?
25
32
  @running = false
33
+ log("worker running=#{@running}")
26
34
  else
27
35
  raise Interrupt
28
36
  end
@@ -30,8 +38,14 @@ module QC
30
38
  end
31
39
  end
32
40
 
41
+ def setup_child
42
+ log("forked worker running setup")
43
+ end
44
+
33
45
  def start
46
+ log("worker starting")
34
47
  while running?
48
+ log("worker running...")
35
49
  if fork_worker?
36
50
  fork_and_work
37
51
  else
@@ -41,38 +55,49 @@ module QC
41
55
  end
42
56
 
43
57
  def fork_and_work
44
- @cpid = fork { work }
58
+ @cpid = fork { setup_child; work }
59
+ log("worker forked pid=#{@cpid}")
45
60
  Process.wait(@cpid)
46
61
  end
47
62
 
48
63
  def work
64
+ log("worker start working")
49
65
  if job = lock_job
66
+ log("worker locked job=#{job.id}")
50
67
  begin
51
68
  job.work
69
+ log("worker finished job=#{job.id}")
52
70
  rescue Object => e
71
+ log("worker failed job=#{job.id} exception=#{e.inspect}")
53
72
  handle_failure(job,e)
54
73
  ensure
55
74
  @queue.delete(job)
75
+ log("worker deleted job=#{job.id}")
56
76
  end
57
77
  end
58
- job = nil
59
- GC.start
60
78
  end
61
79
 
62
80
  def lock_job
81
+ log("worker attempting a lock")
63
82
  attempts = 0
64
83
  job = nil
65
84
  until job
66
85
  job = @queue.dequeue
67
86
  if job.nil?
87
+ log("worker missed lock attempt=#{attempts}")
68
88
  attempts += 1
69
89
  if attempts < MAX_LOCK_ATTEMPTS
70
- sleep(2**attempts)
90
+ seconds = 2**attempts
91
+ log("worker sleeps seconds=#{seconds}")
92
+ sleep(seconds)
93
+ log("worker tries again")
71
94
  next
72
95
  else
96
+ log("worker reached max attempts. max=#{MAX_LOCK_ATTEMPTS}")
73
97
  break
74
98
  end
75
99
  else
100
+ log("worker successfully locked job")
76
101
  end
77
102
  end
78
103
  job
@@ -87,5 +112,9 @@ module QC
87
112
  puts "!"
88
113
  end
89
114
 
115
+ def log(msg)
116
+ Logger.puts(msg)
117
+ end
118
+
90
119
  end
91
120
  end
@@ -1,11 +1,13 @@
1
1
  module DatabaseHelpers
2
2
 
3
- def init_db(table_name=nil)
4
- database = QC::Database.new(table_name)
5
- database.silence_warnings
6
- database.init_db
3
+ def init_db(table_name="queue_classic_jobs")
4
+ database = QC::Database.new(table_name)
5
+ database.execute("SET client_min_messages TO 'warning'")
6
+ database.execute("DROP TABLE IF EXISTS #{table_name} CASCADE")
7
+ database.execute("CREATE TABLE #{table_name} (id serial, details text, locked_at timestamp)")
8
+ database.load_functions
7
9
  database.disconnect
8
- true
10
+ database
9
11
  end
10
12
 
11
13
  end
@@ -3,9 +3,7 @@ require File.expand_path("../helper.rb", __FILE__)
3
3
  context "DurableArray" do
4
4
 
5
5
  setup do
6
- @database = QC::Database.new
7
- @database.drop_table
8
- @database.init_db
6
+ @database = init_db
9
7
  @array = QC::DurableArray.new(@database)
10
8
  end
11
9
 
data/test/queue_test.rb CHANGED
@@ -2,7 +2,7 @@ require File.expand_path("../helper.rb", __FILE__)
2
2
 
3
3
  context "Queue" do
4
4
 
5
- setup { init_db }
5
+ setup { @database = init_db }
6
6
 
7
7
  test "Queue class responds to enqueue" do
8
8
  QC::Queue.enqueue("Klass.method")
@@ -43,10 +43,10 @@ context "Queue" do
43
43
 
44
44
  test "queue instance responds to enqueue" do
45
45
  QC::Queue.enqueue("Something.hard_to_find")
46
- init_db(:custom_queue_name)
46
+ tmp_db = init_db(:custom_queue_name)
47
47
  @queue = QC::Queue.new(:custom_queue_name)
48
48
  @queue.enqueue "Klass.method"
49
- @queue.disconnect
49
+ @queue.database.disconnect
50
50
  end
51
51
 
52
52
  test "queue only uses 1 connection per class" do
@@ -55,7 +55,7 @@ context "Queue" do
55
55
  QC::Queue.delete QC::Queue.dequeue
56
56
  QC::Queue.enqueue "Klass.method"
57
57
  QC::Queue.dequeue
58
- assert_equal 1, QC.connection_status[:total]
58
+ assert_equal 1, @database.execute("SELECT count(*) from pg_stat_activity")[0]["count"].to_i
59
59
  end
60
60
 
61
61
  end
data/test/worker_test.rb CHANGED
@@ -23,10 +23,14 @@ end
23
23
  context "Worker" do
24
24
 
25
25
  setup do
26
- init_db
26
+ @database = init_db
27
27
  @worker = TestWorker.new
28
28
  end
29
29
 
30
+ teardown do
31
+ @database.disconnect
32
+ end
33
+
30
34
  test "working a job" do
31
35
  QC::Queue.enqueue "TestNotifier.deliver", {}
32
36
 
@@ -46,6 +50,7 @@ context "Worker" do
46
50
  test "only makes one connection" do
47
51
  QC.enqueue "TestNotifier.deliver", {}
48
52
  @worker.work
49
- assert_equal 1, QC.connection_status[:total]
53
+ assert_equal 1, @database.execute("SELECT count(*) from pg_stat_activity")[0]["count"].to_i
50
54
  end
55
+
51
56
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: queue_classic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3.pre
4
+ version: 0.3.5.pre
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -13,26 +13,15 @@ date: 2011-08-22 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg
16
- requirement: &2153629240 !ruby/object:Gem::Requirement
16
+ requirement: &2160828260 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.10.1
21
+ version: 0.11.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153629240
25
- - !ruby/object:Gem::Dependency
26
- name: json
27
- requirement: &2153628740 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: *2153628740
24
+ version_requirements: *2160828260
36
25
  description: Queue Classic (beta) is a queueing library for Ruby apps (Rails, Sinatra,
37
26
  Etc...) Queue Classic features asynchronous job polling, database maintained locks
38
27
  and no ridiculous dependencies. As a matter of fact, Queue Classic only requires
@@ -46,6 +35,7 @@ files:
46
35
  - lib/queue_classic/database.rb
47
36
  - lib/queue_classic/durable_array.rb
48
37
  - lib/queue_classic/job.rb
38
+ - lib/queue_classic/logger.rb
49
39
  - lib/queue_classic/queue.rb
50
40
  - lib/queue_classic/tasks.rb
51
41
  - lib/queue_classic/worker.rb