redis-repeater 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/redis-repeater CHANGED
@@ -1,9 +1,34 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'yaml'
3
4
  require File.dirname(__FILE__) + '/../lib/redis_repeater'
5
+ require 'pidly'
4
6
 
5
- if ARGV.empty?
6
- puts "Please specify a config directory"
7
+ class RedisRepeaterController < Pidly::Control
8
+
9
+ before_start do
10
+ if ARGV.size != 2
11
+ puts 'redis-repeater start /path/to/config.yml'
12
+ exit
13
+ else
14
+ configuration = YAML::load File.open(ARGV[1], 'r')
15
+ @repeater = RedisRepeater::Repeater.new(configuration)
16
+ puts self.pid
17
+ end
18
+ end
19
+
20
+ start do
21
+ @repeater.run_forever
22
+ end
23
+
24
+ end
25
+
26
+ @daemon = RedisRepeaterController.spawn :name => 'redis_repeater', :verbose => true
27
+
28
+ # Handle the command
29
+ commands = %w{start stop status restart clean! kill}
30
+ if commands.include?(ARGV.first)
31
+ @daemon.send ARGV.first
7
32
  else
8
- RedisRepeater::start(ARGV[0])
33
+ puts "No such command, try one of: #{commands.join(', ')}"
9
34
  end
@@ -1,75 +1,14 @@
1
- require File.dirname(__FILE__) + '/redis_repeater/scheduler.rb'
2
- require File.dirname(__FILE__) + '/redis_repeater/scheduler_job.rb'
3
- require File.dirname(__FILE__) + '/redis_repeater/transfer_scheduler_job.rb'
4
-
5
- require 'yaml'
1
+ require 'bundler/setup'
2
+ require 'redis'
6
3
  require 'logger'
7
- require 'fileutils'
8
-
9
4
  require 'eventmachine'
10
- require 'redis'
11
-
12
- module RedisRepeater
13
-
14
- DefaultRedisHost = 'localhost'
15
- DefaultRedisPort = 6379
16
- LogDefaultFilename = File.dirname(__FILE__) + '/../log/redis_repeater.log'
17
5
 
18
- def self.start(config_path)
6
+ require File.dirname(__FILE__) + '/redis_repeater/configuration_error'
7
+ require File.dirname(__FILE__) + '/redis_repeater/repeater'
19
8
 
20
- # allow a directory OR a yml file
21
- if File.directory?(config_path)
22
- config = YAML::load File.open(File.join(config_path, 'config.yml'))
23
- queues = YAML::load File.open(File.join(config_path, 'queues.yml'))
24
- else
25
- config = YAML::load File.open(config_path)
26
- queues = config['queues']
27
- end
28
-
29
- # Connect to redis
30
- redis_from = redis_configure(config, 'origin')
31
- redis_to = redis_configure(config, 'destination')
32
-
33
- # replace 'magic' resque:queues key with all resque queues, but don't overwrite otherwise configured queues
34
- # warning: only resolves queue names on startup, if new queue is created it will not pick it up.
35
- if queues.has_key?("resque:queues")
36
- redis_from.smembers("resque:queues").each do |queue|
37
- queues["resque:queue:#{queue}"] = queues["resque:queues"] unless queues.has_key?("resque:queue:#{queue}")
38
- end
39
- queues.delete("resque:queues")
40
- end
41
-
42
- # Logger
43
- if config.has_key?('log')
44
- log_filename = config['log']
45
- FileUtils.mkdir_p(File.dirname(log_filename))
46
- logger = Logger.new(log_filename)
47
- else
48
- logger = Logger.new(STDOUT)
49
- end
50
-
51
- # Load the queues into the scheduler
52
- scheduler = Scheduler.new(logger)
53
- EventMachine::run do
54
- queues.each do |name, timeout|
55
- puts "#{name} - #{timeout}"
56
- scheduler << TransferSchedulerJob.new(name, timeout, logger, redis_from, redis_to, config['counter'])
57
- end
58
- end
59
-
60
- # Perform forever
61
- scheduler.perform # TODO make configurable
62
-
63
- end
9
+ module RedisRepeater
64
10
 
65
- def self.redis_configure(config, name)
66
- host = DefaultRedisHost
67
- port = DefaultRedisPort
68
- if config.has_key?(name)
69
- host = config[name]['host'] if config[name].has_key?('host')
70
- port = config[name]['port'] if config[name].has_key?('port')
71
- end
72
- Redis.new(:host => host, :port => port)
73
- end
11
+ autoload :VERSION, File.dirname(__FILE__) + '/redis_repeater/version'
12
+ autoload :TransferSchedulerJob, File.dirname(__FILE__) + '/redis_repeater/transfer_scheduler_job'
74
13
 
