async-job 0.9.2 → 0.10.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eeb1fabbd3ce8f2806c1b3c3c4fc5c81c0f661e92d49f29a26ed5dac46960ab9
4
- data.tar.gz: 4187e3a56169deffce40590a9ebda6e075ec66f0450e8808cb5af6bc2ab7ce13
3
+ metadata.gz: 225fd8c2429fb1d8cdcee418e89c4c8ecb09da8fbda4b46331973a92ae186e75
4
+ data.tar.gz: 2c555bc6168739cf76188a536fd57e0b3b89d900d82ca2a661e03ada8e1078cf
5
5
  SHA512:
6
- metadata.gz: 5d1ef79240e7ee4928675166a2e4ebf608910b559401aefb7eba8088edb47fa50a08b7a784dadb4b23708627c53d1275b2559fb70ed1a2e13387680dde14fbe2
7
- data.tar.gz: 5bbdefdc4c753d464a30f548f8a1d91f350f54d92248732de0e9c4bca012bda8422c803cea0c4547b708804b354677d18bb5df76c29fc42744a537fcb1f32e94
6
+ metadata.gz: 42a545a3279caf7c5ed5de25593993119375693184452eaf9a1794dedd197a27a0859e91325dbe0831c0734646f05a4d915dc479dfe2fcc850d577d39ed32683
7
+ data.tar.gz: '0063657068e88a81be3f7978ae4963dba29ffa0536bd48db202906a0a28608dc2971fc269a04873c1552de043e90364baf9ef5986718139d2a4a839c695cda5c'
checksums.yaml.gz.sig CHANGED
Binary file
@@ -40,7 +40,7 @@ module Async
40
40
  @delegate = delegate
41
41
  end
42
42
 
43
- def build(delegate = @delegate, &block)
43
+ def build(delegate = @delegate)
44
44
  # We then wrap the delegate with the middleware in reverse order:
45
45
  @dequeue.reverse_each do |middleware|
46
46
  delegate = middleware.call(delegate)
@@ -53,10 +53,6 @@ module Async
53
53
  client = middleware.call(client)
54
54
  end
55
55
 
56
- if block_given?
57
- client = yield(client) || client
58
- end
59
-
60
56
  return Queue.new(client, server, @delegate)
61
57
  end
62
58
  end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module Job
8
- VERSION = "0.9.2"
8
+ VERSION = "0.10.0"
9
9
  end
10
10
  end
