reqless 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/README.md +648 -0
- data/Rakefile +117 -0
- data/bin/docker-build-and-test +22 -0
- data/exe/reqless-web +11 -0
- data/lib/reqless/config.rb +31 -0
- data/lib/reqless/failure_formatter.rb +43 -0
- data/lib/reqless/job.rb +496 -0
- data/lib/reqless/job_reservers/ordered.rb +29 -0
- data/lib/reqless/job_reservers/round_robin.rb +46 -0
- data/lib/reqless/job_reservers/shuffled_round_robin.rb +21 -0
- data/lib/reqless/lua/reqless-lib.lua +2965 -0
- data/lib/reqless/lua/reqless.lua +2545 -0
- data/lib/reqless/lua_script.rb +90 -0
- data/lib/reqless/middleware/requeue_exceptions.rb +94 -0
- data/lib/reqless/middleware/retry_exceptions.rb +72 -0
- data/lib/reqless/middleware/sentry.rb +66 -0
- data/lib/reqless/middleware/timeout.rb +63 -0
- data/lib/reqless/queue.rb +189 -0
- data/lib/reqless/queue_priority_pattern.rb +16 -0
- data/lib/reqless/server/static/css/bootstrap-responsive.css +686 -0
- data/lib/reqless/server/static/css/bootstrap-responsive.min.css +12 -0
- data/lib/reqless/server/static/css/bootstrap.css +3991 -0
- data/lib/reqless/server/static/css/bootstrap.min.css +689 -0
- data/lib/reqless/server/static/css/codemirror.css +112 -0
- data/lib/reqless/server/static/css/docs.css +839 -0
- data/lib/reqless/server/static/css/jquery.noty.css +105 -0
- data/lib/reqless/server/static/css/noty_theme_twitter.css +137 -0
- data/lib/reqless/server/static/css/style.css +200 -0
- data/lib/reqless/server/static/favicon.ico +0 -0
- data/lib/reqless/server/static/img/glyphicons-halflings-white.png +0 -0
- data/lib/reqless/server/static/img/glyphicons-halflings.png +0 -0
- data/lib/reqless/server/static/js/bootstrap-alert.js +94 -0
- data/lib/reqless/server/static/js/bootstrap-scrollspy.js +125 -0
- data/lib/reqless/server/static/js/bootstrap-tab.js +130 -0
- data/lib/reqless/server/static/js/bootstrap-tooltip.js +270 -0
- data/lib/reqless/server/static/js/bootstrap-typeahead.js +285 -0
- data/lib/reqless/server/static/js/bootstrap.js +1726 -0
- data/lib/reqless/server/static/js/bootstrap.min.js +6 -0
- data/lib/reqless/server/static/js/codemirror.js +2972 -0
- data/lib/reqless/server/static/js/jquery.noty.js +220 -0
- data/lib/reqless/server/static/js/mode/javascript.js +360 -0
- data/lib/reqless/server/static/js/theme/cobalt.css +18 -0
- data/lib/reqless/server/static/js/theme/eclipse.css +25 -0
- data/lib/reqless/server/static/js/theme/elegant.css +10 -0
- data/lib/reqless/server/static/js/theme/lesser-dark.css +45 -0
- data/lib/reqless/server/static/js/theme/monokai.css +28 -0
- data/lib/reqless/server/static/js/theme/neat.css +9 -0
- data/lib/reqless/server/static/js/theme/night.css +21 -0
- data/lib/reqless/server/static/js/theme/rubyblue.css +21 -0
- data/lib/reqless/server/static/js/theme/xq-dark.css +46 -0
- data/lib/reqless/server/views/_job.erb +259 -0
- data/lib/reqless/server/views/_job_list.erb +8 -0
- data/lib/reqless/server/views/_pagination.erb +7 -0
- data/lib/reqless/server/views/about.erb +130 -0
- data/lib/reqless/server/views/completed.erb +11 -0
- data/lib/reqless/server/views/config.erb +14 -0
- data/lib/reqless/server/views/failed.erb +48 -0
- data/lib/reqless/server/views/failed_type.erb +18 -0
- data/lib/reqless/server/views/job.erb +17 -0
- data/lib/reqless/server/views/layout.erb +451 -0
- data/lib/reqless/server/views/overview.erb +137 -0
- data/lib/reqless/server/views/queue.erb +125 -0
- data/lib/reqless/server/views/queues.erb +45 -0
- data/lib/reqless/server/views/tag.erb +6 -0
- data/lib/reqless/server/views/throttles.erb +38 -0
- data/lib/reqless/server/views/track.erb +75 -0
- data/lib/reqless/server/views/worker.erb +34 -0
- data/lib/reqless/server/views/workers.erb +14 -0
- data/lib/reqless/server.rb +549 -0
- data/lib/reqless/subscriber.rb +74 -0
- data/lib/reqless/test_helpers/worker_helpers.rb +55 -0
- data/lib/reqless/throttle.rb +57 -0
- data/lib/reqless/version.rb +5 -0
- data/lib/reqless/worker/base.rb +237 -0
- data/lib/reqless/worker/forking.rb +215 -0
- data/lib/reqless/worker/serial.rb +41 -0
- data/lib/reqless/worker.rb +5 -0
- data/lib/reqless.rb +309 -0
- metadata +399 -0
data/Rakefile
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_helper'
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.rspec_opts = %w[--profile]
|
8
|
+
t.ruby_opts = "-Ispec -rsimplecov_setup"
|
9
|
+
end
|
10
|
+
|
11
|
+
min_coverage_threshold = 85.0
|
12
|
+
desc "Checks the spec coverage and fails if it is less than #{min_coverage_threshold}%"
|
13
|
+
task :check_coverage do
|
14
|
+
percent = File.read("./coverage/coverage_percent.txt").to_f
|
15
|
+
if percent < min_coverage_threshold
|
16
|
+
raise "Spec coverage was not high enough: #{percent.round(2)}%"
|
17
|
+
else
|
18
|
+
puts "Nice job! Spec coverage is still at least #{min_coverage_threshold}%"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
task default: [:spec, :check_coverage]
|
23
|
+
|
24
|
+
namespace :core do
|
25
|
+
reqless_core_dir = "./lib/reqless/reqless-core"
|
26
|
+
|
27
|
+
desc "Builds the reqless-core lua scripts"
|
28
|
+
task :build do
|
29
|
+
Dir.chdir(reqless_core_dir) do
|
30
|
+
sh "make clean && make"
|
31
|
+
sh "cp reqless.lua ../lua"
|
32
|
+
sh "cp reqless-lib.lua ../lua"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
task :update_submodule do
|
37
|
+
Dir.chdir(reqless_core_dir) do
|
38
|
+
sh "git checkout main"
|
39
|
+
sh "git pull --rebase"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Updates reqless-core and rebuilds it"
|
44
|
+
task update: [:update_submodule, :build]
|
45
|
+
|
46
|
+
namespace :verify do
|
47
|
+
script_files = %w[ lib/reqless/lua/reqless.lua lib/reqless/lua/reqless-lib.lua ]
|
48
|
+
|
49
|
+
desc "Verifies the script has no uncommitted changes"
|
50
|
+
task :clean do
|
51
|
+
script_files.each do |file|
|
52
|
+
git_status = `git status -- #{file}`
|
53
|
+
unless /working directory clean/.match(git_status)
|
54
|
+
raise "#{file} is dirty: \n\n#{git_status}\n\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Verifies the script is current"
|
60
|
+
task :current do
|
61
|
+
require 'digest/md5'
|
62
|
+
our_md5s = script_files.map do |file|
|
63
|
+
Digest::MD5.hexdigest(File.read file)
|
64
|
+
end
|
65
|
+
|
66
|
+
canonical_md5s = Dir.chdir(reqless_core_dir) do
|
67
|
+
sh "make clean && make"
|
68
|
+
script_files.map do |file|
|
69
|
+
Digest::MD5.hexdigest(File.read(File.basename file))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
unless our_md5s == canonical_md5s
|
74
|
+
raise "The current scripts are out of date with reqless-core"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "Verifies the committed script is current"
|
80
|
+
task verify: %w[ verify:clean verify:current ]
|
81
|
+
end
|
82
|
+
|
83
|
+
desc "Starts a reqless console"
|
84
|
+
task :console do
|
85
|
+
ENV['PUBLIC_SEQUEL_API'] = 'true'
|
86
|
+
ENV['NO_NEW_RELIC'] = 'true'
|
87
|
+
exec "bundle exec pry -r./conf/console"
|
88
|
+
end
|
89
|
+
|
90
|
+
namespace :reqless do
|
91
|
+
desc "Runs a test worker so you can send signals to it for testing"
|
92
|
+
task :run_test_worker do
|
93
|
+
require 'reqless'
|
94
|
+
require 'reqless/job_reservers/ordered'
|
95
|
+
require 'reqless/worker'
|
96
|
+
queue = Reqless::Client.new.queues["example"]
|
97
|
+
queue.client.redis.flushdb
|
98
|
+
|
99
|
+
ENV['VVERBOSE'] = '1'
|
100
|
+
|
101
|
+
class ExampleJob
|
102
|
+
def self.perform(job)
|
103
|
+
sleep_time = job.data.fetch("sleep")
|
104
|
+
print "Sleeping for #{sleep_time}..."
|
105
|
+
sleep sleep_time
|
106
|
+
puts "done"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
20.times do |i|
|
111
|
+
queue.put(ExampleJob, sleep: i)
|
112
|
+
end
|
113
|
+
|
114
|
+
reserver = Reqless::JobReservers::Ordered.new([queue])
|
115
|
+
Reqless::Workers::ForkingWorker.new(reserver, log_level: Logger::INFO).run
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e -o pipefail
|
4
|
+
|
5
|
+
# From https://stackoverflow.com/a/4774063
|
6
|
+
REPO_DIR="$( cd -- "$(dirname "$0")/.." >/dev/null 2>&1 ; pwd -P )"
|
7
|
+
|
8
|
+
COMMAND="$@"
|
9
|
+
if [ -z "$COMMAND" ]; then
|
10
|
+
COMMAND="bundle exec rspec"
|
11
|
+
fi
|
12
|
+
|
13
|
+
test -f "$REPO_DIR/Gemfile.lock" && rm -f "$REPO_DIR/Gemfile.lock"
|
14
|
+
redis-cli flushdb
|
15
|
+
docker build -t reqless-rb "$REPO_DIR"
|
16
|
+
docker run \
|
17
|
+
-it \
|
18
|
+
--publish 4000:4000 \
|
19
|
+
--rm \
|
20
|
+
--volume $REPO_DIR:/app \
|
21
|
+
reqless-rb \
|
22
|
+
$COMMAND
|
data/exe/reqless-web
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
require "rackup"
|
6
|
+
|
7
|
+
rackup_file = File.realpath(File.dirname(__FILE__) + '/../config.ru')
|
8
|
+
|
9
|
+
ARGV << rackup_file
|
10
|
+
server = Rackup::Server.new
|
11
|
+
server.start
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Reqless
|
6
|
+
# A configuration class associated with a reqless client
|
7
|
+
class Config
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
@client.call('config.get', key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(key, value)
|
17
|
+
@client.call('config.set', key, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get the specified `reqless` configuration option, or if
|
21
|
+
# none is provided, get the complete current configuration
|
22
|
+
def all
|
23
|
+
JSON.parse(@client.call('config.getAll'))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Restore this option to the default (remove this option)
|
27
|
+
def clear(option)
|
28
|
+
@client.call('config.unset', option)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
3
|
+
module Reqless
|
4
|
+
# A helper for formatting failure messages
|
5
|
+
class FailureFormatter
|
6
|
+
Failure = Struct.new(:group, :message) do
|
7
|
+
# allow de-structring assignment
|
8
|
+
def to_ary
|
9
|
+
[group, message]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@replacements = { Dir.pwd => '.' }
|
15
|
+
@replacements[ENV['GEM_HOME']] = '<GEM_HOME>' if ENV.key?('GEM_HOME')
|
16
|
+
end
|
17
|
+
|
18
|
+
def format(job, error, lines_to_remove = caller(2))
|
19
|
+
group = "#{job.klass_name}:#{error.class}"
|
20
|
+
message = "#{truncated_message(error)}\n\n" +
|
21
|
+
"#{format_failure_backtrace(error.backtrace, lines_to_remove)}"
|
22
|
+
Failure.new(group, message)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# TODO: pull this out into a config option.
|
28
|
+
MAX_ERROR_MESSAGE_SIZE = 10_000
|
29
|
+
def truncated_message(error)
|
30
|
+
return error.message if error.message.length <= MAX_ERROR_MESSAGE_SIZE
|
31
|
+
error.message.slice(0, MAX_ERROR_MESSAGE_SIZE) +
|
32
|
+
"\n... (truncated due to length)"
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_failure_backtrace(error_backtrace, lines_to_remove)
|
36
|
+
(error_backtrace - lines_to_remove).map do |line|
|
37
|
+
@replacements.reduce(line) do |formatted, (original, new)|
|
38
|
+
formatted.sub(original, new)
|
39
|
+
end
|
40
|
+
end.join("\n")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|