75
14
  end
@@ -0,0 +1,7 @@
1
+ module RedisRepeater
2
+
3
+ class ConfigurationError < StandardError
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,66 @@
1
+ module RedisRepeater
2
+
3
+ class Repeater
4
+
5
+ attr_reader :servers, :repeats, :logger
6
+
7
+ def initialize(configuration, logger = nil)
8
+ raise ConfigurationError.new 'Configuration is empty' unless configuration
9
+ @servers = {}
10
+ @repeats = []
11
+ # Get the logger
12
+ @logger = logger
13
+ @logger ||= Logger.new(File.open(configuration['log'], 'a')) if configuration['log']
14
+ @logger ||= Logger.new(STDOUT)
15
+ # Load the configuration and start the server
16
+ load_hash_configuration configuration
17
+ end
18
+
19
+ def run_forever
20
+ raise ConfigurationError.new('No repeat jobs specified in configuration') if @repeats.empty?
21
+ # start the scheduler and run it forever
22
+ EventMachine::run do
23
+ @repeats.each do |job|
24
+ EventMachine::add_periodic_timer(job.timeout) { job.perform }
25
+ end
26
+ end
27
+ @logger.info "\nRepeating #{@repeats.count} #{@repeats.count == 1 ? 'queue' : 'queues'} forever!"
28
+ end
29
+
30
+ private
31
+
32
+ def load_hash_configuration(hash)
33
+ hash['servers'] && hash['servers'].each do |name, value|
34
+ options = {}
35
+ value.each { |k, v| options[k.to_sym] = v }
36
+ @servers[name] = Redis.new(options)
37
+ end
38
+ hash['repeats'] && hash['repeats'].each do |repeat|
39
+ options = {}
40
+ options[:source] = find_server_by_name repeat['source']
41
+ # Handle 'magic' queues
42
+ if repeat['queue'] == 'resque:queues' || repeat['queue'] == 'resque:queues:*'
43
+ options[:source].smembers('resque:queues').each do |queue|
44
+ repeat = repeat.dup
45
+ repeat['queue'] = "resque:queue:#{queue}"
46
+ hash['repeats'] << repeat
47
+ end
48
+ next
49
+ end
50
+ options[:destinations] = repeat['destinations'].map { |d| { :server => find_server_by_name(d['server']), :queue => d['queue'] || repeat['queue'] } }
51
+ options[:queue] = repeat['queue']
52
+ options[:timeout] = repeat['timeout'] || 0
53
+ options[:maintain_count] = !!repeat['maintain_count'] # default false
54
+ @repeats << TransferSchedulerJob.new(self, options)
55
+ end
56
+ end
57
+
58
+ def find_server_by_name(name)
59
+ server = @servers[name]
60
+ raise ConfigurationError.new("No such server found: #{name}") if server.nil?
61
+ server
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -1,25 +1,30 @@
1
1
  module RedisRepeater
2
2
 
3
- # Move jobs from One Redis queue to the same queue on a different machine
3
+ class TransferSchedulerJob
4
4
 
5
- class TransferSchedulerJob < SchedulerJob
5
+ attr_reader :source, :destinations, :queue, :timeout, :maintain_count
6
6
 
7
- def initialize(name, timeout, logger, redis_from, redis_to, maintain_count)
8
- @redis_from = redis_from
9
- @redis_to = redis_to
10
- @maintain_count = maintain_count
11
- super(name, timeout, logger)
7
+ def initialize(repeater, config)
8
+ @source = config[:source]
9
+ @destinations = config[:destinations]
10
+ @queue = config[:queue]
11
+ @timeout = config[:timeout]
12
+ @maintain_count = config[:maintain_count]
13
+ @logger = repeater.logger
12
14
  end
13
15
 
14
16
  def perform
17
+ # Transport everything we can
15
18
  count = 0
16
- while (item = @redis_from.lpop @name)
17
- # work the same direction resque does
18
- @redis_to.rpush(@name, item)
19
+ while (item = @source.lpop @queue)
20
+ @destinations.each do |destination|
21
+ destination[:server].rpush destination[:queue], item
22
+ end
19
23
  count += 1
20
24
  end
