workerholic 0.0.15 → 0.0.16

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.
@@ -1,7 +1,7 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  describe 'dequeuing and processesing of jobs' do
4
- let(:redis) { Redis.new }
4
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
5
5
 
6
6
  xit 'successfully dequeues and process a simple job' do
7
7
  serialized_job = Workerholic::JobSerializer.serialize(
@@ -1,15 +1,20 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  describe 'enqueuing jobs to Redis' do
4
- let(:redis) { Redis.new }
4
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
5
5
 
6
6
  context 'successfully creates a job and enqueues it in Redis' do
7
7
  it 'enqueues a simple job in redis' do
8
8
  SimpleJobTest.new.perform_async('test job')
9
- serialized_job = redis.lpop(TEST_QUEUE)
9
+ serialized_job = redis.lpop(WORKERHOLIC_QUEUE_NAMESPACE + TEST_QUEUE)
10
10
  job_from_redis = Workerholic::JobSerializer.deserialize(serialized_job)
11
11
 
12
- expected_job = Workerholic::JobWrapper.new(klass: SimpleJobTest, arguments: ['test job'], wrapper: SimpleJobTest)
12
+ expected_job = Workerholic::JobWrapper.new(
13
+ klass: SimpleJobTest,
14
+ arguments: ['test job'],
15
+ wrapper: SimpleJobTest,
16
+ queue: WORKERHOLIC_QUEUE_NAMESPACE + TEST_QUEUE
17
+ )
13
18
  expected_job.statistics.enqueued_at = job_from_redis.statistics.enqueued_at
14
19
 
15
20
  expect(job_from_redis.to_hash).to eq(expected_job.to_hash)
@@ -17,13 +22,14 @@ describe 'enqueuing jobs to Redis' do
17
22
 
18
23
  it 'enqueues a complex job in redis' do
19
24
  ComplexJobTest.new.perform_async('test job', { a: 1, b: 2 }, [1, 2, 3])
20
- serialized_job = redis.lpop(TEST_QUEUE)
25
+ serialized_job = redis.lpop(WORKERHOLIC_QUEUE_NAMESPACE + TEST_QUEUE)
21
26
  job_from_redis = Workerholic::JobSerializer.deserialize(serialized_job)
22
27
 
23
28
  expected_job = Workerholic::JobWrapper.new(
24
29
  klass: ComplexJobTest,
25
30
  arguments: ['test job', { a: 1, b: 2 }, [1, 2, 3]],
26
- wrapper: ComplexJobTest
31
+ wrapper: ComplexJobTest,
32
+ queue: WORKERHOLIC_QUEUE_NAMESPACE + TEST_QUEUE
27
33
  )
28
34
 
29
35
  expected_job.statistics.enqueued_at = job_from_redis.statistics.enqueued_at
@@ -31,20 +37,27 @@ describe 'enqueuing jobs to Redis' do
31
37
  expect(job_from_redis.to_hash).to eq(expected_job.to_hash)
32
38
  end
33
39
 
34
- xit 'enqueues a delayed job in redis' do
40
+ it 'enqueues a delayed job in redis' do
35
41
  DelayedJobTest.new.perform_delayed(100, 'test job')
36
- serialized_job = redis.lpop(TEST_QUEUE)
42
+
43
+ serialized_job, execution_time = redis.zrange('workerholic:scheduled_jobs', 0, 0, with_scores: true).first
37
44
  job_from_redis = Workerholic::JobSerializer.deserialize(serialized_job)
38
45
 
39
- expected_job = Workerholic::JobWrapper.new(klass: SimpleJobTest, arguments: ['test job'], wrapper: SimpleJobTest)
46
+ expected_job = Workerholic::JobWrapper.new(
47
+ klass: DelayedJobTest,
48
+ arguments: ['test job'],
49
+ wrapper: DelayedJobTest,
50
+ queue: 'workerholic:queue:main'
51
+ )
40
52
  expected_job.statistics.enqueued_at = job_from_redis.statistics.enqueued_at
41
53
 
42
54
  expect(job_from_redis.to_hash).to eq(expected_job.to_hash)
55
+ expect(Time.now.to_i + 100).to eq(execution_time.to_i)
43
56
  end
44
57
 
45
58
  it 'enqueues a job with the right statistics' do
46
59
  SimpleJobTest.new.perform_async('test_job')
47
- serialized_job = redis.lpop(TEST_QUEUE)
60
+ serialized_job = redis.lpop(WORKERHOLIC_QUEUE_NAMESPACE + TEST_QUEUE)
48
61
  job_from_redis = Workerholic::JobSerializer.deserialize(serialized_job)
49
62
 
50
63
  expect(job_from_redis.statistics.enqueued_at).to be < Time.now.to_f
@@ -7,7 +7,7 @@ class JobWithError
7
7
  end
8
8
 
9
9
  describe Workerholic::JobRetry do
10
- let(:redis) { Redis.new }
10
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
11
11
 
12
12
  it 'increments retry count' do
13
13
  job = Workerholic::JobWrapper.new(class: JobWithError, arguments: [])
@@ -1,47 +1,59 @@
1
1
  require_relative 'spec_helper'
2
2
 
3
3
  describe Workerholic::JobScheduler do
4
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
4
5
  let(:scheduler) do
5
6
  Workerholic::JobScheduler.new(
6
- sorted_set: Workerholic::SortedSet.new(TEST_SCHEDULED_SORTED_SET),
7
- queue_name: TEST_QUEUE
7
+ sorted_set: Workerholic::SortedSet.new(TEST_SCHEDULED_SORTED_SET)
8
8
  )
9
9
  end
10
+ let(:serialized_job) do
11
+ job = Workerholic::JobWrapper.new(
12
+ class: ComplexJobTest,
13
+ arguments: ['test job', { a: 1, b: 2 }, [1, 2, 3]],
14
+ queue: Workerholic::Queue.new.name
15
+ )
10
16
 
11
- let(:redis) { Redis.new }
17
+ Workerholic::JobSerializer.serialize(job)
18
+ end
12
19
 
13
- context 'with non-empty set' do
14
- let(:serialized_job) do
15
- job = Workerholic::JobWrapper.new(
16
- class: ComplexJobTest,
17
- arguments: ['test job', { a: 1, b: 2 }, [1, 2, 3]]
18
- )
20
+ it 'checks the time for scheduled job inside sorted set' do
21
+ score = Time.now.to_f
22
+ scheduler.schedule(serialized_job, score)
19
23
 
20
- Workerholic::JobSerializer.serialize(job)
21
- end
24
+ expect(scheduler.job_due?).to eq(true)
25
+ end
22
26
 
23
- it 'checks the time for scheduled job inside sorted set' do
24
- score = Time.now.to_f
25
- scheduler.schedule(serialized_job, score)
27
+ it 'fetches a job from a sorted set' do
28
+ score = Time.now.to_f
29
+ scheduler.schedule(serialized_job, score)
30
+ scheduler.enqueue_due_jobs
26
31
 
27
- expect(scheduler.job_due?).to eq(true)
28
- end
32
+ expect(scheduler.sorted_set.empty?).to eq(true)
33
+ end
29
34
 
30
- it 'fetches a job from a sorted set' do
31
- score = Time.now.to_f
32
- scheduler.schedule(serialized_job, score)
33
- scheduler.enqueue_due_jobs
35
+ it 'enqueues due job to the main queue by default' do
36
+ score = Time.now.to_f
37
+ scheduler.schedule(serialized_job, score)
38
+ scheduler.enqueue_due_jobs
34
39
 
35
- expect(scheduler.sorted_set.empty?).to eq(true)
36
- end
40
+ expect(Workerholic::Queue.new.empty?).to eq(false)
41
+ expect(Workerholic::Queue.new.dequeue).to eq(serialized_job)
42
+ end
43
+
44
+ it 'enqueues due job in specific queue' do
45
+ job = Workerholic::JobWrapper.new(
46
+ class: ComplexJobTest,
47
+ arguments: ['test job', { a: 1, b: 2 }, [1, 2, 3]],
48
+ queue: TEST_QUEUE
49
+ )
50
+ serialized_job = Workerholic::JobSerializer.serialize(job)
51
+ score = Time.now.to_f
37
52
 
38
- it 'enqueues due job to the main queue' do
39
- score = Time.now.to_f
40
- scheduler.schedule(serialized_job, score)
41
- scheduler.enqueue_due_jobs
53
+ scheduler.schedule(serialized_job, score)
54
+ scheduler.enqueue_due_jobs
42
55
 
43
- expect(scheduler.queue.empty?).to eq(false)
44
- expect(scheduler.queue.dequeue).to eq(serialized_job)
45
- end
56
+ expect(Workerholic::Queue.new(TEST_QUEUE).empty?).to eq(false)
57
+ expect(Workerholic::Queue.new(TEST_QUEUE).dequeue).to eq(serialized_job)
46
58
  end
47
59
  end
@@ -2,12 +2,13 @@ require_relative 'spec_helper'
2
2
 
3
3
  describe Workerholic::JobWrapper do
4
4
  it 'returns a hash with job meta info and job stats info' do
5
- job = Workerholic::JobWrapper.new(klass: SimpleJobTest, arguments: ['test job'])
5
+ job = Workerholic::JobWrapper.new(klass: SimpleJobTest, arguments: ['test job'], queue: TEST_QUEUE)
6
6
 
7
7
  expected_result = {
8
8
  klass: SimpleJobTest,
9
9
  wrapper: nil,
10
10
  arguments: ['test job'],
11
+ queue: TEST_QUEUE,
11
12
  retry_count: 0,
12
13
  execute_at: nil,
13
14
  statistics: {
data/spec/queue_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require_relative 'spec_helper'
2
2
 
3
3
  describe Workerholic::Queue do
4
- let(:redis) { Redis.new }
4
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
5
5
  let(:queue) { Workerholic::Queue.new(TEST_QUEUE) }
6
6
  let(:job) { 'test job' }
7
7
 
@@ -2,7 +2,7 @@ require_relative 'spec_helper'
2
2
 
3
3
  describe Workerholic::SortedSet do
4
4
  let(:job) {{ class: SimpleJobTest, arguments: [] }}
5
- let(:redis) { Redis.new }
5
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
6
6
  let(:sorted_set) { Workerholic::SortedSet.new(TEST_SCHEDULED_SORTED_SET) }
7
7
 
8
8
  it 'adds a serialized job to the sorted set' do
data/spec/spec_helper.rb CHANGED
@@ -20,10 +20,6 @@ RSpec.configure do |config|
20
20
  config.shared_context_metadata_behavior = :apply_to_host_groups
21
21
 
22
22
  config.before do
23
- Redis.new.del(TEST_QUEUE, ANOTHER_TEST_QUEUE, BALANCER_TEST_QUEUE, ANOTHER_BALANCER_TEST_QUEUE, TEST_SCHEDULED_SORTED_SET, HASH_TEST)
24
- end
25
-
26
- config.after do
27
- Redis.new.del(TEST_QUEUE, ANOTHER_TEST_QUEUE, BALANCER_TEST_QUEUE, ANOTHER_BALANCER_TEST_QUEUE, TEST_SCHEDULED_SORTED_SET, HASH_TEST)
23
+ Redis.new(url: Workerholic::REDIS_URL).flushdb
28
24
  end
29
25
  end
@@ -0,0 +1,73 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Workerholic::StatsAPI do
4
+ let(:storage) { Workerholic::Storage::RedisWrapper.new }
5
+ let(:job) { Workerholic::JobWrapper.new(klass: SimpleJobTest, arguments: ['test job']) }
6
+
7
+ it 'returns full statistics for category' do
8
+ namespace = 'workerholic:stats:completed_jobs:*'
9
+
10
+ Workerholic::StatsStorage.save_job('completed_jobs', job)
11
+
12
+ jobs_classes = storage.get_keys_for_namespace(namespace)
13
+
14
+ serialized_job = storage.sorted_set_all_members(jobs_classes.first)
15
+ deserialized_job = Workerholic::JobSerializer.deserialize_stats(serialized_job.first)
16
+
17
+ stats_api_result = Workerholic::StatsAPI.job_statistics(category: 'completed_jobs')
18
+ first_job_stat = stats_api_result.first[0]
19
+
20
+ expect(stats_api_result.size).to eq 1
21
+ expect(first_job_stat).to eq deserialized_job
22
+ end
23
+
24
+ it 'returns jobs count statistics for category' do
25
+ Workerholic::StatsStorage.save_job('completed_jobs', job)
26
+
27
+ stats_api_result = Workerholic::StatsAPI.job_statistics(category: 'completed_jobs', count_only: true)
28
+ stat_counter = stats_api_result.first
29
+
30
+ expect(stat_counter).to eq [job.klass.to_s, 1]
31
+ end
32
+
33
+ context 'with scheduled job' do
34
+ let(:namespace) { 'workerholic:scheduled_jobs' }
35
+
36
+ before do
37
+ sorted_set = Workerholic::SortedSet.new(namespace)
38
+
39
+ job.execute_at = Time.now.to_f + 10
40
+ job_hash = job.to_hash
41
+ job_hash[:klass] = job.klass.to_s
42
+ job_hash[:wrapper] = nil
43
+
44
+ sorted_set.add(Workerholic::JobSerializer.serialize(job_hash), job_hash[:execute_at])
45
+ end
46
+
47
+ it 'returns scheduled jobs statistics' do
48
+ serialized_job = storage.sorted_set_all_members(namespace).first
49
+ deserialized_job = Workerholic::JobSerializer.deserialize_stats(serialized_job)
50
+
51
+ stats_api_result = Workerholic::StatsAPI.scheduled_jobs
52
+
53
+ first_scheduled_job = stats_api_result.first
54
+
55
+ expect(first_scheduled_job).to eq deserialized_job
56
+ end
57
+
58
+ it 'returns scheduled jobs count' do
59
+ stats_api_result = Workerholic::StatsAPI.scheduled_jobs(count_only: true)
60
+
61
+ expect(stats_api_result).to eq 1
62
+ end
63
+ end
64
+
65
+ it 'returns historical statistics for namespace' do
66
+ category = 'completed_jobs'
67
+ Workerholic::StatsStorage.update_historical_stats(category, job.klass.to_s)
68
+
69
+ history_hash = Workerholic::StatsAPI.history_for_period(category: category, period: 1)
70
+
71
+ expect(history_hash[:job_counts]).to match [1, 0]
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Workerholic::StatsStorage do
4
+ let(:storage) { Workerholic::Storage::RedisWrapper.new }
5
+ let(:stats_namespace) { 'workerholic:stats' }
6
+
7
+ it 'saves job stats' do
8
+ job = Workerholic::JobWrapper.new(klass: SimpleJobTest, arguments: ['test job'])
9
+
10
+ job_hash = job.to_hash
11
+ job_hash[:klass] = job_hash[:klass].to_s
12
+ job_hash[:wrapper] = nil
13
+
14
+ local_namespace = ":completed_jobs:#{job.klass.to_s}"
15
+ namespace = stats_namespace + local_namespace
16
+
17
+ Workerholic::StatsStorage.save_job('completed_jobs', job)
18
+
19
+ serialized_stats = storage.sorted_set_all_members(namespace).first
20
+ deserialized_stats = Workerholic::JobSerializer.deserialize_stats(serialized_stats)
21
+
22
+ expect(storage.sorted_set_size(namespace)).to eq 1
23
+ expect(deserialized_stats).to eq job_hash
24
+ end
25
+
26
+ it 'saves process memory usage' do
27
+ Workerholic::StatsStorage.save_processes_memory_usage
28
+
29
+ namespace = stats_namespace + ':memory:processes'
30
+ process_data = storage.hash_get_all(namespace)
31
+
32
+ expect(storage.hash_get(namespace, process_data.keys.first)).to eq process_data.values.first
33
+ end
34
+
35
+ it 'cleans previous metrics records' do
36
+ Workerholic::StatsStorage.delete_memory_stats
37
+
38
+ namespace = stats_namespace + ':memory:processes'
39
+
40
+ expect(storage.hash_get_all(namespace).empty?).to be true
41
+ end
42
+ end
data/spec/storage_spec.rb CHANGED
@@ -2,7 +2,7 @@ require_relative 'spec_helper'
2
2
 
3
3
  describe Workerholic::Storage do
4
4
  let(:storage) { Workerholic::Storage::RedisWrapper.new }
5
- let(:redis) { Redis.new }
5
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
6
6
  let(:queue_name) { TEST_QUEUE }
7
7
  let(:job) { 'test job' }
8
8
 
@@ -63,10 +63,10 @@ describe Workerholic::Storage do
63
63
  end
64
64
 
65
65
  it 'returns the workerholic queue names that are in redis' do
66
- storage.push(queue_name, job)
67
- storage.push(ANOTHER_TEST_QUEUE, job)
66
+ storage.push(WORKERHOLIC_QUEUE_NAMESPACE + queue_name, job)
67
+ storage.push(WORKERHOLIC_QUEUE_NAMESPACE + ANOTHER_TEST_QUEUE, job)
68
68
 
69
- expect(storage.fetch_queue_names).to match_array([queue_name, ANOTHER_TEST_QUEUE])
69
+ expect(storage.fetch_queue_names).to match_array([WORKERHOLIC_QUEUE_NAMESPACE + queue_name, WORKERHOLIC_QUEUE_NAMESPACE + ANOTHER_TEST_QUEUE])
70
70
  end
71
71
 
72
72
  it 'sets k and a value to a hash in redis' do
@@ -1,10 +1,13 @@
1
1
  require_relative 'spec_helper'
2
2
 
3
- TESTED_QUEUES = [BALANCER_TEST_QUEUE, ANOTHER_BALANCER_TEST_QUEUE]
3
+ TESTED_QUEUES = [
4
+ WORKERHOLIC_QUEUE_NAMESPACE + BALANCER_TEST_QUEUE,
5
+ WORKERHOLIC_QUEUE_NAMESPACE + ANOTHER_BALANCER_TEST_QUEUE
6
+ ]
4
7
 
5
8
  describe Workerholic::WorkerBalancer do
6
9
  let(:storage) { Workerholic::Storage::RedisWrapper.new }
7
- let(:redis) { Redis.new }
10
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
8
11
 
9
12
  before do
10
13
  100.times do |n|
data/spec/worker_spec.rb CHANGED
@@ -17,7 +17,7 @@ class WorkerJobTest
17
17
  end
18
18
 
19
19
  describe Workerholic::Worker do
20
- let(:redis) { Redis.new }
20
+ let(:redis) { Redis.new(url: Workerholic::REDIS_URL) }
21
21
  let(:job) do
22
22
  {
23
23
  klass: WorkerJobTest,
data/web/application.rb CHANGED
@@ -1,6 +1,7 @@
1
+ $LOAD_PATH << __dir__ + '/../lib'
1
2
  require 'sinatra/base'
2
3
 
3
- # require 'sinatra/reloader'
4
+ require 'sinatra/reloader'
4
5
  require 'json'
5
6
  require 'workerholic'
6
7
 
@@ -11,6 +12,8 @@ class WorkerholicWeb < Sinatra::Base
11
12
  end
12
13
 
13
14
  get '/overview' do
15
+ @processes = Workerholic::StatsAPI.process_stats
16
+
14
17
  erb :overview
15
18
  end
16
19
 
@@ -49,13 +52,22 @@ class WorkerholicWeb < Sinatra::Base
49
52
  erb :queues
50
53
  end
51
54
 
55
+ get '/history' do
56
+ @days = params[:days]
57
+ @classes = Workerholic::StatsAPI.jobs_classes(true)
58
+ @class = params[:class] || 'completed'
59
+
60
+ erb :history
61
+ end
62
+
52
63
  get '/overview-data' do
53
64
  JSON.generate({
54
65
  completed_jobs: Workerholic::StatsAPI.job_statistics( {category: 'completed_jobs', count_only: true} ),
55
66
  failed_jobs: Workerholic::StatsAPI.job_statistics( {category: 'failed_jobs', count_only: true} ),
56
67
  queued_jobs: Workerholic::StatsAPI.queued_jobs,
68
+ scheduled_jobs: Workerholic::StatsAPI.scheduled_jobs( { count_only: true }),
57
69
  workers_count: Workerholic.workers_count,
58
- # memory_usage: Workerholic::StatsAPI.memory_usage
70
+ memory_usage: Workerholic::StatsAPI.process_stats,
59
71
  })
60
72
  end
61
73
 
@@ -72,4 +84,13 @@ class WorkerholicWeb < Sinatra::Base
72
84
  })
73
85
  end
74
86
 
87
+ get '/historic-data' do
88
+ puts params[:className]
89
+ params[:className] = nil if params[:className] == 'completed'
90
+
91
+ JSON.generate({
92
+ completed_jobs: Workerholic::StatsAPI.history_for_period({ category: 'completed_jobs', klass: params[:className], period: params[:days].to_i }),
93
+ failed_jobs: Workerholic::StatsAPI.history_for_period({ category: 'failed_jobs', klass: params[:className], period: params[:days].to_i })
94
+ })
95
+ end
75
96
  end