background_queue 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +48 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +20 -0
- data/README.md +69 -0
- data/Rakefile +42 -0
- data/TODO +2 -0
- data/VERSION +1 -0
- data/background_queue.gemspec +158 -0
- data/bin/bg_queue +26 -0
- data/lib/background_queue.rb +8 -0
- data/lib/background_queue/client.rb +96 -0
- data/lib/background_queue/client_lib/command.rb +36 -0
- data/lib/background_queue/client_lib/config.rb +109 -0
- data/lib/background_queue/client_lib/connection.rb +105 -0
- data/lib/background_queue/client_lib/job_handle.rb +19 -0
- data/lib/background_queue/command.rb +49 -0
- data/lib/background_queue/config.rb +118 -0
- data/lib/background_queue/server_lib/balanced_queue.rb +108 -0
- data/lib/background_queue/server_lib/config.rb +339 -0
- data/lib/background_queue/server_lib/event_connection.rb +133 -0
- data/lib/background_queue/server_lib/event_server.rb +35 -0
- data/lib/background_queue/server_lib/job.rb +252 -0
- data/lib/background_queue/server_lib/job_registry.rb +30 -0
- data/lib/background_queue/server_lib/lru.rb +193 -0
- data/lib/background_queue/server_lib/owner.rb +54 -0
- data/lib/background_queue/server_lib/priority_queue.rb +156 -0
- data/lib/background_queue/server_lib/queue_registry.rb +123 -0
- data/lib/background_queue/server_lib/server.rb +314 -0
- data/lib/background_queue/server_lib/sorted_workers.rb +52 -0
- data/lib/background_queue/server_lib/task.rb +79 -0
- data/lib/background_queue/server_lib/task_registry.rb +51 -0
- data/lib/background_queue/server_lib/thread_manager.rb +121 -0
- data/lib/background_queue/server_lib/worker.rb +18 -0
- data/lib/background_queue/server_lib/worker_balancer.rb +97 -0
- data/lib/background_queue/server_lib/worker_client.rb +94 -0
- data/lib/background_queue/server_lib/worker_thread.rb +70 -0
- data/lib/background_queue/utils.rb +40 -0
- data/lib/background_queue/worker/base.rb +46 -0
- data/lib/background_queue/worker/calling.rb +59 -0
- data/lib/background_queue/worker/config.rb +35 -0
- data/lib/background_queue/worker/environment.rb +70 -0
- data/lib/background_queue/worker/worker_loader.rb +94 -0
- data/lib/background_queue_server.rb +21 -0
- data/lib/background_queue_worker.rb +5 -0
- data/spec/background_queue/client_lib/command_spec.rb +75 -0
- data/spec/background_queue/client_lib/config_spec.rb +156 -0
- data/spec/background_queue/client_lib/connection_spec.rb +170 -0
- data/spec/background_queue/client_spec.rb +95 -0
- data/spec/background_queue/command_spec.rb +34 -0
- data/spec/background_queue/config_spec.rb +134 -0
- data/spec/background_queue/server_lib/balanced_queue_spec.rb +122 -0
- data/spec/background_queue/server_lib/config_spec.rb +443 -0
- data/spec/background_queue/server_lib/event_connection_spec.rb +190 -0
- data/spec/background_queue/server_lib/event_server_spec.rb +48 -0
- data/spec/background_queue/server_lib/integration/full_test_spec.rb +247 -0
- data/spec/background_queue/server_lib/integration/queue_integration_spec.rb +98 -0
- data/spec/background_queue/server_lib/integration/serialize_spec.rb +127 -0
- data/spec/background_queue/server_lib/job_registry_spec.rb +65 -0
- data/spec/background_queue/server_lib/job_spec.rb +525 -0
- data/spec/background_queue/server_lib/owner_spec.rb +33 -0
- data/spec/background_queue/server_lib/priority_queue_spec.rb +182 -0
- data/spec/background_queue/server_lib/server_spec.rb +353 -0
- data/spec/background_queue/server_lib/sorted_workers_spec.rb +122 -0
- data/spec/background_queue/server_lib/task_registry_spec.rb +69 -0
- data/spec/background_queue/server_lib/task_spec.rb +20 -0
- data/spec/background_queue/server_lib/thread_manager_spec.rb +106 -0
- data/spec/background_queue/server_lib/worker_balancer_spec.rb +127 -0
- data/spec/background_queue/server_lib/worker_client_spec.rb +196 -0
- data/spec/background_queue/server_lib/worker_thread_spec.rb +125 -0
- data/spec/background_queue/utils_spec.rb +27 -0
- data/spec/background_queue/worker/base_spec.rb +35 -0
- data/spec/background_queue/worker/calling_spec.rb +103 -0
- data/spec/background_queue/worker/environment_spec.rb +67 -0
- data/spec/background_queue/worker/worker_loader_spec.rb +103 -0
- data/spec/background_queue_spec.rb +7 -0
- data/spec/resources/config-client.yml +7 -0
- data/spec/resources/config-serialize.yml +12 -0
- data/spec/resources/config.yml +12 -0
- data/spec/resources/example_worker.rb +4 -0
- data/spec/resources/example_worker_with_error.rb +4 -0
- data/spec/resources/test_worker.rb +8 -0
- data/spec/shared/queue_registry_shared.rb +216 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/default_task.rb +9 -0
- data/spec/support/private.rb +23 -0
- data/spec/support/simple_server.rb +28 -0
- data/spec/support/simple_task.rb +58 -0
- data/spec/support/test_worker_server.rb +205 -0
- metadata +254 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module BackgroundQueue::ClientLib
|
4
|
+
|
5
|
+
#store a command and all its parameters as a hash to be serialized when passing to the server.
|
6
|
+
module Command
|
7
|
+
|
8
|
+
#create an 'add task' command
|
9
|
+
def self.add_task_command(worker, owner_id, job_id, task_id, priority, task_parameters={}, options={} )
|
10
|
+
BackgroundQueue::Command.new(:add_task, options, :worker=>worker, :owner_id=>owner_id, :job_id=>job_id, :task_id=>task_id, :priority=>priority, :params=>task_parameters)
|
11
|
+
end
|
12
|
+
|
13
|
+
#create an 'add tasks' command
|
14
|
+
def self.add_tasks_command(worker, owner_id, job_id, tasks, priority, shared_parameters={}, options={} )
|
15
|
+
raise BackgroundQueue::InvalidCommand, "No Tasks In List" if tasks.nil? || tasks.length == 0
|
16
|
+
BackgroundQueue::Command.new(:add_tasks, options, :worker=>worker, :owner_id=>owner_id, :job_id=>job_id, :tasks=>tasks, :priority=>priority, :shared_parameters=>shared_parameters)
|
17
|
+
end
|
18
|
+
|
19
|
+
#create an 'get_status' command
|
20
|
+
def self.get_status_command(job_id, options={} )
|
21
|
+
BackgroundQueue::Command.new(:get_status, options, {:job_id=>job_id})
|
22
|
+
end
|
23
|
+
|
24
|
+
#create an 'stats' command
|
25
|
+
def self.stats_command(options={})
|
26
|
+
BackgroundQueue::Command.new(:stats, options, {})
|
27
|
+
end
|
28
|
+
|
29
|
+
#create a 'remove tasks' command
|
30
|
+
#is this needed?
|
31
|
+
#def self.remove_tasks_command(tasks, options={})
|
32
|
+
# raise BackgroundQueue::InvalidCommand, "No Tasks In List" if tasks.nil? || tasks.length == 0
|
33
|
+
# BackgroundQueue::Command.new(:remove_tasks, options, {:tasks=>tasks})
|
34
|
+
#end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module BackgroundQueue::ClientLib
|
2
|
+
|
3
|
+
#The client configuration which is stored as a YAML file containing a root key for each environments configuration, much like database.yml.
|
4
|
+
#
|
5
|
+
#Example
|
6
|
+
#=======
|
7
|
+
#
|
8
|
+
# development:
|
9
|
+
# server:
|
10
|
+
# host: 127.0.0.1
|
11
|
+
# port: 3000
|
12
|
+
# memcache: 127.0.0.1:9999
|
13
|
+
# production:
|
14
|
+
# server:
|
15
|
+
# host: 192.168.3.56
|
16
|
+
# port: 3000
|
17
|
+
# failover:
|
18
|
+
# -
|
19
|
+
# host: 192.168.3.57
|
20
|
+
# port: 3000
|
21
|
+
# -
|
22
|
+
# host: 192.168.3.58
|
23
|
+
# memcache: 192.168.3.1:9999, 192.168.3.3:9999
|
24
|
+
class Config < BackgroundQueue::Config
|
25
|
+
|
26
|
+
#the primary {BackgroundQueue::Config::Server}
|
27
|
+
attr_reader :server
|
28
|
+
#an array of failover {BackgroundQueue::Config::Server}
|
29
|
+
attr_reader :failover
|
30
|
+
#an array of Strings defining memcache server address
|
31
|
+
attr_reader :memcache
|
32
|
+
|
33
|
+
#load the configration using a hash just containing the environment
|
34
|
+
def self.load_hash(env_config, path)
|
35
|
+
BackgroundQueue::ClientLib::Config.new(
|
36
|
+
build_primary_server_entry(env_config, path),
|
37
|
+
build_failover_server_entries(env_config, path),
|
38
|
+
build_memcache_array(env_config, path)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
private
|
44
|
+
|
45
|
+
def build_primary_server_entry(env_config, path)
|
46
|
+
server_entry = BackgroundQueue::Utils.get_hash_entry(env_config, :server)
|
47
|
+
if server_entry
|
48
|
+
begin
|
49
|
+
BackgroundQueue::ClientLib::Config::Server.new(server_entry)
|
50
|
+
rescue Exception=>e
|
51
|
+
raise BackgroundQueue::LoadError, "Error loading 'server' entry from background queue configuration file #{full_path(path)}: #{e.message}"
|
52
|
+
end
|
53
|
+
else
|
54
|
+
raise BackgroundQueue::LoadError, "Missing 'server' entry in background queue configuration file #{full_path(path)}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_failover_server_entries(env_config, path)
|
59
|
+
entries = []
|
60
|
+
failover_entry = BackgroundQueue::Utils.get_hash_entry(env_config, :failover)
|
61
|
+
if failover_entry && failover_entry.kind_of?(Array)
|
62
|
+
failover_entry.each_with_index do |entry, index|
|
63
|
+
begin
|
64
|
+
entries << BackgroundQueue::ClientLib::Config::Server.new(entry)
|
65
|
+
rescue Exception=>e
|
66
|
+
raise BackgroundQueue::LoadError, "Error loading 'failover' entry (#{index + 1}) from background queue configuration file #{full_path(path)}: #{e.message}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
elsif failover_entry
|
70
|
+
raise BackgroundQueue::LoadError, "Error loading 'failover' entries configuration file #{full_path(path)}: invalid data type (#{failover_entry.class.name}), expecting Array"
|
71
|
+
end
|
72
|
+
entries
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
#do not call this directly, use a load_* method
|
79
|
+
def initialize(server, failover, memcache)
|
80
|
+
@server = server
|
81
|
+
@failover = failover
|
82
|
+
@memcache = memcache
|
83
|
+
end
|
84
|
+
|
85
|
+
#A server entry in the configuration
|
86
|
+
class Server
|
87
|
+
|
88
|
+
attr_reader :host
|
89
|
+
attr_reader :port
|
90
|
+
|
91
|
+
def initialize(config_entry)
|
92
|
+
if config_entry.kind_of?(Hash)
|
93
|
+
@host = BackgroundQueue::Utils.get_hash_entry(config_entry, :host)
|
94
|
+
raise BackgroundQueue::LoadError, "Missing 'host' configuration entry" if @host.nil?
|
95
|
+
|
96
|
+
@port = BackgroundQueue::Utils.get_hash_entry(config_entry, :port)
|
97
|
+
if @port
|
98
|
+
@port = @port.to_i
|
99
|
+
else
|
100
|
+
@port = BackgroundQueue::Config::DEFAULT_PORT
|
101
|
+
end
|
102
|
+
else
|
103
|
+
raise BackgroundQueue::LoadError, "Invalid data type (#{config_entry.class.name}), expecting Hash"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module BackgroundQueue::ClientLib
|
4
|
+
#A connection to a backend queue server
|
5
|
+
#Handles sending the command to the server and receiving the reply
|
6
|
+
#For now connections are not pooled/reused
|
7
|
+
class Connection
|
8
|
+
def initialize(client, server)
|
9
|
+
@client = client
|
10
|
+
@server = server
|
11
|
+
@socket = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
#send a command to the server
|
15
|
+
def send_command(command)
|
16
|
+
check_connected
|
17
|
+
send_with_header(command.to_buf)
|
18
|
+
response = receive_with_header
|
19
|
+
BackgroundQueue::Command.from_buf(response)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def connect
|
25
|
+
begin
|
26
|
+
Timeout::timeout(3) {
|
27
|
+
@socket = TCPSocket.open(@server.host, @server.port)
|
28
|
+
true
|
29
|
+
}
|
30
|
+
rescue Timeout::Error
|
31
|
+
raise BackgroundQueue::ClientLib::ConnectionError, "Timeout Connecting to #{@server.host}:#{@server.port}"
|
32
|
+
rescue Exception=>e
|
33
|
+
raise BackgroundQueue::ClientLib::ConnectionError, "Error Connecting to #{@server.host}:#{@server.port}: #{e.message}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_connected
|
38
|
+
connect if @socket.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_data(data)
|
42
|
+
written = 0
|
43
|
+
to_write = data.length
|
44
|
+
while written < to_write do
|
45
|
+
begin
|
46
|
+
Timeout::timeout(5) {
|
47
|
+
amt_written = @socket.write(data)
|
48
|
+
written += amt_written
|
49
|
+
if written < to_write
|
50
|
+
data = data[amt_written, data.length - amt_written ]
|
51
|
+
end
|
52
|
+
}
|
53
|
+
rescue Timeout::Error
|
54
|
+
raise BackgroundQueue::ClientLib::ConnectionError, "Timeout Sending to #{@server.host}:#{@server.port}"
|
55
|
+
rescue Exception=>e
|
56
|
+
raise BackgroundQueue::ClientLib::ConnectionError, "Error Sending to #{@server.host}:#{@server.port}: #{e.message}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.add_header(data)
|
63
|
+
length = data.length
|
64
|
+
[1,length, data].pack("SLZ#{length}")
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_with_header(data)
|
68
|
+
send_data(BackgroundQueue::ClientLib::Connection.add_header(data))
|
69
|
+
end
|
70
|
+
|
71
|
+
def receive_data(length)
|
72
|
+
to_read = length
|
73
|
+
amt_read = 0
|
74
|
+
sbuf = ""
|
75
|
+
while amt_read < to_read do
|
76
|
+
begin
|
77
|
+
Timeout::timeout(5) {
|
78
|
+
read_amt = to_read - amt_read
|
79
|
+
tbuf = @socket.recvfrom(read_amt)[0]
|
80
|
+
sbuf << tbuf
|
81
|
+
amt_read += tbuf.length
|
82
|
+
}
|
83
|
+
rescue Timeout::Error
|
84
|
+
raise BackgroundQueue::ClientLib::ConnectionError, "Timeout Receiving #{length} bytes from #{@server.host}:#{@server.port}"
|
85
|
+
rescue Exception=>e
|
86
|
+
raise BackgroundQueue::ClientLib::ConnectionError, "Error Receiving #{length} bytes from #{@server.host}:#{@server.port}: #{e.message}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
sbuf
|
90
|
+
end
|
91
|
+
|
92
|
+
def receive_with_header
|
93
|
+
header = receive_data(6).unpack("SL")
|
94
|
+
receive_data(header[1])
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
#Error raised when communication failure occurs
|
102
|
+
class ConnectionError < Exception
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module BackgroundQueue::ClientLib
|
2
|
+
#returned from add_task to describe what the job/server was added.
|
3
|
+
#this is becuase you can call add_task without a job_id, and not know what server was used.
|
4
|
+
#this is passed to get_status
|
5
|
+
class JobHandle
|
6
|
+
|
7
|
+
attr_reader :owner_id
|
8
|
+
attr_reader :job_id
|
9
|
+
attr_reader :server
|
10
|
+
|
11
|
+
def initialize(owner_id, job_id, server)
|
12
|
+
@owner_id = owner_id
|
13
|
+
@job_id = job_id
|
14
|
+
@server = server
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module BackgroundQueue
|
4
|
+
|
5
|
+
#store a command and all its parameters as a hash to be serialized when passing to/from the server.
|
6
|
+
class Command
|
7
|
+
|
8
|
+
attr_accessor :code
|
9
|
+
attr_accessor :options
|
10
|
+
attr_accessor :args
|
11
|
+
|
12
|
+
def initialize(code, options, args)
|
13
|
+
@code = code
|
14
|
+
@options = BackgroundQueue::Utils::AnyKeyHash.new(options)
|
15
|
+
@args = BackgroundQueue::Utils::AnyKeyHash.new(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
#convert the command to a string (currently json) to get sent
|
19
|
+
def to_buf
|
20
|
+
{:c=>@code, :a=>@args.hash, :o=>@options.hash}.to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
#load a command from a string
|
24
|
+
def self.from_buf(buf)
|
25
|
+
hash_data = nil
|
26
|
+
begin
|
27
|
+
hash_data = JSON.load(buf)
|
28
|
+
rescue Exception=>e
|
29
|
+
raise InvalidCommand, "Invalid data format (should be json) when loading command from buffer: #{e.message}"
|
30
|
+
end
|
31
|
+
begin
|
32
|
+
raise "Missing 'c' (code)" if hash_data['c'].nil?
|
33
|
+
code = hash_data['c'].intern
|
34
|
+
raise "Missing 'a' (args)" if hash_data['a'].nil?
|
35
|
+
args = hash_data['a']
|
36
|
+
raise "Missing 'o' (options)" if hash_data['o'].nil?
|
37
|
+
options = hash_data['o']
|
38
|
+
BackgroundQueue::Command.new(code, options, args)
|
39
|
+
rescue Exception=>e
|
40
|
+
raise InvalidCommand, "Error loading command from buffer: #{e.message}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#Error raised when command is invalid
|
46
|
+
class InvalidCommand < Exception
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module BackgroundQueue
|
5
|
+
|
6
|
+
#Base class that loads configuration files for client/server
|
7
|
+
class Config
|
8
|
+
|
9
|
+
#the default port that is used if the configuration does not specify a port
|
10
|
+
DEFAULT_PORT = 2222
|
11
|
+
|
12
|
+
#load the configuration using a file path
|
13
|
+
def self.load_file(path)
|
14
|
+
string = get_string_from_file(path)
|
15
|
+
load_string(string, path)
|
16
|
+
end
|
17
|
+
|
18
|
+
#load the configuration using a string that may contain ERB syntax
|
19
|
+
def self.load_string(string, path)
|
20
|
+
evaled_string = evaluate_erb(string, path)
|
21
|
+
load_yaml(evaled_string, path)
|
22
|
+
end
|
23
|
+
|
24
|
+
#load the configuration using a string of YAML
|
25
|
+
def self.load_yaml(yaml_string, path)
|
26
|
+
all_configs = convert_yaml_to_hash(yaml_string, path)
|
27
|
+
env_config = extract_enviroment_entry(all_configs, path)
|
28
|
+
load_hash(env_config, path)
|
29
|
+
end
|
30
|
+
|
31
|
+
#load the configration using a hash just containing the environment. Overridden by Client/Server class.
|
32
|
+
def self.load_hash(env_config, path)
|
33
|
+
raise "Invalid Loading of Ciguration using abstract base class. Use Server or Client subclass."
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class << self
|
38
|
+
private
|
39
|
+
|
40
|
+
#nothing more annoying than not understanding where the library thinks path is pointing to...
|
41
|
+
def full_path(path)
|
42
|
+
if path.nil?
|
43
|
+
'<unknown>'
|
44
|
+
else
|
45
|
+
begin
|
46
|
+
File.expand_path(path)
|
47
|
+
rescue
|
48
|
+
'<unknown>'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_string_from_file(path)
|
54
|
+
if File.exist?(path)
|
55
|
+
File.open(path) { |f| f.read }
|
56
|
+
else
|
57
|
+
raise BackgroundQueue::LoadError, "Failed to open background_queue configuration file at '#{full_path(path)}'"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def evaluate_erb(string, path)
|
62
|
+
begin
|
63
|
+
message = ERB.new(string)
|
64
|
+
message.result
|
65
|
+
rescue Exception=>ex
|
66
|
+
raise BackgroundQueue::LoadError, "Error executing ERB for background_queue configuration file at '#{full_path(path)}': #{ex.message}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
def convert_yaml_to_hash(string, path)
|
73
|
+
begin
|
74
|
+
result = YAML::load(string)
|
75
|
+
raise "Root of config should be a hash of environment configurations" unless result.kind_of?(Hash)
|
76
|
+
result
|
77
|
+
rescue Exception=>ex
|
78
|
+
raise BackgroundQueue::LoadError, "Error loading YAML for background_queue configuration file at '#{full_path(path)}': #{ex.message}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def current_environment
|
83
|
+
if ENV.has_key?('RAILS_ENV')
|
84
|
+
ENV['RAILS_ENV']
|
85
|
+
elsif defined? Rails
|
86
|
+
Rails.env
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract_enviroment_entry(all_configs, path)
|
91
|
+
env_str = current_environment
|
92
|
+
if all_configs.has_key?(env_str)
|
93
|
+
all_configs[env_str]
|
94
|
+
elsif all_configs.has_key?(env_str.to_s.intern)
|
95
|
+
all_configs[env_str.to_s.intern]
|
96
|
+
else
|
97
|
+
raise BackgroundQueue::LoadError, "Error loading YAML for background_queue configuration file at '#{full_path(path)}': missing enviroment root entry: #{env_str}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_memcache_array(env_config, path)
|
102
|
+
memcache_entry = BackgroundQueue::Utils.get_hash_entry(env_config, :memcache)
|
103
|
+
if memcache_entry && memcache_entry.kind_of?(String)
|
104
|
+
memcache_entry.split(',').collect { |entry| entry.strip }.select { |entry| !entry.nil? && entry.length > 0 }
|
105
|
+
elsif memcache_entry
|
106
|
+
raise BackgroundQueue::LoadError, "Error loading 'memcache' entry in configuration file #{full_path(path)}: invalid data type (#{memcache_entry.class.name}), expecting String (comma separated)"
|
107
|
+
else
|
108
|
+
raise BackgroundQueue::LoadError, "Missing 'memcache' entry in configuration file #{full_path(path)}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
#Error raised when unable to load configuration
|
115
|
+
class LoadError < Exception
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'thread'
|
2
|
+
module BackgroundQueue::ServerLib
|
3
|
+
|
4
|
+
class BalancedQueue < PriorityQueue
|
5
|
+
include BackgroundQueue::ServerLib::QueueRegistry
|
6
|
+
|
7
|
+
attr_reader :server
|
8
|
+
|
9
|
+
def initialize(server)
|
10
|
+
@task_registry = BackgroundQueue::ServerLib::TaskRegistry.new
|
11
|
+
@condvar = ConditionVariable.new
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@server = server
|
14
|
+
@thread_manager = server.thread_manager
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_task(task)
|
19
|
+
@thread_manager.protect_access {
|
20
|
+
status, existing_task = @task_registry.register(task)
|
21
|
+
if status != :waiting
|
22
|
+
if status == :existing
|
23
|
+
remove_item(existing_task)
|
24
|
+
end
|
25
|
+
add_item(task)
|
26
|
+
@thread_manager.signal_access #wake anything reading from the queue
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_task(task)
|
32
|
+
@thread_manager.protect_access {
|
33
|
+
remove_item(task)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def finish_task(task)
|
38
|
+
@thread_manager.protect_access {
|
39
|
+
finish_item(task)
|
40
|
+
existing_task = @task_registry.de_register(task.id)
|
41
|
+
if existing_task
|
42
|
+
add_item(task)
|
43
|
+
end
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def next_task
|
48
|
+
task = nil
|
49
|
+
@thread_manager.control_access {
|
50
|
+
task = next_item
|
51
|
+
if task.nil?
|
52
|
+
@thread_manager.wait_on_access
|
53
|
+
end
|
54
|
+
}
|
55
|
+
task
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.queue_class
|
59
|
+
BackgroundQueue::ServerLib::Owner
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_job(job)
|
63
|
+
@server.jobs.register(job)
|
64
|
+
end
|
65
|
+
|
66
|
+
def synchronous?
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def save_to_file(io)
|
71
|
+
data = []
|
72
|
+
@server.logger.debug("Saving task queue to file")
|
73
|
+
@thread_manager.protect_access {
|
74
|
+
each_item { |owner|
|
75
|
+
owner.each_item { |job|
|
76
|
+
job.each_item { |task|
|
77
|
+
data << task.to_json_object(true)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
@server.logger.debug("Writing #{data.length} entries to file")
|
83
|
+
io.write(JSON.fast_generate(data))
|
84
|
+
end
|
85
|
+
|
86
|
+
def load_from_file(io)
|
87
|
+
@server.logger.debug("Loading task queue from file")
|
88
|
+
tasks = JSON.parse(io.read, :symbolize_names=>true)
|
89
|
+
@server.logger.debug("Adding #{tasks.length} tasks from file")
|
90
|
+
for task_data in tasks
|
91
|
+
task = Task.new(task_data[:owner_id], task_data[:job_id], task_data[:id], task_data[:priority], task_data[:worker], task_data[:params], task_data[:options])
|
92
|
+
add_task(task)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def get_queue_id_from_item(item)
|
99
|
+
item.owner_id
|
100
|
+
end
|
101
|
+
|
102
|
+
def add_item_to_queue(queue, item)
|
103
|
+
queue.add_item(item)
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|