21
- @redis_from.incrby("redis-repeater:#{@name}:count", count) if @maintain_count
22
- @logger.debug "Transported #{count} items for #{@name}"
25
+ # Keep the count and log a debug message
26
+ @source.incrby("redis-repeater:#{@queue}:count", count) if @maintain_count
27
+ @logger.debug "Transported #{count} items from #{@queue} to #{@destinations.count} #{@destinations.count == 1 ? 'destination' : 'destinations'}"
23
28
  end
24
29
 
25
30
  end
@@ -1,5 +1,5 @@
1
1
  module RedisRepeater
2
2
 
3
- VERSION = [0, 1, 0]
3
+ VERSION = [0, 2, 0]
4
4
 
5
5
  end
data/spec/spec_helper.rb CHANGED
@@ -1,39 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/../lib/redis_repeater'
2
2
 
3
- SERVER_PORT = 6391
4
- CLIENT_PORT = 6392
5
- QN = 'queue-1234'
3
+ SERVER_ONE = { 'hostname' => 'localhost', 'port' => 6379 }
4
+ SERVER_TWO = { 'hostname' => 'localhost', 'port' => 6380 }
6
5
 
7
- SERVERS = ['client', 'server']
8
-
9
- def start_servers
10
- SERVERS.each do |s|
11
- print "starting a #{s}.. "
12
- `redis-server #{File.dirname(__FILE__) + "/support/#{s}.conf"}`
13
- puts 'DONE'
14
- end
15
- end
16
-
17
- def stop_servers
18
- SERVERS.each do |s|
19
- pid = `cat #{s}.pidfile`.chop
20
- print "stopping #{s} (PID:#{pid}).. "
21
- `kill #{pid}`
22
- puts 'DONE'
23
- end
24
- end
25
-
26
- def start_repeater(type = :default)
27
- print 'starting the repeater.. '
28
- # TODO deamonize
29
- @rthread = Thread.start do
30
- RedisRepeater.start(File.dirname(__FILE__) + "/support/config/#{type}")
31
- end
32
- puts 'DONE'
33
- end
34
-
35
- def stop_repeater
36
- print 'stopping the repeater.. '
37
- @rthread.kill
38
- puts 'DONE'
39
- end
6
+ REDIS_ONE = Redis.new SERVER_ONE
7
+ TEST_FILENAME = 'spec/test.log'
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: redis-repeater
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - John Crepezzi
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-03-14 00:00:00 -04:00
13
+ date: 2011-04-20 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -46,6 +46,17 @@ dependencies:
46
46
  version: "0"
47
47
  type: :runtime
48
48
  version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: pidly
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :runtime
59
+ version_requirements: *id004
49
60
  description: Automatically move events from one redis queue to the same queue on another server
50
61
  email: john@crepezzi.com
51
62
  executables:
@@ -55,8 +66,8 @@ extensions: []
55
66
  extra_rdoc_files: []
56
67
 
57
68
  files:
58
- - lib/redis_repeater/scheduler.rb
59
- - lib/redis_repeater/scheduler_job.rb
69
+ - lib/redis_repeater/configuration_error.rb
70
+ - lib/redis_repeater/repeater.rb
60
71
  - lib/redis_repeater/transfer_scheduler_job.rb
61
72
  - lib/redis_repeater/version.rb
62
73
  - lib/redis_repeater.rb
@@ -1,19 +0,0 @@
1
- module RedisRepeater
2
-
3
- class Scheduler
4
-
5
- def initialize(logger = nil)
6
- @logger = logger
7
- @logger.info 'Running on schedule forever...'
8
- end
9
-
10
- # When adding, we move through the current queue, and
11
- # find the first one that is performing after we want to
12
- def <<(job)
13
- scheduler = self
14
- EventMachine::add_timer(job.timeout) { job.perform; scheduler << job }
15
- end
16
-
17
- end
18
-
19
- end
@@ -1,24 +0,0 @@
1
- module RedisRepeater
2
-
3
- class SchedulerJob
4
-
5
- def initialize(name, timeout, logger)
6
- @name = name
7
- @timeout = timeout
8
- @logger = logger
9
- @next_allowed_performance = Time.now + timeout
10
- end
11
-
12
- def perform
13
- puts "Performing #{self.name}"
14
- end
15
-
16
- def adjust_next_allowed_performance
17
- @next_allowed_performance = Time.now + timeout
18
- end
19
-
20
- attr_reader :name, :timeout, :next_allowed_performance
21
-
22
- end
23
-
24
- end