redis-repeater 0.1.0 → 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/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