qpush 0.1.4 → 0.1.6

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.byebug_history +43 -0
  3. data/README.md +4 -4
  4. data/bin/console +1 -0
  5. data/jobs/fail_job.rb +1 -1
  6. data/jobs/test_job.rb +1 -1
  7. data/lib/qpush.rb +2 -2
  8. data/lib/qpush/base.rb +7 -5
  9. data/lib/qpush/base/config.rb +41 -0
  10. data/lib/qpush/{job.rb → base/job.rb} +4 -23
  11. data/lib/qpush/base/redis.rb +13 -0
  12. data/lib/qpush/client.rb +7 -0
  13. data/lib/qpush/client/config.rb +13 -0
  14. data/lib/qpush/client/job.rb +12 -0
  15. data/lib/qpush/client/redis.rb +16 -0
  16. data/lib/qpush/jobs/queue_delayed.rb +0 -2
  17. data/lib/qpush/server.rb +10 -7
  18. data/lib/qpush/server/apis.rb +10 -0
  19. data/lib/qpush/server/apis/delay.rb +5 -3
  20. data/lib/qpush/server/apis/execute.rb +0 -4
  21. data/lib/qpush/server/apis/fail.rb +2 -2
  22. data/lib/qpush/server/apis/history.rb +3 -3
  23. data/lib/qpush/server/apis/morgue.rb +3 -7
  24. data/lib/qpush/server/apis/perform.rb +3 -7
  25. data/lib/qpush/server/apis/queue.rb +3 -7
  26. data/lib/qpush/server/apis/setup.rb +1 -5
  27. data/lib/qpush/server/apis/success.rb +3 -7
  28. data/lib/qpush/server/config.rb +49 -0
  29. data/lib/qpush/server/database.rb +1 -1
  30. data/lib/qpush/server/delay.rb +4 -4
  31. data/lib/qpush/server/heartbeat.rb +3 -3
  32. data/lib/qpush/server/jobs.rb +12 -1
  33. data/lib/qpush/server/launcher.rb +3 -3
  34. data/lib/qpush/server/loader.rb +7 -7
  35. data/lib/qpush/server/manager.rb +21 -11
  36. data/lib/qpush/server/perform.rb +1 -2
  37. data/lib/qpush/server/queue.rb +1 -1
  38. data/lib/qpush/server/redis.rb +48 -0
  39. data/lib/qpush/server/worker.rb +57 -23
  40. data/lib/qpush/version.rb +1 -1
  41. data/lib/qpush/web/apis/create.rb +16 -0
  42. data/lib/qpush/web/apis/crons.rb +1 -1
  43. data/lib/qpush/web/apis/heart.rb +1 -1
  44. data/lib/qpush/web/apis/history.rb +1 -1
  45. data/lib/qpush/web/apis/jobs.rb +1 -1
  46. data/lib/qpush/web/apis/morgue.rb +1 -1
  47. data/lib/qpush/web/apis/queue_delayed.rb +1 -1
  48. data/lib/qpush/web/apis/retries.rb +1 -1
  49. data/lib/qpush/web/apis/stats.rb +2 -2
  50. data/lib/qpush/web/config.rb +13 -0
  51. data/lib/qpush/web/get.rb +0 -8
  52. data/lib/qpush/web/post.rb +7 -3
  53. data/lib/qpush/web/public/app/shared/navbar/assets/logo.png +0 -0
  54. data/lib/qpush/web/public/assets/data.json +6 -0
  55. data/lib/qpush/web/public/assets/svg/more.svg +7 -0
  56. data/lib/qpush/web/public/css/main.css +1 -0
  57. data/lib/qpush/web/public/fonts/icons.eot +0 -0
  58. data/lib/qpush/web/public/fonts/icons.otf +0 -0
  59. data/lib/qpush/web/public/fonts/icons.svg +685 -0
  60. data/lib/qpush/web/public/fonts/icons.ttf +0 -0
  61. data/lib/qpush/web/public/fonts/icons.woff +0 -0
  62. data/lib/qpush/web/public/fonts/icons.woff2 +0 -0
  63. data/lib/qpush/web/public/index.html +40 -0
  64. data/lib/qpush/web/public/js/app.js +52 -0
  65. data/lib/qpush/web/public/js/shims.js +15 -0
  66. data/lib/qpush/web/public/tsconfig.json +20 -0
  67. data/lib/qpush/web/redis.rb +16 -0
  68. data/lib/qpush/web/server.rb +9 -5
  69. data/qpush.gemspec +1 -0
  70. metadata +42 -5
  71. data/lib/qpush/config.rb +0 -63
  72. data/lib/qpush/redis.rb +0 -53
