async-job 0.2.1 → 0.4.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: 895f34561c4da96884e09c180b33ab7ed4ac52b5c72e10af70403deea920200e
4
- data.tar.gz: 8af66add606e0eebb5abfb5d35a67d1e41547c86227054a56fd1dbf1e2f90929
3
+ metadata.gz: 1e330cf8c0430796e25264e8c892f70423f1fa0ffbc645a19198774f51ef4893
4
+ data.tar.gz: 3c1ac69a6bde193876717c65dace5e97803b66ab9471d5baeffc33bb1762701a
5
5
  SHA512:
6
- metadata.gz: 5bd937f27dd026b2c6c3e44c2a2d801cd936c477fd1e514d0c34f07d0bc4db6cf7c3583ea26348a85277b33cc59bbd90ec1626bf67eb8bdac1b85eaca6b588ee
7
- data.tar.gz: 94a26598b246051666bf68c9cd1f11785ca75f32c43fb9d74349ad193650cd03df9cc7069d2b46f128b11cd50fc35383b0018d31acdceb833d013090ce711fc3
6
+ metadata.gz: 956e76aadce5f21fd099ba72d38914f4a4a6981a1d4382eee34a7bc37a0d79cb7bedb6b93000146a752fdc829945a93c72ef873fc3012acda5f6ea50d58128ae
7
+ data.tar.gz: 8fb1c37bc2f065c08a08dbdc25f04dbfe2aab5d67eb57c297a50fb5665e9840014fe1c3bd7257fc2dfa463919507fdf450dcaa71d1c17dc5cde0441b949c108b
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative '../../coder'
7
+
8
+ module Async
9
+ module Job
10
+ module Backend
11
+ module Inline
12
+ class Server
13
+ def initialize(delegate)
14
+ @delegate = delegate
15
+ end
16
+
17
+ def call(job)
18
+ scheduled_at = Coder::Time(job["scheduled_at"])
19
+
20
+ Async do
21
+ if scheduled_at
22
+ sleep(scheduled_at - Time.now)
23
+ end
24
+
25
+ @delegate.call(job)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative 'inline/server'
7
+
8
+ module Async
9
+ module Job
10
+ module Backend
11
+ module Inline
12
+ def self.new(delegate)
13
+ return Server.new(delegate)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -9,16 +9,21 @@ require_relative 'processing_queue'
9
9
  require_relative 'ready_queue'
10
10
 
11
11
  require 'securerandom'
12
+ require_relative '../../coder'
12
13
 
13
14
  module Async
14
15
  module Job
15
16
  module Backend
16
17
  module Redis
17
18
  class Server
18
- def initialize(client, prefix)
19
+ def initialize(delegate, client, prefix: 'async-job', coder: Coder::DEFAULT, resolution: 10)
20
+ @delegate = delegate
21
+
19
22
  @id = SecureRandom.uuid
20
23
  @client = client
21
24
  @prefix = prefix
25
+ @coder = coder
26
+ @resolution = resolution
22
27
 
23
28
  @job_store = JobStore.new(@client, "#{@prefix}:jobs")
24
29
 
@@ -31,37 +36,39 @@ module Async
31
36
  def start
32
37
  Console.info(self, "Starting server...")
33
38
  # Start the delayed queue, which will move jobs to the ready queue when they are ready:
34
- @delayed_queue.start(@ready_queue)
39
+ @delayed_queue.start(@ready_queue, resolution: @resolution)
35
40
 
36
41
  # Start the processing queue, which will move jobs to the ready queue when they are abandoned:
37
42
  @processing_queue.start
43
+
44
+ while true
45
+ self.dequeue
46
+ end
38
47
  end
39
48
 
40
- def enqueue(job)
41
- @ready_queue.add(job, @job_store)
49
+ def call(job)
50
+ scheduled_at = Coder::Time(job["scheduled_at"])
51
+
52
+ if scheduled_at
53
+ @delayed_queue.add(@coder.dump(job), scheduled_at, @job_store)
54
+ else
55
+ @ready_queue.add(@coder.dump(job), @job_store)
56
+ end
42
57
  end
43
58
 
44
- def schedule(job, timestamp)
45
- @delayed_queue.add(job, timestamp, @job_store)
46
- end
59
+ protected
47
60
 
48
- def process(&block)
61
+ def dequeue
49
62
  id = @processing_queue.fetch
