queue_classic 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|