@@ -2,10 +2,6 @@ module QPush
2
2
  module Server
3
3
  module Apis
4
4
  class Morgue < Base
5
- def initialize(job)
6
- @job = job
7
- end
8
-
9
5
  def call
10
6
  send_to_morgue
11
7
  end
@@ -13,9 +9,9 @@ module QPush
13
9
  private
14
10
 
15
11
  def send_to_morgue
16
- QPush.redis.with do |conn|
17
- conn.hincrby(QPush.keys.stats, 'dead', 1)
18
- conn.lpush(QPush.keys.morgue, @job.to_json)
12
+ Server.redis do |conn|
13
+ conn.hincrby(Server.keys.stats, 'dead', 1)
14
+ conn.lpush(Server.keys.morgue, @job.to_json)
19
15
  end
20
16
  end
21
17
  end
@@ -2,10 +2,6 @@ module QPush
2
2
  module Server
3
3
  module Apis
4
4
  class Perform < Base
5
- def initialize(job)
6
- @job = job
7
- end
8
-
9
5
  def call
10
6
  perform_job
11
7
  end
@@ -13,9 +9,9 @@ module QPush
13
9
  private
14
10
 
15
11
  def perform_job
16
- QPush.redis.with do |conn|
17
- conn.hincrby(QPush.keys.stats, 'performed', 1)
18
- conn.lpush("#{QPush.keys.perform}:#{@job.priority}", @job.to_json)
12
+ Server.redis do |conn|
13
+ conn.hincrby(Server.keys.stats, 'performed', 1)
14
+ conn.lpush("#{Server.keys.perform}:#{@job.priority}", @job.to_json)
19
15
  end
20
16
  end
21
17
  end
@@ -2,10 +2,6 @@ module QPush
2
2
  module Server
3
3
  module Apis
4
4
  class Queue < Base
5
- def initialize(job)
6
- @job = job
7
- end
8
-
9
5
  def call
10
6
  queue_job
11
7
  end
@@ -13,9 +9,9 @@ module QPush
13
9
  private
14
10
 
15
11
  def queue_job
16
- QPush.redis.with do |conn|
17
- conn.hincrby(QPush.keys.stats, 'queued', 1)
18
- conn.lpush(QPush.keys.queue, @job.to_json)
12
+ Server.redis do |conn|
13
+ conn.hincrby(Server.keys.stats, 'queued', 1)
14
+ conn.lpush(Server.keys.queue, @job.to_json)
19
15
  end
20
16
  end
21
17
  end
@@ -2,10 +2,6 @@ module QPush
2
2
  module Server
3
3
  module Apis
4
4
  class Setup < Base
5
- def initialize(job)
6
- @job = job
7
- end
8
-
9
5
  def call
10
6
  invalid_job && return unless @job.valid?
11
7
  setup_job
@@ -19,7 +15,7 @@ module QPush
19
15
  end
20
16
 
21
17
  def invalid_job
22
- Server.log.err("Job INVALID | #{@job.klass} | #{@job.id} | #{@job.errors.full_messages.join(' ')}")
18
+ Server.log.err("Worker #{Server.worker.id} | Job INVALID | #{@job.klass} | #{@job.id} | #{@job.errors.full_messages.join(' ')}")
23
19
  end
24
20
  end
25
21
  end
@@ -2,10 +2,6 @@ module QPush
2
2
  module Server
3
3
  module Apis
4
4
  class Success < Base
5
- def initialize(job)
6
- @job = job
7
- end
8
-
9
5
  def call
10
6
  update_job
11
7
  stat_increment
@@ -21,13 +17,13 @@ module QPush
21
17
  end
22
18
 
23
19
  def stat_increment
24
- QPush.redis.with do |c|
25
- c.hincrby(QPush.keys.stats, 'success', 1)
20
+ Server.redis do |c|
21
+ c.hincrby(Server.keys.stats, 'success', 1)
26
22
  end
