queue_classic 2.2.0 → 2.2.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.
- checksums.yaml +7 -0
- data/lib/generators/queue_classic/install_generator.rb +22 -0
- data/lib/generators/queue_classic/templates/add_queue_classic.rb +9 -0
- data/lib/queue_classic.rb +8 -25
- data/lib/queue_classic/conn.rb +8 -21
- data/lib/queue_classic/queue.rb +32 -9
- data/lib/queue_classic/railtie.rb +9 -0
- data/lib/queue_classic/setup.rb +10 -32
- data/lib/queue_classic/worker.rb +16 -50
- data/readme.md +11 -24
- data/sql/create_table.sql +10 -0
- data/sql/drop_ddl.sql +2 -1
- data/test/helper.rb +2 -27
- data/test/queue_test.rb +15 -2
- metadata +9 -11
- data/lib/queue_classic/queries.rb +0 -42
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3137935dc373e4d8af179e93422961d1a5c07e89
|
4
|
+
data.tar.gz: 709e4565ef2dcf4635094391558942c02c44e113
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0141f28277f3f8e26422c77c0787f3cbc84986504a97a762a62119a0036237feea1fc43ee7cc081e2483737d524aa3576d553a1a8601bf15688cf11fc0576f81
|
7
|
+
data.tar.gz: 4abb6f06e3ab1e7ad39e410cea0ff277a4b0948faf923797cec4e193131f1c54d69b36a90fba06c43b9ef6f0ed1f69ad7a9acde266c48f3566fe3a72f9e05ed1
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module QC
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
namespace "queue_classic:install"
|
10
|
+
self.source_paths << File.join(File.dirname(__FILE__), 'templates')
|
11
|
+
desc 'Generates (but does not run) a migration to add a queue_classic table.'
|
12
|
+
|
13
|
+
def self.next_migration_number(dirname)
|
14
|
+
next_migration_number = current_migration_number(dirname) + 1
|
15
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_migration_file
|
19
|
+
migration_template 'add_queue_classic.rb', 'db/migrate/add_queue_classic.rb'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/queue_classic.rb
CHANGED
@@ -1,24 +1,12 @@
|
|
1
|
-
require "pg"
|
2
|
-
require "uri"
|
3
|
-
require "json"
|
4
|
-
|
5
|
-
require "queue_classic/conn"
|
6
|
-
require "queue_classic/queries"
|
7
|
-
require "queue_classic/queue"
|
8
|
-
require "queue_classic/worker"
|
9
|
-
require "queue_classic/setup"
|
10
|
-
|
11
1
|
module QC
|
12
|
-
Root = File.expand_path("..", File.dirname(__FILE__))
|
13
|
-
SqlFunctions = File.join(QC::Root, "/sql/ddl.sql")
|
14
|
-
DropSqlFunctions = File.join(QC::Root, "/sql/drop_ddl.sql")
|
15
|
-
CreateTable = File.join(QC::Root, "/sql/create_table.sql")
|
16
|
-
|
17
2
|
# You can use the APP_NAME to query for
|
18
3
|
# postgres related process information in the
|
19
4
|
# pg_stat_activity table.
|
20
5
|
APP_NAME = ENV["QC_APP_NAME"] || "queue_classic"
|
21
6
|
|
7
|
+
# Number of seconds to block on the listen chanel for new jobs.
|
8
|
+
WAIT_TIME = (ENV["QC_LISTEN_TIME"] || 5).to_i
|
9
|
+
|
22
10
|
# Why do you want to change the table name?
|
23
11
|
# Just deal with the default OK?
|
24
12
|
# If you do want to change this, you will
|
@@ -35,11 +23,6 @@ module QC
|
|
35
23
|
# There is nothing special about 9....
|
36
24
|
TOP_BOUND = (ENV["QC_TOP_BOUND"] || 9).to_i
|
37
25
|
|
38
|
-
# If you are using PostgreSQL > 9
|
39
|
-
# then you will have access to listen/notify with payload.
|
40
|
-
# Set this value if you wish to make your worker more efficient.
|
41
|
-
LISTENING_WORKER = !ENV["QC_LISTENING_WORKER"].nil?
|
42
|
-
|
43
26
|
# Set this variable if you wish for
|
44
27
|
# the worker to fork a UNIX process for
|
45
28
|
# each locked job. Remember to re-establish
|
@@ -47,11 +30,6 @@ module QC
|
|
47
30
|
# for more details.
|
48
31
|
FORK_WORKER = !ENV["QC_FORK_WORKER"].nil?
|
49
32
|
|
50
|
-
# The worker uses an exponential back-off
|
51
|
-
# algorithm to lock a job. This value will be used
|
52
|
-
# as the max exponent.
|
53
|
-
MAX_LOCK_ATTEMPTS = (ENV["QC_MAX_LOCK_ATTEMPTS"] || 5).to_i
|
54
|
-
|
55
33
|
# Defer method calls on the QC module to the
|
56
34
|
# default queue. This facilitates QC.enqueue()
|
57
35
|
def self.method_missing(sym, *args, &block)
|
@@ -101,3 +79,8 @@ module QC
|
|
101
79
|
return result
|
102
80
|
end
|
103
81
|
end
|
82
|
+
|
83
|
+
require "queue_classic/queue"
|
84
|
+
require "queue_classic/worker"
|
85
|
+
require "queue_classic/setup"
|
86
|
+
require "queue_classic/railtie" if defined?(Rails)
|
data/lib/queue_classic/conn.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'uri'
|
3
|
+
require 'pg'
|
2
4
|
|
3
5
|
module QC
|
4
6
|
module Conn
|
@@ -22,15 +24,10 @@ module QC
|
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def wait(chan, t)
|
31
|
-
listen(chan)
|
32
|
-
wait_for_notify(t)
|
33
|
-
unlisten(chan)
|
27
|
+
def wait(chan)
|
28
|
+
execute('LISTEN "' + chan + '"')
|
29
|
+
wait_for_notify(WAIT_TIME)
|
30
|
+
execute('UNLISTEN "' + chan + '"')
|
34
31
|
drain_notify
|
35
32
|
end
|
36
33
|
|
@@ -106,19 +103,9 @@ module QC
|
|
106
103
|
QC.log(msg)
|
107
104
|
end
|
108
105
|
|
109
|
-
def listen(chan)
|
110
|
-
log(:at => "LISTEN")
|
111
|
-
execute('LISTEN "' + chan + '"') #quotes matter
|
112
|
-
end
|
113
|
-
|
114
|
-
def unlisten(chan)
|
115
|
-
log(:at => "UNLISTEN")
|
116
|
-
execute('UNLISTEN "' + chan + '"') #quotes matter
|
117
|
-
end
|
118
|
-
|
119
106
|
def wait_for_notify(t)
|
120
|
-
|
121
|
-
|
107
|
+
Array.new.tap do |msgs|
|
108
|
+
connection.wait_for_notify(t) {|event, pid, msg| msgs << msg}
|
122
109
|
end
|
123
110
|
end
|
124
111
|
|
data/lib/queue_classic/queue.rb
CHANGED
@@ -1,30 +1,53 @@
|
|
1
|
+
require 'queue_classic'
|
2
|
+
require 'queue_classic/conn'
|
3
|
+
require 'json'
|
4
|
+
|
1
5
|
module QC
|
2
6
|
class Queue
|
3
7
|
|
4
|
-
attr_reader :name, :
|
5
|
-
def initialize(name,
|
8
|
+
attr_reader :name, :top_bound
|
9
|
+
def initialize(name, top_bound=nil)
|
6
10
|
@name = name
|
7
|
-
@
|
11
|
+
@top_bound = top_bound || QC::TOP_BOUND
|
8
12
|
end
|
9
13
|
|
10
14
|
def enqueue(method, *args)
|
11
|
-
|
15
|
+
QC.log_yield(:measure => 'queue.enqueue') do
|
16
|
+
s="INSERT INTO #{TABLE_NAME} (q_name, method, args) VALUES ($1, $2, $3)"
|
17
|
+
res = Conn.execute(s, name, method, JSON.dump(args))
|
18
|
+
end
|
12
19
|
end
|
13
20
|
|
14
|
-
def lock
|
15
|
-
|
21
|
+
def lock
|
22
|
+
QC.log_yield(:measure => 'queue.lock') do
|
23
|
+
s = "SELECT * FROM lock_head($1, $2)"
|
24
|
+
if r = Conn.execute(s, name, top_bound)
|
25
|
+
{:id => r["id"],
|
26
|
+
:method => r["method"],
|
27
|
+
:args => JSON.parse(r["args"])}
|
28
|
+
end
|
29
|
+
end
|
16
30
|
end
|
17
31
|
|
18
32
|
def delete(id)
|
19
|
-
|
33
|
+
QC.log_yield(:measure => 'queue.delete') do
|
34
|
+
Conn.execute("DELETE FROM #{TABLE_NAME} where id = $1", id)
|
35
|
+
end
|
20
36
|
end
|
21
37
|
|
22
38
|
def delete_all
|
23
|
-
|
39
|
+
QC.log_yield(:measure => 'queue.delete_all') do
|
40
|
+
s = "DELETE FROM #{TABLE_NAME} WHERE q_name = $1"
|
41
|
+
Conn.execute(s, name)
|
42
|
+
end
|
24
43
|
end
|
25
44
|
|
26
45
|
def count
|
27
|
-
|
46
|
+
QC.log_yield(:measure => 'queue.count') do
|
47
|
+
s = "SELECT COUNT(*) FROM #{TABLE_NAME} WHERE q_name = $1"
|
48
|
+
r = Conn.execute(s, name)
|
49
|
+
r["count"].to_i
|
50
|
+
end
|
28
51
|
end
|
29
52
|
|
30
53
|
end
|
data/lib/queue_classic/setup.rb
CHANGED
@@ -1,40 +1,18 @@
|
|
1
1
|
module QC
|
2
2
|
module Setup
|
3
|
-
|
3
|
+
Root = File.expand_path("../..", File.dirname(__FILE__))
|
4
|
+
SqlFunctions = File.join(Root, "/sql/ddl.sql")
|
5
|
+
CreateTable = File.join(Root, "/sql/create_table.sql")
|
6
|
+
DropSqlFunctions = File.join(Root, "/sql/drop_ddl.sql")
|
4
7
|
|
5
|
-
def create
|
6
|
-
|
7
|
-
|
8
|
+
def self.create
|
9
|
+
Conn.execute(File.read(CreateTable))
|
10
|
+
Conn.execute(File.read(SqlFunctions))
|
8
11
|
end
|
9
12
|
|
10
|
-
def drop
|
11
|
-
|
12
|
-
|
13
|
+
def self.drop
|
14
|
+
Conn.execute("DROP TABLE IF EXISTS queue_classic_jobs CASCADE")
|
15
|
+
Conn.execute(File.read(DropSqlFunctions))
|
13
16
|
end
|
14
|
-
|
15
|
-
def create_table
|
16
|
-
Conn.transaction do
|
17
|
-
Conn.execute(File.read(CreateTable))
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def drop_table
|
22
|
-
Conn.transaction do
|
23
|
-
Conn.execute("DROP TABLE IF EXISTS queue_classic_jobs")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def create_functions
|
28
|
-
Conn.transaction do
|
29
|
-
Conn.execute(File.read(SqlFunctions))
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def drop_functions
|
34
|
-
Conn.transaction do
|
35
|
-
Conn.execute(File.read(DropSqlFunctions))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
17
|
end
|
40
18
|
end
|
data/lib/queue_classic/worker.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'queue_classic'
|
2
|
+
require 'queue_classic/queue'
|
3
|
+
require 'queue_classic/conn'
|
4
|
+
|
1
5
|
module QC
|
2
6
|
class Worker
|
3
7
|
|
@@ -5,15 +9,10 @@ module QC
|
|
5
9
|
# In the case no arguments are passed to the initializer,
|
6
10
|
# the defaults are pulled from the environment variables.
|
7
11
|
def initialize(args={})
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@fork_worker = args[:fork_worker] ||= QC::FORK_WORKER
|
11
|
-
@listening_worker = args[:listening_worker] ||= QC::LISTENING_WORKER
|
12
|
-
@max_attempts = args[:max_attempts] ||= QC::MAX_LOCK_ATTEMPTS
|
13
|
-
|
14
|
-
@running = true
|
15
|
-
@queue = Queue.new(@q_name, @listening_worker)
|
12
|
+
@fork_worker = args[:fork_worker] || QC::FORK_WORKER
|
13
|
+
@queue = QC::Queue.new((args[:q_name] || QC::QUEUE), args[:top_bound])
|
16
14
|
log(args.merge(:at => "worker_initialized"))
|
15
|
+
@running = true
|
17
16
|
end
|
18
17
|
|
19
18
|
# Start a loop and work jobs indefinitely.
|
@@ -36,9 +35,9 @@ module QC
|
|
36
35
|
# Define setup_child to hook into the forking process.
|
37
36
|
# Using setup_child is good for re-establishing database connections.
|
38
37
|
def fork_and_work
|
39
|
-
|
40
|
-
log(:at => :fork, :pid =>
|
41
|
-
Process.wait(
|
38
|
+
cpid = fork {setup_child; work}
|
39
|
+
log(:at => :fork, :pid => cpid)
|
40
|
+
Process.wait(cpid)
|
42
41
|
end
|
43
42
|
|
44
43
|
# This method will lock a job & process the job.
|
@@ -50,33 +49,15 @@ module QC
|
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# This method will terminate early if the stop method is called or
|
58
|
-
# @max_attempts has been reached.
|
59
|
-
#
|
60
|
-
# It is important that callers delete the job when finished.
|
61
|
-
# *@queue.delete(job[:id])*
|
52
|
+
# Attempt to lock a job in the queue's table.
|
53
|
+
# Return a hash when a job is locked.
|
54
|
+
# Caller responsible for deleting the job when finished.
|
62
55
|
def lock_job
|
63
56
|
log(:at => "lock_job")
|
64
|
-
attempts = 0
|
65
57
|
job = nil
|
66
|
-
|
67
|
-
job = @queue.lock
|
68
|
-
|
69
|
-
log(:at => "failed_lock", :attempts => attempts)
|
70
|
-
if attempts < @max_attempts
|
71
|
-
wait(2**attempts)
|
72
|
-
attempts += 1
|
73
|
-
next
|
74
|
-
else
|
75
|
-
break
|
76
|
-
end
|
77
|
-
else
|
78
|
-
log(:at => "finished_lock", :job => job[:id])
|
79
|
-
end
|
58
|
+
while @running
|
59
|
+
break if job = @queue.lock
|
60
|
+
Conn.wait(@queue.name)
|
80
61
|
end
|
81
62
|
job
|
82
63
|
end
|
@@ -106,21 +87,6 @@ module QC
|
|
106
87
|
klass.send(message, *args)
|
107
88
|
end
|
108
89
|
|
109
|
-
# If @listening_worker is set, the worker will use the database
|
110
|
-
# to sleep. The database approach preferred over a syscall since
|
111
|
-
# the database will break the sleep when new jobs are inserted into
|
112
|
-
# the queue.
|
113
|
-
def wait(t)
|
114
|
-
if @listening_worker
|
115
|
-
log(:at => "listen_wait", :wait => t)
|
116
|
-
Conn.wait(@queue.chan)
|
117
|
-
else
|
118
|
-
log(:at => "sleep_wait", :wait => t)
|
119
|
-
Kernel.sleep(t)
|
120
|
-
end
|
121
|
-
log(:at => "finished_listening")
|
122
|
-
end
|
123
|
-
|
124
90
|
# This method will be called when an exception
|
125
91
|
# is raised during the execution of the job.
|
126
92
|
def handle_failure(job,e)
|
data/readme.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# queue_classic
|
2
2
|
|
3
|
-
v2.2.
|
3
|
+
v2.2.1
|
4
4
|
|
5
5
|
queue_classic provides a simple interface to a PostgreSQL-backed message queue. queue_classic specializes in concurrent locking and minimizing database load while providing a simple, intuitive developer experience. queue_classic assumes that you are already using PostgreSQL in your production environment and that adding another dependency (e.g. redis, beanstalkd, 0mq) is undesirable.
|
6
6
|
|
@@ -14,7 +14,7 @@ Features:
|
|
14
14
|
|
15
15
|
Contents:
|
16
16
|
|
17
|
-
* [Documentation](http://rubydoc.info/gems/queue_classic/2.2.
|
17
|
+
* [Documentation](http://rubydoc.info/gems/queue_classic/2.2.1/frames)
|
18
18
|
* [Usage](#usage)
|
19
19
|
* [Setup](#setup)
|
20
20
|
* [Configuration](#configuration)
|
@@ -95,7 +95,7 @@ class MyWorker < QC::Worker
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
worker = MyWorker.new
|
98
|
+
worker = MyWorker.new
|
99
99
|
|
100
100
|
trap('INT') {exit}
|
101
101
|
trap('TERM') {worker.stop}
|
@@ -129,14 +129,7 @@ Declare dependencies in Gemfile.
|
|
129
129
|
|
130
130
|
```ruby
|
131
131
|
source "http://rubygems.org"
|
132
|
-
gem "queue_classic", "2.2.
|
133
|
-
```
|
134
|
-
|
135
|
-
Require these files in your Rakefile so that you can run `rake qc:work`.
|
136
|
-
|
137
|
-
```ruby
|
138
|
-
require "queue_classic"
|
139
|
-
require "queue_classic/tasks"
|
132
|
+
gem "queue_classic", "2.2.1"
|
140
133
|
```
|
141
134
|
|
142
135
|
By default, queue_classic will use the QC_DATABASE_URL falling back on DATABASE_URL. The URL must be in the following format: `postgres://username:password@localhost/database_name`. If you use Heroku's PostgreSQL service, this will already be set. If you don't want to set this variable, you can set the connection in an initializer. **QueueClassic will maintain its own connection to the database.** This may double the number of connections to your database. Set QC::Conn.connection to share the connection between Rails & QueueClassic
|
@@ -146,21 +139,15 @@ require 'queue_classic'
|
|
146
139
|
QC::Conn.connection = ActiveRecord::Base.connection.raw_connection
|
147
140
|
```
|
148
141
|
|
149
|
-
|
142
|
+
Next you need to run the queue classic generator to create the database
|
143
|
+
migration. This will setup the necessary table to use queue classic.
|
150
144
|
|
151
|
-
```ruby
|
152
|
-
require 'queue_classic'
|
153
|
-
|
154
|
-
class AddQueueClassic < ActiveRecord::Migration
|
155
|
-
def self.up
|
156
|
-
QC::Setup.create
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.down
|
160
|
-
QC::Setup.drop
|
161
|
-
end
|
162
|
-
end
|
163
145
|
```
|
146
|
+
rails generate queue_classic:install
|
147
|
+
rake db:migrate
|
148
|
+
```
|
149
|
+
|
150
|
+
**Note on using ActiveRecord migrations:** If you use the migration, and you wish to use commands that reset the database from the stored schema (e.g. `rake db:reset`), your application must be configured with `config.active_record.schema_format = :sql` in `config/application.rb`. If you don't do this, the PL/pgSQL function that queue_classic creates will be lost when you reset the database.
|
164
151
|
|
165
152
|
### Rake Task Setup
|
166
153
|
|
data/sql/create_table.sql
CHANGED
@@ -16,4 +16,14 @@ end if;
|
|
16
16
|
|
17
17
|
end $$ language plpgsql;
|
18
18
|
|
19
|
+
create function queue_classic_notify() returns trigger as $$ begin
|
20
|
+
perform pg_notify(new.q_name, '');
|
21
|
+
return null;
|
22
|
+
end $$ language plpgsql;
|
23
|
+
|
24
|
+
create trigger queue_classic_notify
|
25
|
+
after insert on queue_classic_jobs
|
26
|
+
for each row
|
27
|
+
execute procedure queue_classic_notify();
|
28
|
+
|
19
29
|
CREATE INDEX idx_qc_on_name_only_unlocked ON queue_classic_jobs (q_name, id) WHERE locked_at IS NULL;
|
data/sql/drop_ddl.sql
CHANGED
data/test/helper.rb
CHANGED
@@ -17,36 +17,11 @@ class QCTest < Minitest::Test
|
|
17
17
|
QC.delete_all
|
18
18
|
end
|
19
19
|
|
20
|
-
def init_db
|
20
|
+
def init_db
|
21
21
|
QC::Conn.execute("SET client_min_messages TO 'warning'")
|
22
22
|
QC::Setup.drop
|
23
23
|
QC::Setup.create
|
24
|
-
QC::Conn.execute(
|
25
|
-
DO $$
|
26
|
-
-- Set initial sequence to a large number to test the entire toolchain
|
27
|
-
-- works on integers with higher bits set.
|
28
|
-
DECLARE
|
29
|
-
quoted_name text;
|
30
|
-
quoted_size text;
|
31
|
-
BEGIN
|
32
|
-
-- Find the name of the relevant sequence.
|
33
|
-
--
|
34
|
-
-- pg_get_serial_sequence quotes identifiers as part of its
|
35
|
-
-- behavior.
|
36
|
-
SELECT name
|
37
|
-
INTO STRICT quoted_name
|
38
|
-
FROM pg_get_serial_sequence('queue_classic_jobs', 'id') AS name;
|
39
|
-
|
40
|
-
-- Don't quote, because ALTER SEQUENCE RESTART doesn't like
|
41
|
-
-- general literals, only unquoted numeric literals.
|
42
|
-
SELECT pow(2, 34)::text AS size
|
43
|
-
INTO STRICT quoted_size;
|
44
|
-
|
45
|
-
EXECUTE 'ALTER SEQUENCE ' || quoted_name ||
|
46
|
-
' RESTART ' || quoted_size || ';';
|
47
|
-
END;
|
48
|
-
$$;
|
49
|
-
EOS
|
24
|
+
QC::Conn.execute(File.read('./test/helper.sql'))
|
50
25
|
end
|
51
26
|
|
52
27
|
def capture_debug_output
|
data/test/queue_test.rb
CHANGED
@@ -56,7 +56,7 @@ class QueueTest < QCTest
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def test_queue_instance
|
59
|
-
queue = QC::Queue.new("queue_classic_jobs"
|
59
|
+
queue = QC::Queue.new("queue_classic_jobs")
|
60
60
|
queue.enqueue("Klass.method")
|
61
61
|
assert_equal(1, queue.count)
|
62
62
|
queue.delete(queue.lock[:id])
|
@@ -64,7 +64,7 @@ class QueueTest < QCTest
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def test_repair_after_error
|
67
|
-
queue = QC::Queue.new("queue_classic_jobs"
|
67
|
+
queue = QC::Queue.new("queue_classic_jobs")
|
68
68
|
queue.enqueue("Klass.method")
|
69
69
|
assert_equal(1, queue.count)
|
70
70
|
connection = QC::Conn.connection
|
@@ -100,4 +100,17 @@ class QueueTest < QCTest
|
|
100
100
|
ensure
|
101
101
|
QC.default_queue = nil
|
102
102
|
end
|
103
|
+
|
104
|
+
def test_enqueue_triggers_notify
|
105
|
+
QC::Conn.execute('LISTEN "' + QC::QUEUE + '"')
|
106
|
+
QC::Conn.send(:drain_notify)
|
107
|
+
|
108
|
+
msgs = QC::Conn.send(:wait_for_notify, 0.25)
|
109
|
+
assert_equal(0, msgs.length)
|
110
|
+
|
111
|
+
QC.enqueue("Klass.method")
|
112
|
+
msgs = QC::Conn.send(:wait_for_notify, 0.25)
|
113
|
+
assert_equal(1, msgs.length)
|
114
|
+
end
|
115
|
+
|
103
116
|
end
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: queue_classic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
5
|
-
prerelease:
|
4
|
+
version: 2.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Ryan Smith (♠ ace hacker)
|
@@ -14,7 +13,6 @@ dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: pg
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -39,9 +36,11 @@ files:
|
|
39
36
|
- sql/create_table.sql
|
40
37
|
- sql/ddl.sql
|
41
38
|
- sql/drop_ddl.sql
|
39
|
+
- lib/generators/queue_classic/install_generator.rb
|
40
|
+
- lib/generators/queue_classic/templates/add_queue_classic.rb
|
42
41
|
- lib/queue_classic/conn.rb
|
43
|
-
- lib/queue_classic/queries.rb
|
44
42
|
- lib/queue_classic/queue.rb
|
43
|
+
- lib/queue_classic/railtie.rb
|
45
44
|
- lib/queue_classic/setup.rb
|
46
45
|
- lib/queue_classic/tasks.rb
|
47
46
|
- lib/queue_classic/worker.rb
|
@@ -54,27 +53,26 @@ files:
|
|
54
53
|
homepage: http://github.com/ryandotsmith/queue_classic
|
55
54
|
licenses:
|
56
55
|
- MIT
|
56
|
+
metadata: {}
|
57
57
|
post_install_message:
|
58
58
|
rdoc_options: []
|
59
59
|
require_paths:
|
60
60
|
- lib
|
61
61
|
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
-
none: false
|
63
62
|
requirements:
|
64
|
-
- -
|
63
|
+
- - '>='
|
65
64
|
- !ruby/object:Gem::Version
|
66
65
|
version: '0'
|
67
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
67
|
requirements:
|
70
|
-
- -
|
68
|
+
- - '>='
|
71
69
|
- !ruby/object:Gem::Version
|
72
70
|
version: '0'
|
73
71
|
requirements: []
|
74
72
|
rubyforge_project:
|
75
|
-
rubygems_version:
|
73
|
+
rubygems_version: 2.0.3
|
76
74
|
signing_key:
|
77
|
-
specification_version:
|
75
|
+
specification_version: 4
|
78
76
|
summary: postgres backed queue
|
79
77
|
test_files:
|
80
78
|
- test/benchmark_test.rb
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module QC
|
2
|
-
module Queries
|
3
|
-
extend self
|
4
|
-
|
5
|
-
def insert(q_name, method, args, chan=nil)
|
6
|
-
QC.log_yield(:action => "insert_job") do
|
7
|
-
s = "INSERT INTO #{TABLE_NAME} (q_name, method, args) VALUES ($1, $2, $3)"
|
8
|
-
res = Conn.execute(s, q_name, method, JSON.dump(args))
|
9
|
-
Conn.notify(chan) if chan
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def lock_head(q_name, top_bound)
|
14
|
-
s = "SELECT * FROM lock_head($1, $2)"
|
15
|
-
if r = Conn.execute(s, q_name, top_bound)
|
16
|
-
{
|
17
|
-
:id => r["id"],
|
18
|
-
:method => r["method"],
|
19
|
-
:args => JSON.parse(r["args"])
|
20
|
-
}
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def count(q_name=nil)
|
25
|
-
s = "SELECT COUNT(*) FROM #{TABLE_NAME}"
|
26
|
-
s << " WHERE q_name = $1" if q_name
|
27
|
-
r = Conn.execute(*[s, q_name].compact)
|
28
|
-
r["count"].to_i
|
29
|
-
end
|
30
|
-
|
31
|
-
def delete(id)
|
32
|
-
Conn.execute("DELETE FROM #{TABLE_NAME} where id = $1", id)
|
33
|
-
end
|
34
|
-
|
35
|
-
def delete_all(q_name=nil)
|
36
|
-
s = "DELETE FROM #{TABLE_NAME}"
|
37
|
-
s << " WHERE q_name = $1" if q_name
|
38
|
-
Conn.execute(*[s, q_name].compact)
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|