50
63
  begin
51
- job = @job_store.get(id)
52
- yield id, job
64
+ job = @coder.load(@job_store.get(id))
65
+ @delegate.call(job)
53
66
  @processing_queue.complete(id)
54
67
  rescue => error
55
68
  @processing_queue.retry(id)
56
69
  raise
57
70
  end
58
71
  end
59
-
60
- def each(&block)
61
- while true
62
- process(&block)
63
- end
64
- end
65
72
  end
66
73
  end
67
74
  end
@@ -10,13 +10,9 @@ module Async
10
10
  module Job
11
11
  module Backend
12
12
  module Redis
13
- def self.new(**options)
14
- endpoint = options.fetch(:endpoint) {Async::Redis.local_endpoint}
15
- prefix = options.fetch(:prefix) {"async:job"}
16
-
13
+ def self.new(delegate, endpoint: Async::Redis.local_endpoint, **options)
17
14
  client = Async::Redis::Client.new(endpoint)
18
-
19
- return Async::Job::Backend::Redis::Server.new(client, prefix)
15
+ return Server.new(delegate, client, **options)
20
16
  end
21
17
  end
22
18
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require 'async/queue'
7
+
8
+ module Async
9
+ module Job
10
+ class Buffer
11
+ def initialize(delegate = nil)
12
+ @jobs = Async::Queue.new
13
+ @delegate = delegate
14
+ end
15
+
16
+ attr :jobs
17
+
18
+ def call(job)
19
+ @jobs.enqueue(job)
20
+ @delegate&.call(job)
21
+ end
22
+
23
+ def pop
24
+ @jobs.dequeue
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,70 @@
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
+ class Builder
9
+ Pipeline = Struct.new(:producer, :consumer, :delegate)
10
+
11
+ def self.build(delegate = nil, &block)
12
+ builder = self.new(delegate)
13
+
14
+ builder.instance_eval(&block)
15
+
16
+ return builder.build
17
+ end
18
+
19
+ def initialize(delegate = nil)
20
+ @enqueue = []
21
+ @dequeue = []
22
+ @delegate = delegate
23
+
24
+ @queue = nil
25
+ end
26
+
27
+ def enqueue(middleware)
28
+ @enqueue << middleware
29
+ end
30
+
31
+ def queue(queue, *arguments, **options)
32
+ # The delegate is the output side of the queue, e.g. a Queuedelegate or similar wrapper.
33
+ # The queue itself is instantiated with the delegate.
34
+ @queue = ->(delegate){queue.new(delegate, *arguments, **options)}
35
+ end
36
+
37
+ def dequeue(middleware)
38
+ @dequeue << middleware
39
+ end
40
+
41
+ def delegate(delegate)
42
+ @delegate = delegate
43
+ end
44
+
45
+ def build(&block)
46
+ # To construct the queue, we need the delegate.
47
+ delegate = @delegate
48
+
49
+ # We then wrap the delegate with the middleware in reverse order:
50
+ @dequeue.reverse_each do |middleware|
51
+ delegate = middleware.new(delegate)
52
+ end
53
+
54
+ # We can now construct the queue with the delegate:
55
+ producer = consumer = @queue.call(delegate)
56
+
57
+ # We now construct the queue producer:
58
+ @enqueue.reverse_each do |middleware|
59
+ producer = middleware.new(queue)
60
+ end
61
+
62
+ if block_given?
63
+ producer = yield(producer)
64
+ end
65
+
66
+ return Pipeline.new(producer, consumer, @delegate)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require 'json'
7
+
8
+ module Async
9
+ module Job
10
+ module Coder
11
+ class JSON
12
+ def initialize
13
+ end
14
+
15
+ def dump(job)
16
+ ::JSON.dump(job)
17
+ end
18
+
19
+ def load(data)
20
+ ::JSON.parse(data, symbolize_names: true)
21
+ end
22
+
23
+ DEFAULT = self.new
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require 'msgpack'
7
+
8
+ module Async
9
+ module Job
10
+ module Coder
11
+ class Marshal
12
+ def dump(job)
13
+ ::Marshal.dump(job)
14
+ end
15
+
16
+ def load(data)
17
+ ::Marshal.load(data)
18
+ end
19
+
20
+ DEFAULT = self.new
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require 'msgpack'
7
+
8
+ module Async
9
+ module Job
10
+ module Coder
11
+ class MessagePack
12
+ def initialize
13
+ @factory = ::MessagePack::Factory.new
14
+
15
+ @factory.register_type(0x01, Symbol)
16
+
17
+ @factory.register_type(0x02, Time,
18
+ packer: ::MessagePack::Time::Packer,
19
+ unpacker: ::MessagePack::Time::Unpacker
20
+ )
21
+ end
22
+
23
+ def dump(job)
24
+ @factory.pack(job)
25
+ end
26
+
27
+ def load(data)
28
+ @factory.unpack(data)
29
+ end
30
+
31
+ DEFAULT = self.new
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative 'coder/json'
7
+
8
+ module Async
9
+ module Job
10
+ module Coder
11
+ DEFAULT = JSON::DEFAULT
12
+
13
+ # Type-cast for time values. See <https://bugs.ruby-lang.org/issues/20298> for background.
14
+ # @parameter value [Time || Integer || String || nil]
15
+ def self.Time(value)
16
+ case value
17
+ when ::Time
18
+ value
19
+ when Integer
20
+ ::Time.at(value)
21
+ when String
22
+ ::Time.new(value)
23
+ when nil
24
+ nil
25
+ else
26
+ value.to_time
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -10,13 +10,13 @@ module Async
10
10
  self.new(...).enqueue
