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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/job/builder.rb +1 -5
- data/lib/async/job/version.rb +1 -1
- data/readme.md +4 -1
- data.tar.gz.sig +0 -0
- metadata +3 -23
- metadata.gz.sig +0 -0
- data/lib/async/job/processor/redis/delayed_jobs.rb +0 -64
- data/lib/async/job/processor/redis/job_store.rb +0 -25
- data/lib/async/job/processor/redis/processing_list.rb +0 -107
- data/lib/async/job/processor/redis/ready_list.rb +0 -36
- data/lib/async/job/processor/redis/server.rb +0 -109
- data/lib/async/job/processor/redis.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 225fd8c2429fb1d8cdcee418e89c4c8ecb09da8fbda4b46331973a92ae186e75
|
4
|
+
data.tar.gz: 2c555bc6168739cf76188a536fd57e0b3b89d900d82ca2a661e03ada8e1078cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42a545a3279caf7c5ed5de25593993119375693184452eaf9a1794dedd197a27a0859e91325dbe0831c0734646f05a4d915dc479dfe2fcc850d577d39ed32683
|
7
|
+
data.tar.gz: '0063657068e88a81be3f7978ae4963dba29ffa0536bd48db202906a0a28608dc2971fc269a04873c1552de043e90364baf9ef5986718139d2a4a839c695cda5c'
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/job/builder.rb
CHANGED
@@ -40,7 +40,7 @@ module Async
|
|
40
40
|
@delegate = delegate
|
41
41
|
end
|
42
42
|
|
43
|
-
def build(delegate = @delegate
|
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
|
data/lib/async/job/version.rb
CHANGED
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
|
-
- [
|
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.
|
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-
|
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:
|
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
|