canvas_sync 0.18.10 → 0.19.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -9
  3. data/lib/canvas_sync/concerns/ability_helper.rb +19 -11
  4. data/lib/canvas_sync/job_batches/batch.rb +47 -2
  5. data/lib/canvas_sync/job_batches/callback.rb +7 -4
  6. data/lib/canvas_sync/job_batches/chain_builder.rb +38 -74
  7. data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +5 -5
  8. data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +54 -22
  9. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +5 -5
  10. data/lib/canvas_sync/job_batches/pool.rb +83 -54
  11. data/lib/canvas_sync/job_batches/pool_refill.lua +40 -0
  12. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/css/styles.less +6 -2
  13. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/js/batch_tree.js +3 -1
  14. data/lib/canvas_sync/job_batches/sidekiq/web/views/_batches_table.erb +3 -1
  15. data/lib/canvas_sync/job_batches/sidekiq/web/views/_jobs_table.erb +2 -0
  16. data/lib/canvas_sync/job_batches/sidekiq/web/views/pool.erb +10 -1
  17. data/lib/canvas_sync/job_batches/sidekiq/web.rb +5 -1
  18. data/lib/canvas_sync/job_batches/status.rb +4 -0
  19. data/lib/canvas_sync/jobs/canvas_process_waiter.rb +3 -35
  20. data/lib/canvas_sync/misc_helper.rb +48 -0
  21. data/lib/canvas_sync/version.rb +1 -1
  22. data/lib/canvas_sync.rb +3 -10
  23. data/spec/canvas_sync/canvas_sync_spec.rb +201 -115
  24. data/spec/canvas_sync/jobs/canvas_process_waiter_spec.rb +0 -48
  25. data/spec/canvas_sync/misc_helper_spec.rb +58 -0
  26. data/spec/dummy/log/test.log +69092 -0
  27. data/spec/job_batching/pool_spec.rb +161 -0
  28. data/spec/job_batching/support/base_job.rb +1 -1
  29. metadata +9 -5
  30. data/lib/canvas_sync/job_batches/hincr_max.lua +0 -5
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobBatches::Pool do
4
+ include ActiveJob::TestHelper
5
+
6
+ subject { described_class.new(order: pool_order, concurrency: pool_concurrency) }
7
+
8
+ let(:pool) { subject }
9
+
10
+ let(:pool_order) { :fifo }
11
+ let(:pool_concurrency) { 2 }
12
+
13
+ describe '#initialize' do
14
+ subject { described_class }
15
+
16
+ it 'creates pid when called without it' do
17
+ expect(subject.new.pid).not_to be_nil
18
+ end
19
+
20
+ it 'reuses pid when called with it' do
21
+ batch = subject.new('dayPO5KxuRXXxw')
22
+ expect(batch.pid).to eq('dayPO5KxuRXXxw')
23
+ end
24
+ end
25
+
26
+ def load_pool(count = 3)
27
+ jobs = count.times.map do |i|
28
+ { job: "BatchTestJobBase", args: [1] }
29
+ end
30
+ subject.add_jobs(jobs, skip_refill: true)
31
+ end
32
+
33
+ describe "#job_checked_in" do
34
+ it "gets called" do
35
+ expect(CanvasSync::JobBatches::Pool).to receive(:job_checked_in).twice
36
+ load_pool
37
+ perform_enqueued_jobs do
38
+ pool.send :refill_allotment
39
+ Sidekiq::Worker.drain_all
40
+ end
41
+ end
42
+
43
+ it "refills the pool" do
44
+ expect(CanvasSync::JobBatches::Pool).to receive(:job_checked_in).and_call_original.exactly(3).times
45
+
46
+ load_pool
47
+ perform_enqueued_jobs do
48
+ pool.send :refill_allotment
49
+ Sidekiq::Worker.drain_all
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#cleanup_if_empty" do
55
+ it "cleans if pool is empty and allowed to close" do
56
+ expect(pool).to receive(:cleanup_redis)
57
+ pool.cleanup_if_empty
58
+ end
59
+
60
+ it "doesn't clean if pool has pending" do
61
+ load_pool
62
+ expect(pool).to_not receive(:cleanup_redis)
63
+ pool.cleanup_if_empty
64
+ end
65
+
66
+ it "doesn't clean if pool has active" do
67
+ subject.redis.sadd("#{subject.send(:redis_key)}-active", "blocked")
68
+ expect(pool).to_not receive(:cleanup_redis)
69
+ pool.cleanup_if_empty
70
+ end
71
+
72
+ it "doesn't clean if pool has activating" do
73
+ subject.redis.hincrby(subject.send(:redis_key), "_active_count", 1)
74
+ expect(pool).to_not receive(:cleanup_redis)
75
+ pool.cleanup_if_empty
76
+ end
77
+
78
+ it "doesn't clean if pool is empty but is kept open" do
79
+ pool.keep_open!
80
+ expect(pool).to_not receive(:cleanup_redis)
81
+ pool.cleanup_if_empty
82
+ end
83
+
84
+ it "doesn't clean if pool is empty but clean_when_empty is false" do
85
+ subject.redis.hset(subject.send(:redis_key), "clean_when_empty", "false")
86
+ expect(pool).to_not receive(:cleanup_redis)
87
+ pool.cleanup_if_empty
88
+ end
89
+ end
90
+
91
+ shared_examples "basic pool tests" do
92
+ describe "#push_job_to_pool" do
93
+ it "adds a job to the pool" do
94
+ subject.send(:push_job_to_pool, { job: 'job1' })
95
+ expect(subject.pending_count).to eq(1)
96
+ end
97
+ end
98
+
99
+ describe "#refill_allotment" do
100
+ it "refills the pool with jobs" do
101
+ load_pool
102
+ expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).twice
103
+ subject.send(:refill_allotment)
104
+ end
105
+
106
+ it "limits to the concurrency count" do
107
+ load_pool
108
+ expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).twice
109
+ expect(subject.send(:refill_allotment)).to eql 2
110
+ expect(subject.pending_count).to eql 1
111
+ end
112
+
113
+ it "considers already active jobs against concurrency" do
114
+ load_pool
115
+ subject.redis.sadd("#{subject.send(:redis_key)}-active", "blocked")
116
+ expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).once
117
+ expect(subject.send(:refill_allotment)).to eql 2
118
+ expect(subject.pending_count).to eql 2
119
+ end
120
+
121
+ it "considers activating jobs against concurrency" do
122
+ load_pool
123
+ subject.redis.hincrby(subject.send(:redis_key), "_active_count", 1)
124
+ expect(CanvasSync::JobBatches::ChainBuilder).to receive(:enqueue_job).once
125
+ expect(subject.send(:refill_allotment)).to eql 2
126
+ expect(subject.pending_count).to eql 2
127
+ end
128
+
129
+ it "doesn't fail if the pool is gone" do
130
+ load_pool
131
+ subject.cleanup_redis
132
+ expect(CanvasSync::JobBatches::ChainBuilder).not_to receive(:enqueue_job)
133
+ expect(subject.send(:refill_allotment)).to eql -1
134
+ end
135
+
136
+ it "doesn't fail if the pool is empty" do
137
+ expect(subject.send(:refill_allotment)).to eql 0
138
+ end
139
+ end
140
+ end
141
+
142
+ context "FIFO Pool" do
143
+ let(:pool_order) { :fifo }
144
+ it_behaves_like "basic pool tests"
145
+ end
146
+
147
+ context "LIFO Pool" do
148
+ let(:pool_order) { :lifo }
149
+ it_behaves_like "basic pool tests"
150
+ end
151
+
152
+ context "Random Pool" do
153
+ let(:pool_order) { :random }
154
+ it_behaves_like "basic pool tests"
155
+ end
156
+
157
+ context "Priority Pool" do
158
+ let(:pool_order) { :priority }
159
+ it_behaves_like "basic pool tests"
160
+ end
161
+ end
@@ -1,5 +1,5 @@
1
1
  class BatchTestJobBase < ActiveJob::Base
