reqless 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/README.md +648 -0
  4. data/Rakefile +117 -0
  5. data/bin/docker-build-and-test +22 -0
  6. data/exe/reqless-web +11 -0
  7. data/lib/reqless/config.rb +31 -0
  8. data/lib/reqless/failure_formatter.rb +43 -0
  9. data/lib/reqless/job.rb +496 -0
  10. data/lib/reqless/job_reservers/ordered.rb +29 -0
  11. data/lib/reqless/job_reservers/round_robin.rb +46 -0
  12. data/lib/reqless/job_reservers/shuffled_round_robin.rb +21 -0
  13. data/lib/reqless/lua/reqless-lib.lua +2965 -0
  14. data/lib/reqless/lua/reqless.lua +2545 -0
  15. data/lib/reqless/lua_script.rb +90 -0
  16. data/lib/reqless/middleware/requeue_exceptions.rb +94 -0
  17. data/lib/reqless/middleware/retry_exceptions.rb +72 -0
  18. data/lib/reqless/middleware/sentry.rb +66 -0
  19. data/lib/reqless/middleware/timeout.rb +63 -0
  20. data/lib/reqless/queue.rb +189 -0
  21. data/lib/reqless/queue_priority_pattern.rb +16 -0
  22. data/lib/reqless/server/static/css/bootstrap-responsive.css +686 -0
  23. data/lib/reqless/server/static/css/bootstrap-responsive.min.css +12 -0
  24. data/lib/reqless/server/static/css/bootstrap.css +3991 -0
  25. data/lib/reqless/server/static/css/bootstrap.min.css +689 -0
  26. data/lib/reqless/server/static/css/codemirror.css +112 -0
  27. data/lib/reqless/server/static/css/docs.css +839 -0
  28. data/lib/reqless/server/static/css/jquery.noty.css +105 -0
  29. data/lib/reqless/server/static/css/noty_theme_twitter.css +137 -0
  30. data/lib/reqless/server/static/css/style.css +200 -0
  31. data/lib/reqless/server/static/favicon.ico +0 -0
  32. data/lib/reqless/server/static/img/glyphicons-halflings-white.png +0 -0
  33. data/lib/reqless/server/static/img/glyphicons-halflings.png +0 -0
  34. data/lib/reqless/server/static/js/bootstrap-alert.js +94 -0
  35. data/lib/reqless/server/static/js/bootstrap-scrollspy.js +125 -0
  36. data/lib/reqless/server/static/js/bootstrap-tab.js +130 -0
  37. data/lib/reqless/server/static/js/bootstrap-tooltip.js +270 -0
  38. data/lib/reqless/server/static/js/bootstrap-typeahead.js +285 -0
  39. data/lib/reqless/server/static/js/bootstrap.js +1726 -0
  40. data/lib/reqless/server/static/js/bootstrap.min.js +6 -0
  41. data/lib/reqless/server/static/js/codemirror.js +2972 -0
  42. data/lib/reqless/server/static/js/jquery.noty.js +220 -0
  43. data/lib/reqless/server/static/js/mode/javascript.js +360 -0
  44. data/lib/reqless/server/static/js/theme/cobalt.css +18 -0
  45. data/lib/reqless/server/static/js/theme/eclipse.css +25 -0
  46. data/lib/reqless/server/static/js/theme/elegant.css +10 -0
  47. data/lib/reqless/server/static/js/theme/lesser-dark.css +45 -0
  48. data/lib/reqless/server/static/js/theme/monokai.css +28 -0
  49. data/lib/reqless/server/static/js/theme/neat.css +9 -0
  50. data/lib/reqless/server/static/js/theme/night.css +21 -0
  51. data/lib/reqless/server/static/js/theme/rubyblue.css +21 -0
  52. data/lib/reqless/server/static/js/theme/xq-dark.css +46 -0
  53. data/lib/reqless/server/views/_job.erb +259 -0
  54. data/lib/reqless/server/views/_job_list.erb +8 -0
  55. data/lib/reqless/server/views/_pagination.erb +7 -0
  56. data/lib/reqless/server/views/about.erb +130 -0
  57. data/lib/reqless/server/views/completed.erb +11 -0
  58. data/lib/reqless/server/views/config.erb +14 -0
  59. data/lib/reqless/server/views/failed.erb +48 -0
  60. data/lib/reqless/server/views/failed_type.erb +18 -0
  61. data/lib/reqless/server/views/job.erb +17 -0
  62. data/lib/reqless/server/views/layout.erb +451 -0
  63. data/lib/reqless/server/views/overview.erb +137 -0
  64. data/lib/reqless/server/views/queue.erb +125 -0
  65. data/lib/reqless/server/views/queues.erb +45 -0
  66. data/lib/reqless/server/views/tag.erb +6 -0
  67. data/lib/reqless/server/views/throttles.erb +38 -0
  68. data/lib/reqless/server/views/track.erb +75 -0
  69. data/lib/reqless/server/views/worker.erb +34 -0
  70. data/lib/reqless/server/views/workers.erb +14 -0
  71. data/lib/reqless/server.rb +549 -0
  72. data/lib/reqless/subscriber.rb +74 -0
  73. data/lib/reqless/test_helpers/worker_helpers.rb +55 -0
  74. data/lib/reqless/throttle.rb +57 -0
  75. data/lib/reqless/version.rb +5 -0
  76. data/lib/reqless/worker/base.rb +237 -0
  77. data/lib/reqless/worker/forking.rb +215 -0
  78. data/lib/reqless/worker/serial.rb +41 -0
  79. data/lib/reqless/worker.rb +5 -0
  80. data/lib/reqless.rb +309 -0
  81. 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