background_queue 0.2.0
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/.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
|