2
- def perform
2
+ def perform(*args, **kwargs)
3
3
  end
4
4
 
5
5
  def self.perform_async(*args)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canvas_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.10
4
+ version: 0.19.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure CustomDev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-06 00:00:00.000000000 Z
11
+ date: 2023-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -485,12 +485,12 @@ files:
485
485
  - lib/canvas_sync/job_batches/chain_builder.rb
486
486
  - lib/canvas_sync/job_batches/context_hash.rb
487
487
  - lib/canvas_sync/job_batches/hier_batch_ids.lua
488
- - lib/canvas_sync/job_batches/hincr_max.lua
489
488
  - lib/canvas_sync/job_batches/jobs/base_job.rb
490
489
  - lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb
491
490
  - lib/canvas_sync/job_batches/jobs/managed_batch_job.rb
492
491
  - lib/canvas_sync/job_batches/jobs/serial_batch_job.rb
493
492
  - lib/canvas_sync/job_batches/pool.rb
493
+ - lib/canvas_sync/job_batches/pool_refill.lua
494
494
  - lib/canvas_sync/job_batches/redis_model.rb
495
495
  - lib/canvas_sync/job_batches/redis_script.rb
496
496
  - lib/canvas_sync/job_batches/schedule_callback.lua
@@ -559,6 +559,7 @@ files:
559
559
  - spec/canvas_sync/jobs/sync_simple_table_job_spec.rb
