nogara-resque-loner 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ module Resque
2
+ module Plugins
3
+ module Loner
4
+ VERSION = "1.2.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ require 'resque-loner'
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ require 'resque-loner/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'nogara-resque-loner'
9
+ s.version = Resque::Plugins::Loner::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ['Jannis Hermanns']
12
+ s.email = ['jannis@moviepilot.com']
13
+ s.homepage = 'http://github.com/jayniz/resque-loner'
14
+ s.summary = 'Adds unique jobs to resque'
15
+ s.has_rdoc = false
16
+
17
+ s.rubyforge_project = 'resque-loner'
18
+
19
+ s.add_dependency 'resque', '~>1.0'
20
+ {
21
+ 'rake' => '> 0.8.7',
22
+ 'rack-test' => '~> 0.5.7',
23
+ 'rspec' => '~> 2.5.0',
24
+ 'mock_redis' => '~> 0.2.0',
25
+ 'yajl-ruby' => '~> 0.8.2'
26
+ }.each do |lib, version|
27
+ s.add_development_dependency lib, version
28
+ end
29
+
30
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
+ s.files = `git ls-files`.split("\n")
32
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
33
+ s.require_paths = ["lib"]
34
+
35
+ s.description = <<desc
36
+ Makes sure that for special jobs, there can be only one job with the same workload in one queue.
37
+
38
+ Example:
39
+ class CacheSweeper
40
+
41
+ include Resque::Plugins::UniqueJob
42
+
43
+ @queue = :cache_sweeps
44
+
45
+ def self.perform(article_id)
46
+ # Cache Me If You Can...
47
+ end
48
+ end
49
+ desc
50
+ end
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ #
5
+ # Resque-loner specific specs. I'm shooting right through the stack here and just
6
+ # test the outcomes, because the implementation will change soon and the tests run
7
+ # quite quickly.
8
+ #
9
+
10
+ class SomeJob
11
+ @queue = :some_queue
12
+ end
13
+
14
+ class SomeUniqueJob
15
+
16
+ include Resque::Plugins::UniqueJob
17
+
18
+ @queue = :other_queue
19
+ def self.perform(foo); end
20
+ end
21
+
22
+ class FailingUniqueJob
23
+ include Resque::Plugins::UniqueJob
24
+ @queue = :other_queue
25
+ def self.perform(foo)
26
+ raise "I beg to differ"
27
+ end
28
+ end
29
+
30
+ class DeprecatedUniqueJob < Resque::Plugins::Loner::UniqueJob
31
+
32
+ @queue = :other_queue
33
+ def self.perform(foo); end
34
+ end
35
+
36
+ class UniqueJobWithTtl
37
+ include Resque::Plugins::UniqueJob
38
+ @queue = :unique_with_ttl
39
+ @loner_ttl = 300
40
+
41
+ def self.perform(*args); end
42
+ end
43
+
44
+ describe "Resque" do
45
+
46
+ before(:each) do
47
+ Resque.redis.flushall
48
+ Resque.size(:other_queue).should == 0
49
+ Resque.size(:some_queue).should == 0
50
+ end
51
+
52
+ describe "Jobs" do
53
+ it "can put multiple normal jobs on a queue" do
54
+ Resque.enqueue SomeJob, "foo"
55
+ Resque.enqueue SomeJob, "foo"
56
+ Resque.size(:some_queue).should == 2
57
+ end
58
+
59
+ it "should allow only one of the same job to sit in a queue" do
60
+ Resque.enqueue SomeUniqueJob, "foo"
61
+ Resque.enqueue SomeUniqueJob, "foo"
62
+ Resque.size(:other_queue).should == 1
63
+ end
64
+
65
+ it "should support deprecated Resque::Plugins::Loner::UniqueJob class" do
66
+ Resque.enqueue DeprecatedUniqueJob, "foo"
67
+ Resque.enqueue DeprecatedUniqueJob, "foo"
68
+ Resque.size(:other_queue).should == 1
69
+ end
70
+
71
+ it "should allow the same jobs to be executed one after the other" do
72
+ Resque.enqueue SomeUniqueJob, "foo"
73
+ Resque.enqueue SomeUniqueJob, "foo"
74
+ Resque.size(:other_queue).should == 1
75
+
76
+ Resque.reserve(:other_queue)
77
+ Resque.size(:other_queue).should == 0
78
+
79
+ Resque.enqueue SomeUniqueJob, "foo"
80
+ Resque.enqueue SomeUniqueJob, "foo"
81
+ Resque.size(:other_queue).should == 1
82
+ end
83
+
84
+ it "should be robust regarding hash attributes" do
85
+ Resque.enqueue SomeUniqueJob, :bar => 1, :foo => 2
86
+ Resque.enqueue SomeUniqueJob, :foo => 2, :bar => 1
87
+ Resque.size(:other_queue).should == 1
88
+ end
89
+
90
+ it "should be robust regarding hash attributes (JSON does not distinguish between string and symbol)" do
91
+ Resque.enqueue SomeUniqueJob, :bar => 1, :foo => 1
92
+ Resque.enqueue SomeUniqueJob, :bar => 1, "foo" => 1
93
+ Resque.size(:other_queue).should == 1
94
+ end
95
+
96
+ it "should mark jobs as unqueued, when Job.destroy is killing them" do
97
+ Resque.enqueue SomeUniqueJob, "foo"
98
+ Resque.enqueue SomeUniqueJob, "foo"
99
+ Resque.size(:other_queue).should == 1
100
+
101
+ Resque::Job.destroy(:other_queue, SomeUniqueJob)
102
+ Resque.size(:other_queue).should == 0
103
+
104
+ Resque.enqueue SomeUniqueJob, "foo"
105
+ Resque.enqueue SomeUniqueJob, "foo"
106
+ Resque.size(:other_queue).should == 1
107
+ end
108
+
109
+ it "should mark jobs as unqueued, when they raise an exception during #perform" do
110
+ 2.times { Resque.enqueue( FailingUniqueJob, "foo" ) }
111
+ Resque.size(:other_queue).should == 1
112
+
113
+ worker = Resque::Worker.new(:other_queue)
114
+ worker.work 0
115
+ Resque.size(:other_queue).should == 0
116
+
117
+ 2.times { Resque.enqueue( FailingUniqueJob, "foo" ) }
118
+ Resque.size(:other_queue).should == 1
119
+ end
120
+
121
+ it "should report if a job is queued or not" do
122
+ Resque.enqueue SomeUniqueJob, "foo"
123
+ Resque.enqueued?(SomeUniqueJob, "foo").should be_true
124
+ Resque.enqueued?(SomeUniqueJob, "bar").should be_false
125
+ end
126
+
127
+ it "should report if a job is in a special queue or not" do
128
+ default_queue = SomeUniqueJob.instance_variable_get(:@queue)
129
+ SomeUniqueJob.instance_variable_set(:@queue, :special_queue)
130
+
131
+ Resque.enqueue SomeUniqueJob, "foo"
132
+ Resque.enqueued_in?( :special_queue, SomeUniqueJob, "foo").should be_true
133
+
134
+ SomeUniqueJob.instance_variable_set(:@queue, default_queue)
135
+
136
+ Resque.enqueued?( SomeUniqueJob, "foo").should be_false
137
+ end
138
+
139
+ it "should not be able to report if a non-unique job was enqueued" do
140
+ Resque.enqueued?(SomeJob).should be_nil
141
+ end
142
+
143
+ it "should cleanup all loners when a queue is destroyed" do
144
+ Resque.enqueue SomeUniqueJob, "foo"
145
+ Resque.enqueue FailingUniqueJob, "foo"
146
+
147
+ Resque.remove_queue(:other_queue)
148
+
149
+ Resque.enqueue(SomeUniqueJob, "foo")
150
+ Resque.size(:other_queue).should == 1
151
+ end
152
+
153
+ it 'should not raise an error when deleting an already empty queue' do
154
+ expect { Resque.remove_queue(:other_queue) }.to_not raise_error
155
+ end
156
+
157
+ it 'should honor loner_ttl in the redis key' do
158
+ Resque.enqueue UniqueJobWithTtl
159
+ Resque.enqueued?(UniqueJobWithTtl).should be_true
160
+ k=Resque.redis.keys "loners:queue:unique_with_ttl:job:*"
161
+ k.length.should == 1
162
+ Resque.redis.ttl(k[0]).should be_within(2).of(UniqueJobWithTtl.loner_ttl)
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,115 @@
1
+ # Redis configuration file example
2
+
3
+ # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
+ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
+ daemonize yes
6
+
7
+ # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
+ # You can specify a custom pid file location here.
9
+ pidfile ./spec/redis-test.pid
10
+
11
+ # Accept connections on the specified port, default is 6379
12
+ port 9736
13
+
14
+ # If you want you can bind a single interface, if the bind option is not
15
+ # specified all the interfaces will listen for connections.
16
+ #
17
+ # bind 127.0.0.1
18
+
19
+ # Close the connection after a client is idle for N seconds (0 to disable)
20
+ timeout 300
21
+
22
+ # Save the DB on disk:
23
+ #
24
+ # save <seconds> <changes>
25
+ #
26
+ # Will save the DB if both the given number of seconds and the given
27
+ # number of write operations against the DB occurred.
28
+ #
29
+ # In the example below the behaviour will be to save:
30
+ # after 900 sec (15 min) if at least 1 key changed
31
+ # after 300 sec (5 min) if at least 10 keys changed
32
+ # after 60 sec if at least 10000 keys changed
33
+ save 900 1
34
+ save 300 10
35
+ save 60 10000
36
+
37
+ # The filename where to dump the DB
38
+ dbfilename dump.rdb
39
+
40
+ # For default save/load DB in/from the working directory
41
+ # Note that you must specify a directory not a file name.
42
+ dir ./spec/
43
+
44
+ # Set server verbosity to 'debug'
45
+ # it can be one of:
46
+ # debug (a lot of information, useful for development/testing)
47
+ # notice (moderately verbose, what you want in production probably)
48
+ # warning (only very important / critical messages are logged)
49
+ loglevel debug
50
+
51
+ # Specify the log file name. Also 'stdout' can be used to force
52
+ # the demon to log on the standard output. Note that if you use standard
53
+ # output for logging but daemonize, logs will be sent to /dev/null
54
+ logfile stdout
55
+
56
+ # Set the number of databases. The default database is DB 0, you can select
57
+ # a different one on a per-connection basis using SELECT <dbid> where
58
+ # dbid is a number between 0 and 'databases'-1
59
+ databases 16
60
+
61
+ ################################# REPLICATION #################################
62
+
63
+ # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
+ # another Redis server. Note that the configuration is local to the slave
65
+ # so for example it is possible to configure the slave to save the DB with a
66
+ # different interval, or to listen to another port, and so on.
67
+
68
+ # slaveof <masterip> <masterport>
69
+
70
+ ################################## SECURITY ###################################
71
+
72
+ # Require clients to issue AUTH <PASSWORD> before processing any other
73
+ # commands. This might be useful in environments in which you do not trust
74
+ # others with access to the host running redis-server.
75
+ #
76
+ # This should stay commented out for backward compatibility and because most
77
+ # people do not need auth (e.g. they run their own servers).
78
+
79
+ # requirepass foobared
80
+
81
+ ################################### LIMITS ####################################
82
+
83
+ # Set the max number of connected clients at the same time. By default there
84
+ # is no limit, and it's up to the number of file descriptors the Redis process
85
+ # is able to open. The special value '0' means no limts.
86
+ # Once the limit is reached Redis will close all the new connections sending
87
+ # an error 'max number of clients reached'.
88
+
89
+ # maxclients 128
90
+
91
+ # Don't use more memory than the specified amount of bytes.
92
+ # When the memory limit is reached Redis will try to remove keys with an
93
+ # EXPIRE set. It will try to start freeing keys that are going to expire
94
+ # in little time and preserve keys with a longer time to live.
95
+ # Redis will also try to remove objects from free lists if possible.
96
+ #
97
+ # If all this fails, Redis will start to reply with errors to commands
98
+ # that will use more memory, like SET, LPUSH, and so on, and will continue
99
+ # to reply to most read-only commands like GET.
100
+ #
101
+ # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
+ # 'state' server or cache, not as a real DB. When Redis is used as a real
103
+ # database the memory usage will grow over the weeks, it will be obvious if
104
+ # it is going to use too much memory in the long run, and you'll have the time
105
+ # to upgrade. With maxmemory after the limit is reached you'll start to get
106
+ # errors for write operations, and this may even lead to DB inconsistency.
107
+
108
+ # maxmemory <bytes>
109
+
110
+ ############################### ADVANCED CONFIG ###############################
111
+
112
+ # Glue small output buffers together in order to send small replies in a
113
+ # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
+ # in terms of number of queries per second. Use 'yes' if unsure.
115
+ glueoutputbuf yes
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rspec'
4
+
5
+ require 'ruby-debug'
6
+ require 'mock_redis'
7
+ require 'resque'
8
+ require 'resque-loner'
9
+
10
+ RSpec.configure do |config|
11
+ config.before(:suite) do
12
+ Resque.redis = MockRedis.new
13
+ end
14
+ end
@@ -0,0 +1,159 @@
1
+ # Inspired by rabbitmq.rake the Redbox project at http://github.com/rick/redbox/tree/master
2
+ require 'fileutils'
3
+ require 'open-uri'
4
+ require 'pathname'
5
+
6
+ class RedisRunner
7
+
8
+ def self.redis_dir
9
+ @redis_dir ||= if ENV['PREFIX']
10
+ Pathname.new(ENV['PREFIX'])
11
+ else
12
+ Pathname.new(`which redis-server`) + '..' + '..'
13
+ end
14
+ end
15
+
16
+ def self.bin_dir
17
+ redis_dir + 'bin'
18
+ end
19
+
20
+ def self.config
21
+ @config ||= if File.exists?(redis_dir + 'etc/redis.conf')
22
+ redis_dir + 'etc/redis.conf'
23
+ else
24
+ redis_dir + '../etc/redis.conf'
25
+ end
26
+ end
27
+
28
+ def self.dtach_socket
29
+ '/tmp/redis.dtach'
30
+ end
31
+
32
+ # Just check for existance of dtach socket
33
+ def self.running?
34
+ File.exists? dtach_socket
35
+ end
36
+
37
+ def self.start
38
+ puts 'Detach with Ctrl+\ Re-attach with rake redis:attach'
39
+ sleep 1
40
+ command = "#{bin_dir}/dtach -A #{dtach_socket} #{bin_dir}/redis-server #{config}"
41
+ sh command
42
+ end
43
+
44
+ def self.attach
45
+ exec "#{bin_dir}/dtach -a #{dtach_socket}"
46
+ end
47
+
48
+ def self.stop
49
+ sh 'echo "SHUTDOWN" | nc localhost 6379'
50
+ end
51
+
52
+ end
53
+
54
+ namespace :redis do
55
+
56
+ desc 'About redis'
57
+ task :about do
58
+ puts "\nSee http://code.google.com/p/redis/ for information about redis.\n\n"
59
+ end
60
+
61
+ desc 'Start redis'
62
+ task :start do
63
+ RedisRunner.start
64
+ end
65
+
66
+ desc 'Stop redis'
67
+ task :stop do
68
+ RedisRunner.stop
69
+ end
70
+
71
+ desc 'Restart redis'
72
+ task :restart do
73
+ RedisRunner.stop
74
+ RedisRunner.start
75
+ end
76
+
77
+ desc 'Attach to redis dtach socket'
78
+ task :attach do
79
+ RedisRunner.attach
80
+ end
81
+
82
+ desc 'Install the latest verison of Redis from Github (requires git, duh)'
83
+ task :install => [:about, :download, :make] do
84
+ bin_dir = '/usr/bin'
85
+ conf_dir = '/etc'
86
+
87
+ if ENV['PREFIX']
88
+ bin_dir = "#{ENV['PREFIX']}/bin"
89
+ sh "mkdir -p #{bin_dir}" unless File.exists?("#{bin_dir}")
90
+
91
+ conf_dir = "#{ENV['PREFIX']}/etc"
92
+ sh "mkdir -p #{conf_dir}" unless File.exists?("#{conf_dir}")
93
+ end
94
+
95
+ %w(redis-benchmark redis-cli redis-server).each do |bin|
96
+ sh "cp /tmp/redis/#{bin} #{bin_dir}"
97
+ end
98
+
99
+ puts "Installed redis-benchmark, redis-cli and redis-server to #{bin_dir}"
100
+
101
+ unless File.exists?("#{conf_dir}/redis.conf")
102
+ sh "cp /tmp/redis/redis.conf #{conf_dir}/redis.conf"
103
+ puts "Installed redis.conf to #{conf_dir} \n You should look at this file!"
104
+ end
105
+ end
106
+
107
+ task :make do
108
+ sh "cd /tmp/redis && make clean"
109
+ sh "cd /tmp/redis && make"
110
+ end
111
+
112
+ desc "Download package"
113
+ task :download do
114
+ sh 'rm -rf /tmp/redis/' if File.exists?("/tmp/redis/.svn")
115
+ sh 'git clone git://github.com/antirez/redis.git /tmp/redis' unless File.exists?('/tmp/redis')
116
+ sh "cd /tmp/redis && git pull" if File.exists?("/tmp/redis/.git")
117
+ end
118
+
119
+ end
120
+
121
+ namespace :dtach do
122
+
123
+ desc 'About dtach'
124
+ task :about do
125
+ puts "\nSee http://dtach.sourceforge.net/ for information about dtach.\n\n"
126
+ end
127
+
128
+ desc 'Install dtach 0.8 from source'
129
+ task :install => [:about, :download, :make] do
130
+
131
+ bin_dir = "/usr/bin"
132
+
133
+ if ENV['PREFIX']
134
+ bin_dir = "#{ENV['PREFIX']}/bin"
135
+ sh "mkdir -p #{bin_dir}" unless File.exists?("#{bin_dir}")
136
+ end
137
+
138
+ sh "cp /tmp/dtach-0.8/dtach #{bin_dir}"
139
+ end
140
+
141
+ task :make do
142
+ sh 'cd /tmp/dtach-0.8/ && ./configure && make'
143
+ end
144
+
145
+ desc "Download package"
146
+ task :download do
147
+ unless File.exists?('/tmp/dtach-0.8.tar.gz')
148
+ require 'net/http'
149
+
150
+ url = 'http://downloads.sourceforge.net/project/dtach/dtach/0.8/dtach-0.8.tar.gz'
151
+ open('/tmp/dtach-0.8.tar.gz', 'wb') do |file| file.write(open(url).read) end
152
+ end
153
+
154
+ unless File.directory?('/tmp/dtach-0.8')
155
+ sh 'cd /tmp && tar xzf dtach-0.8.tar.gz'
156
+ end
157
+ end
158
+ end
159
+