27
23
  end
28
24
 
29
25
  def log_success
30
- Server.log.info("Job SUCCESS | #{@job.klass} with ID: #{@job.id} | #{@job.run_time}")
26
+ Server.log.info("Worker #{Server.worker.id} | Job SUCCESS | #{@job.klass} with ID: #{@job.id} | #{@job.run_time}")
31
27
  end
32
28
 
33
29
  def update_history
@@ -0,0 +1,49 @@
1
+ module QPush
2
+ module Server
3
+ include QPush::Base::ConfigHelper
4
+
5
+ class << self
6
+ attr_accessor :worker, :keys
7
+
8
+ def config
9
+ @config ||= Config.new
10
+ end
11
+ end
12
+
13
+ class WorkerConfig
14
+ DEFAULTS = {
15
+ namespace: 'default',
16
+ priorities: 5,
17
+ queue_threads: 2,
18
+ perform_threads: 2,
19
+ delay_threads: 1 }.freeze
20
+
21
+ attr_accessor :perform_threads, :queue_threads, :delay_threads,
22
+ :namespace, :priorities
23
+
24
+ def initialize(options = {})
25
+ options = DEFAULTS.merge(options)
26
+ options.each { |key, value| send("#{key}=", value) }
27
+ end
28
+
29
+ def for_keys
30
+ { namespace: @namespace, priorities: @priorities }
31
+ end
32
+ end
33
+
34
+ class Config < QPush::Base::Config
35
+ SERVER_DEFAULTS = {
36
+ database_url: ENV['DATABASE_URL'],
37
+ database_pool: 10,
38
+ jobs_path: '/jobs',
39
+ workers: [WorkerConfig.new] }.freeze
40
+
41
+ attr_accessor :database_url, :database_pool, :jobs_path, :workers
42
+
43
+ def initialize
44
+ super
45
+ SERVER_DEFAULTS.each { |key, value| send("#{key}=", value) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -7,7 +7,7 @@ module QPush
7
7
 
8
8
  class Database
9
9
  def self.create
10
- Sequel.connect(QPush.config.database_url, max_connections: QPush.config.database_pool)
10
+ Sequel.connect(Server.config.database_url, max_connections: Server.config.database_pool)
11
11
  end
12
12
  end
13
13
  end
@@ -14,7 +14,7 @@ module QPush
14
14
  #
15
15
  def start
16
16
  until @done
17
- QPush.redis.with do |conn|
17
+ Server.redis do |conn|
18
18
  @conn = conn
19
19
  watch_delay { retrieve_delays }
20
20
  end
@@ -34,7 +34,7 @@ module QPush
34
34
  # If any are found, begin to update them.
35
35
  #
36
36
  def retrieve_delays
37
- delays = @conn.zrangebyscore(QPush.keys.delay, 0, Time.now.to_i)
37
+ delays = @conn.zrangebyscore(Server.keys.delay, 0, Time.now.to_i)
38
38
  delays.any? ? update_delays(delays) : @conn.unwatch
39
39
  end
40
40
 
@@ -42,7 +42,7 @@ module QPush
42
42
  #
43
43
  def update_delays(delays)
44
44
  @conn.multi do |multi|
45
- multi.zrem(QPush.keys.delay, delays)
45
+ multi.zrem(Server.keys.delay, delays)
46
46
  delays.each { |job| perform_job(job) }
47
47
  end
48
48
  end
@@ -59,7 +59,7 @@ module QPush
59
59
  # Performs a watch on our delay list
60
60
  #
61
61
  def watch_delay
62
- @conn.watch(QPush.keys.delay) do
62
+ @conn.watch(Server.keys.delay) do
63
63
  yield if block_given?
64
64
  end
65
65
  end
@@ -1,6 +1,6 @@
1
1
  module QPush
2
2
  module Server
3
- # The Heartbeat worker periodically updates the heart namespace.
3
+ # The Heartbeat worker periodically updates the heart key.
4
4
  # The key is set with an expiry. This helps to indicate if the QPush server
5
5
  # is currently active.
6
6
  #
@@ -9,11 +9,11 @@ module QPush
9
9
  @done = false
10
10
  end
11
11
 
12
- # Starts our perform process. This will run until instructed to stop.
12
+ # Starts our heartbeat process. This will run until instructed to stop.
13
13
  #
14
14
  def start
15
15
  until @done
16
- QPush.redis.with { |c| c.setex(QPush.keys.heart, 30, true) }
16
+ Server.redis { |c| c.setex(Server.keys.heart, 30, true) }
17
17
  sleep 15
18
18
  end
19
19
  end
@@ -1,5 +1,15 @@
1
1
  module QPush
2
2
  module Server
3
+ module JobRegister
4
+ def included(base)
5
+ _register_job(base)
6
+ end
7
+
8
+ def _register_job(base)
9
+ Server.redis { |c| c.sadd(QPush::Base::KEY + ':jobs', base.name) }
10
+ end
11
+ end
12
+
3
13
  module JobHelpers
4
14
  def mark_success
5
15
  @failed = false
@@ -46,12 +56,13 @@ module QPush
46
56
  end
47
57
  end
48
58
 
49
- class Job < QPush::Job::Base
59
+ class Job < QPush::Base::Job
50
60
  extend Forwardable
51
61
 
52
62
  include QPush::Server::JobHelpers
53
63
  include ObjectValidator::Validate
54
64
 
65
+
55
66
  def initialize(options)
56
67
  super
57
68
  @api = ApiWrapper.new(self)
@@ -27,7 +27,7 @@ module QPush
27
27
  #
28
28
  def setup_options
29
29
  parser = OptionParser.new do |o|
30
- o.banner = 'Usage: bundle exec bin/QPush [options]'
30
+ o.banner = 'Usage: bundle exec qpush-server [options]'
31
31
 
32
32
  o.on('-c', '--config PATH', 'Load PATH for config file') do |arg|
33
33
  load(arg)
@@ -42,11 +42,11 @@ module QPush
42
42
  # Requires all base jobs as well as user jobs.
43
43
  #
44
44
  def setup_jobs
45
- Loader.call
45
+ JobLoader.call
46
46
  end
47
47
 
48
48
  def boot_manager
49
- manager = Manager.new(QPush.config.manager_options)
49
+ manager = Manager.new(Server.config.workers)
50
50
  manager.start
51
51
  end
52
52
  end
@@ -3,18 +3,18 @@ module QPush
3
3
  # The Loader will 'require' all jobs within the users job folder.
4
4
  # The job folder is specified in the config.
5
5
  #
6
- class Loader
6
+ class JobLoader
7
7
  # Provides a shortend caller.
8
8
  #
9
9
  def self.call
10
- jobs = Loader.new
11
- jobs.call
10
+ loader = new
11
+ loader.call
12
12
  end
13
13
 
14
14
  # Entrypoint to load all jobs.
15
15
  #
16
16
  def call
17
- remove_old
17
+ flush_jobs
18
18
  load_jobs
19
19
  end
20
20
 
@@ -22,14 +22,14 @@ module QPush
22
22
 
23
23
  # Removes old jobs from the redis job list.
24
24
  #
25
- def remove_old
26
- QPush.redis.with { |c| c.del(QPush.keys.jobs) }
25
+ def flush_jobs
26
+ Server.redis { |c| c.del(QPush::Base::KEY + ':jobs') }
27
27
  end
28
28
 
29
29
  # Requires user jobs that are specified from the config.
30
30
  #
31
31
  def load_jobs
32
- Dir[Dir.pwd + "#{QPush.config.jobs_path}/**/*.rb"].each do |file|
32
+ Dir[Dir.pwd + "#{Server.config.jobs_path}/**/*.rb"].each do |file|
33
33
  require file
34
34
  end
35
35
  end
@@ -6,11 +6,11 @@ module QPush
6
6
  class Manager
7
7
  include ObjectValidator::Validate
8
8
 
9
- attr_accessor :workers, :options
9
+ attr_accessor :configs
10
10
  attr_reader :forks
11
11
 
12
- def initialize(options = {})
13
- options.each { |key, value| send("#{key}=", value) }
12
+ def initialize(configs)
13
+ @configs = configs
14
14
  @master = Process.pid
15
15
  @forks = []
16
16
  at_exit { shutdown }
@@ -23,6 +23,7 @@ module QPush
23
23
  def start
24
24
  validate!
25
25
  start_messages
26
+ flush_spaces
26
27
  create_workers
27
28
  Process.wait
28
29
  end
@@ -31,8 +32,9 @@ module QPush
31
32
  #
32
33
  def shutdown
33
34
  unless @forks.empty?
34
- @forks.each { |w| Process.kill('SIGTERM', w[:pid].to_i) }
35
+ @forks.each { |w| Process.kill('QUIT', w[:pid].to_i) }
35
36
  end
37
+ Process.waitall
36
38
  Process.kill('SIGTERM', @master)
37
39
  end
38
40
 
@@ -41,8 +43,8 @@ module QPush
41
43
  # Create the specified number of workers and starts them
42
44
  #
43
45
  def create_workers
44
- @workers.times do |id|
45
- pid = fork { Worker.new(@options.merge(id: id)).start }
46
+ @configs.each_with_index do |config, id|
47
+ pid = fork { Worker.new(id, config).start }
46
48
  @forks << { id: id, pid: pid }
47
49
  end
48
50
  end
@@ -50,8 +52,7 @@ module QPush
50
52
  # Information about the start process
51
53
  #
52
54
  def start_messages
53
- Server.log.info("* Workers: #{@workers}")
54
- Server.log.info("* Threads: #{@options[:queue_threads]} queue, #{@options[:perform_threads]} perform, #{@options[:delay_threads]} delay")
55
+ Server.log.info("* Worker count: #{@configs.count}")
55
56
  end
56
57
 
57
58
  # Validates our data before starting our Workers. Also instantiates our
@@ -61,6 +62,13 @@ module QPush
61
62
  return if valid?
62
63
  fail ServerError, errors.full_messages.join(' ')
63
64
  end
65
+
66
+ # Removes the list of namespaces used by our server from Redis. This
67
+ # prepares it for the new list that will be created by our workers.
68
+ #
69
+ def flush_spaces
70
+ Server.redis { |c| c.del(QPush::Base::KEY + ':namespaces') }
71
+ end
64
72
  end
65
73
 
66
74
  # The ManagerValidator ensures the data for our manager is valid before
@@ -69,10 +77,12 @@ module QPush
69
77
  class ManagerValidator
70
78
  include ObjectValidator::Validator
71
79
 
72
- validates :redis, with: { proc: proc { QPush.redis.with { |c| c.ping && c.quit } },
80
+ validates :redis, with: { proc: proc { Server.redis { |c| c.ping && c.quit } },
73
81
  msg: 'could not be connected with' }
74
- validates :workers, type: Integer, greater_than: 0
75
- validates :options, type: Hash
82
+ validates :configs, with: { proc: proc { |m| m.configs.count > 0 },
83
+ msg: 'were not defined' }
84
+ validates :configs, with: { proc: proc { |m| m.configs.each { |c| c.is_a?(WorkerConfig) } },
85
+ msg: 'are not valid WorkerConfig objects' }
76
86
  end
77
87
  end
78
88
  end
@@ -6,7 +6,6 @@ module QPush
6
6
  class Perform
7
7
  def initialize
8
8
  @done = false
9
- @lists = QPush.keys.perform_lists
10
9
  end
11
10
 
12
11
  # Starts our perform process. This will run until instructed to stop.
@@ -29,7 +28,7 @@ module QPush
29
28
  # Performs a 'blocking pop' on our redis job list.
30
29
  #
31
30
  def retrieve_job
32
- json = QPush.redis.with { |c| c.brpop(@lists) }
31
+ json = Server.redis { |c| c.brpop(Server.keys.perform_list) }
33
32
  Job.new(JSON.parse(json.last))
34
33
  rescue => e
35
34
  raise ServerError, e.message
@@ -29,7 +29,7 @@ module QPush
29
29
  # Performs a 'blocking pop' on our redis job list.
30
30
  #
31
31
  def retrieve_job
32
- json = QPush.redis.with { |c| c.brpop(QPush.keys.queue) }
32
+ json = Server.redis { |c| c.brpop(Server.keys.queue) }
33
33
  Job.new(JSON.parse(json.last))
34
34
  rescue => e
35
35
  raise ServerError, e.message