560
560
  - spec/canvas_sync/jobs/sync_submissions_job_spec.rb
561
561
  - spec/canvas_sync/jobs/sync_terms_job_spec.rb
562
+ - spec/canvas_sync/misc_helper_spec.rb
562
563
  - spec/canvas_sync/models/accounts_spec.rb
563
564
  - spec/canvas_sync/models/admins_spec.rb
564
565
  - spec/canvas_sync/models/assignment_group_spec.rb
@@ -700,6 +701,7 @@ files:
700
701
  - spec/job_batching/integration/simple.rb
701
702
  - spec/job_batching/integration/workflow.rb
702
703
  - spec/job_batching/integration_helper.rb
704
+ - spec/job_batching/pool_spec.rb
703
705
  - spec/job_batching/sidekiq_spec.rb
704
706
  - spec/job_batching/status_spec.rb
705
707
  - spec/job_batching/support/base_job.rb
@@ -745,9 +747,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
745
747
  version: '0'
746
748
  required_rubygems_version: !ruby/object:Gem::Requirement
747
749
  requirements:
748
- - - ">="
750
+ - - ">"
749
751
  - !ruby/object:Gem::Version
750
- version: '0'
752
+ version: 1.3.1
751
753
  requirements: []
752
754
  rubygems_version: 3.1.6
753
755
  signing_key:
@@ -771,6 +773,7 @@ test_files:
771
773
  - spec/canvas_sync/jobs/sync_simple_table_job_spec.rb
772
774
  - spec/canvas_sync/jobs/sync_submissions_job_spec.rb
773
775
  - spec/canvas_sync/jobs/sync_terms_job_spec.rb
776
+ - spec/canvas_sync/misc_helper_spec.rb
774
777
  - spec/canvas_sync/models/accounts_spec.rb
775
778
  - spec/canvas_sync/models/admins_spec.rb
776
779
  - spec/canvas_sync/models/assignment_group_spec.rb
@@ -912,6 +915,7 @@ test_files:
912
915
  - spec/job_batching/integration/simple.rb
913
916
  - spec/job_batching/integration/workflow.rb
914
917
  - spec/job_batching/integration_helper.rb
918
+ - spec/job_batching/pool_spec.rb
915
919
  - spec/job_batching/sidekiq_spec.rb
916
920
  - spec/job_batching/status_spec.rb
917
921
  - spec/job_batching/support/base_job.rb
@@ -1,5 +0,0 @@
1
- local r=redis.call('HGET', KEYS[1], ARGV[1])
2
- if r == false or r < ARGV[2] then
3
- redis.call('HINCRBY', KEYS[1], ARGV[1], 1)
4
- end
5
- return r or 0