data/readme.md CHANGED
@@ -10,10 +10,13 @@ Please see the [project documentation](https://socketry.github.io/async-job/) fo
10
10
 
11
11
  - [Getting Started](https://socketry.github.io/async-job/guides/getting-started/index) - This guide gives you an overview of the `async-job` gem and explains the core concepts.
12
12
 
13
- - [Redis Processor](https://socketry.github.io/async-job/guides/redis-processor/index) - This guide gives a brief overview of the implementation of the Redis queue.
13
+ - [Inline Queue](https://socketry.github.io/async-job/guides/inline-queue/index) - This guide explains how to use the Inline queue to execute background jobs.
14
+
15
+ - [Aggregate Queue](https://socketry.github.io/async-job/guides/aggregate-queue/index) - This guide explains how to use the Aggregate queue to reduce input latency and improve the performance of your application.
14
16
 
15
17
  ## See Also
16
18
 
19
+ - [async-job-processor-redis](https://github.com/socketry/async-job-processor-redis) - Redis processor for `async-job` (similar to Sidekiq).
17
20
  - [async-job-adapter-active\_job](https://github.com/socketry/async-job-adapter-active_job) - ActiveJob adapter for `async-job`.
18
21
 
19
22
  ## Contributing
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-job
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -38,7 +38,7 @@ cert_chain:
38
38
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
39
39
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
40
40
  -----END CERTIFICATE-----
41
- date: 2024-08-09 00:00:00.000000000 Z
41
+ date: 2024-08-14 00:00:00.000000000 Z
42
42
  dependencies:
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: async
@@ -54,20 +54,6 @@ dependencies:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
56
  version: '2.9'
57
- - !ruby/object:Gem::Dependency
58
- name: async-redis
59
- requirement: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: '0'
64
- type: :runtime
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: '0'
71
57
  description:
72
58
  email:
73
59
  executables: []
@@ -86,12 +72,6 @@ files:
86
72
  - lib/async/job/processor/delayed.rb
87
73
  - lib/async/job/processor/generic.rb
88
74
  - lib/async/job/processor/inline.rb
89
- - lib/async/job/processor/redis.rb
90
- - lib/async/job/processor/redis/delayed_jobs.rb
91
- - lib/async/job/processor/redis/job_store.rb
92
- - lib/async/job/processor/redis/processing_list.rb
93
- - lib/async/job/processor/redis/ready_list.rb
94
- - lib/async/job/processor/redis/server.rb
95
75
  - lib/async/job/queue.rb
96
76
  - lib/async/job/version.rb
97
77
  - license.md
@@ -120,5 +100,5 @@ requirements: []
120
100
  rubygems_version: 3.5.11
121
101
  signing_key:
122
102
  specification_version: 4
123
- summary: A asynchronous job queue for Ruby.
103
+ summary: An asynchronous job queue for Ruby.
124
104
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- module Async
7
- module Job
8
- module Processor
9
- module Redis
10
- class DelayedJobs
11
- ADD = <<~LUA
12
- redis.call('HSET', KEYS[1], ARGV[1], ARGV[2])
13
- redis.call('ZADD', KEYS[2], ARGV[3], ARGV[1])
14
- LUA
15
-
16
- MOVE = <<~LUA
17
- local jobs = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1])
18
- redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1])
19
- if #jobs > 0 then
20
- redis.call('LPUSH', KEYS[2], unpack(jobs))
21
- end
22
- return #jobs
23
- LUA
24
-
25
- def initialize(client, key)
26
- @client = client
27
- @key = key
28
-
29
- @add = @client.script(:load, ADD)
30
- @move = @client.script(:load, MOVE)
31
- end
32
-
33
- def start(ready_list, resolution: 10, parent: Async::Task.current)
34
- parent.async do
35
- while true
36
- count = move(destination: ready_list.key)
37
-
38
- if count > 0
39
- Console.debug(self, "Moved #{count} delayed jobs to ready list.")
40
- end
41
-
42
- sleep(resolution)
43
- end
44
- end
45
- end
46
-
47
- attr :key
48
-
49
- def add(job, timestamp, job_store)
50
- id = SecureRandom.uuid
51
-
52
- @client.evalsha(@add, 2, job_store.key, @key, id, job, timestamp.to_f)
53
-
54
- return id
55
- end
56
-
57
- def move(destination:, now: Time.now.to_i)
58
- @client.evalsha(@move, 2, @key, destination, now)
59
- end
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- module Async
7
- module Job
8
- module Processor
9
- module Redis
10
- class JobStore
11
- def initialize(client, key)
12
- @client = client
13
- @key = key
14
- end
15
-
16
- attr :key
17
-
18
- def get(id)
19
- @client.hget(@key, id)
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- module Async
7
- module Job
8
- module Processor
9
- module Redis
10
- class ProcessingList
11
- REQUEUE = <<~LUA
12
- local cursor = "0"
13
- local count = 0
14
-
15
- repeat
16
- -- Scan through all known server id -> job id mappings and requeue any jobs that have been abandoned:
17
- local result = redis.call('SCAN', cursor, 'MATCH', KEYS[1]..':*:pending')
18
- cursor = result[1]
19
- for _, pending_key in pairs(result[2]) do
20
- -- Check if the server is still active:
21
- local server_key = KEYS[1]..":"..pending_key:match("([^:]+):pending")
22
- local state = redis.call('GET', server_key)
23
- if state == false then
24
- while true do
25
- -- Requeue any pending jobs:
26
- local result = redis.call('RPOPLPUSH', pending_key, KEYS[2])
27
-
28
- if result == false then
29
- -- Delete the pending list:
30
- redis.call('DEL', pending_key)
31
- break
32
- end
33
-
34
- count = count + 1
35
- end
36
- end
37
- end
38
- until cursor == "0"
39
-
40
- return count
41
- LUA
42
-
43
- RETRY = <<~LUA
44
- redis.call('LREM', KEYS[1], 1, ARGV[1])
45
- redis.call('LPUSH', KEYS[2], ARGV[1])
46
- LUA
47
-
48
- COMPLETE = <<~LUA
49
- redis.call('LREM', KEYS[1], 1, ARGV[1])
50
- redis.call('HDEL', KEYS[2], ARGV[1])
51
- LUA
52
-
53
- def initialize(client, key, id, ready_list, job_store)
54
- @client = client
55
- @key = key
56
- @id = id
57
-
58
- @ready_list = ready_list
59
- @job_store = job_store
60
-
61
- @pending_key = "#{@key}:#{@id}:pending"
62
- @heartbeat_key = "#{@key}:#{@id}"
63
-
64
- @requeue = @client.script(:load, REQUEUE)
65
- @retry = @client.script(:load, RETRY)
66
- @complete = @client.script(:load, COMPLETE)
67
- end
68
-
69
- attr :key
70
-
71
- def fetch
72
- @client.brpoplpush(@ready_list.key, @pending_key, 0)
73
- end
74
-
75
- def complete(id)
76
- @client.evalsha(@complete, 2, @pending_key, @job_store.key, id)
77
- end
78
-
79
- def retry(id)
80
- Console.warn(self, "Retrying job: #{id}")
81
- @client.evalsha(@retry, 2, @pending_key, @ready_list.key, id)
82
- end
83
-
84
- def start(delay: 5, factor: 2, parent: Async::Task.current)
85
- heartbeat_key = "#{@key}:#{@id}"
86
- start_time = Time.now.to_f
87
-
88
- parent.async do
89
- while true
90
- uptime = (Time.now.to_f - start_time).round(2)
91
- @client.set(heartbeat_key, JSON.dump(uptime: uptime), seconds: delay*factor)
92
-
93
- # Requeue any jobs that have been abandoned:
94
- count = @client.evalsha(@requeue, 2, @key, @ready_list.key)
95
- if count > 0
96
- Console.warn(self, "Requeued #{count} abandoned jobs.")
97
- end
98
-
99
- sleep(delay)
100
- end
101
- end
102
- end
103
- end
104
- end
105
- end
106
- end
107
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- module Async
7
- module Job
8
- module Processor
9
- module Redis
10
- class ReadyList
11
- ADD = <<~LUA
12
- redis.call('HSET', KEYS[1], ARGV[1], ARGV[2])
13
- redis.call('LPUSH', KEYS[2], ARGV[1])
14
- LUA
15
-
16
- def initialize(client, key)
17
- @client = client
18
- @key = key
19
-
20
- @add = @client.script(:load, ADD)
21
- end
22
-
23
- attr :key
24
-
25
- def add(job, job_store)
26
- id = SecureRandom.uuid
27
-
28
- @client.evalsha(@add, 2, job_store.key, @key, id, job)
29
-
30
- return id
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- require_relative 'delayed_jobs'
7
- require_relative 'job_store'
8
- require_relative 'processing_list'
9
- require_relative 'ready_list'
10
- require_relative '../../coder'
11
- require_relative '../generic'
12
-
13
- require 'securerandom'
14
- require 'async/idler'
15
-
16
- module Async
17
- module Job
18
- module Processor
19
- module Redis
20
- class Server < Generic
21
- def initialize(delegate, client, prefix: 'async-job', coder: Coder::DEFAULT, resolution: 10, parent: nil)
22
- super(delegate)
23
-
24
- @id = SecureRandom.uuid
25
- @client = client
26
- @prefix = prefix
27
- @coder = coder
28
- @resolution = resolution
29
-
30
- @job_store = JobStore.new(@client, "#{@prefix}:jobs")
31
- @delayed_jobs = DelayedJobs.new(@client, "#{@prefix}:delayed")
32
- @ready_list = ReadyList.new(@client, "#{@prefix}:ready")
33
- @processing_list = ProcessingList.new(@client, "#{@prefix}:processing", @id, @ready_list, @job_store)
34
-
35
- @parent = parent || Async::Idler.new
36
- end
37
-
38
- def start!
39
- return false if @task
40
-
41
- @task = true
42
-
43
- @parent.async(transient: true, annotation: self.class.name) do |task|
44
- @task = task
45
-
46
- while true
47
- self.dequeue(task)
48
- end
49
- ensure
50
- @task = nil
51
- end
52
- end
53
-
54
- def start
55
- super
56
-
57
- # Start the delayed processor, which will move jobs to the ready processor when they are ready:
58
- @delayed_jobs.start(@ready_list, resolution: @resolution)
59
-
60
- # Start the processing processor, which will move jobs to the ready processor when they are abandoned:
61
- @processing_list.start
62
-
63
- self.start!
64
- end
65
-
66
- def stop
67
- @task&.stop
68
-
69
- super
70
- end
71
-
72
- def call(job)
73
- scheduled_at = Coder::Time(job["scheduled_at"])
74
-
75
- if scheduled_at
76
- @delayed_jobs.add(@coder.dump(job), scheduled_at, @job_store)
77
- else
78
- @ready_list.add(@coder.dump(job), @job_store)
79
- end
80
- end
81
-
82
- protected
83
-
84
- # Dequeue a job from the ready list and process it.
85
- #
86
- # If the job fails for any reason, it will be retried.
87
- #
88
- # If you do not desire this behavior, you should catch exceptions in the delegate.
89
- def dequeue(parent)
90
- _id = @processing_list.fetch
91
-
92
- parent.async do
93
- id = _id; _id = nil
94
-
95
- job = @coder.load(@job_store.get(id))
96
- @delegate.call(job)
97
- @processing_list.complete(id)
98
- rescue => error
99
- Console::Event::Failure.for(error).emit(self, "Job failed with error!", id: id)
100
- @processing_list.retry(id)
101
- end
102
- ensure
103
- @processing_list.retry(_id) if _id
104
- end
105
- end
106
- end
107
- end
108
- end
109
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
5
-
6
- require_relative 'redis/server'
7
- require 'async/redis/client'
8
-
9
- module Async
10
- module Job
11
- module Processor
12
- module Redis
13
- def self.new(delegate, endpoint: Async::Redis.local_endpoint, **options)
14
- client = Async::Redis::Client.new(endpoint)
15
- return Server.new(delegate, client, **options)
16
- end
17
- end
18
- end
19
- end
20
- end