11
11
  end
12
12
 
13
- def initialize(id, perform_at: nil)
13
+ def initialize(id, scheduled_at: nil)
14
14
  @id = id
15
- @perform_at = perform_at
15
+ @scheduled_at = scheduled_at
16
16
  end
17
17
 
18
18
  attr :id
19
- attr :perform_at
19
+ attr :scheduled_at
20
20
 
21
21
  def serialize
22
22
  raise NotImplementedError
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module Job
8
- VERSION = "0.2.1"
8
+ VERSION = "0.4.0"
9
9
  end
10
10
  end
data/lib/async/job.rb CHANGED
@@ -5,3 +5,4 @@
5
5
 
6
6
  require_relative 'job/version'
7
7
  require_relative 'job/backend'
8
+ require_relative 'job/builder'
data/readme.md CHANGED
@@ -6,7 +6,11 @@ Provides an asynchronous job server.
6
6
 
7
7
  ## Usage
8
8
 
9
- The current implementation is incomplete, but with `redis` running on localhost, you can run several instances of `client.rb` and `server.rb` to show the general operation.
9
+ Please see the [project documentation](https://socketry.github.io/async-job/) for more details.
10
+
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
+
13
+ - [Redis Backend](https://socketry.github.io/async-job/guides/redis-backend/index) - This guide gives a brief overview of the implementation of the Redis backend.
10
14
 
11
15
  ## Contributing
12
16
 
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.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -37,7 +37,7 @@ cert_chain:
37
37
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
38
38
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
39
39
  -----END CERTIFICATE-----
40
- date: 2024-02-24 00:00:00.000000000 Z
40
+ date: 2024-02-26 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: async
@@ -75,12 +75,20 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - lib/async/job.rb
77
77
  - lib/async/job/backend.rb
78
+ - lib/async/job/backend/inline.rb
79
+ - lib/async/job/backend/inline/server.rb
78
80
  - lib/async/job/backend/redis.rb
79
81
  - lib/async/job/backend/redis/delayed_queue.rb
80
82
  - lib/async/job/backend/redis/job_store.rb
81
83
  - lib/async/job/backend/redis/processing_queue.rb
82
84
  - lib/async/job/backend/redis/ready_queue.rb
83
85
  - lib/async/job/backend/redis/server.rb
86
+ - lib/async/job/buffer.rb
87
+ - lib/async/job/builder.rb
88
+ - lib/async/job/coder.rb
89
+ - lib/async/job/coder/json.rb
90
+ - lib/async/job/coder/marshal.rb
91
+ - lib/async/job/coder/message_pack.rb
84
92
  - lib/async/job/generic.rb
85
93
  - lib/async/job/version.rb
86
94
  - license.md
@@ -89,7 +97,7 @@ homepage:
89
97
  licenses:
90
98
  - MIT
91
99
  metadata:
92
- documentation_uri: https://socketry.github.io/async-job
100
+ documentation_uri: https://socketry.github.io/async-job/
93
101
  post_install_message:
94
102
  rdoc_options: []
95
103
  require_paths:
metadata.gz.sig CHANGED
Binary file