async-job 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44a092dcad8310e5301d4c273e08c21a5eb046b530ff27cce0320fd56ccd3f8f
4
- data.tar.gz: 11c40eb020b847b46a933d8ff2b1f27bb015edf781e4d4ca3a4579269140a8cb
3
+ metadata.gz: 1e330cf8c0430796e25264e8c892f70423f1fa0ffbc645a19198774f51ef4893
4
+ data.tar.gz: 3c1ac69a6bde193876717c65dace5e97803b66ab9471d5baeffc33bb1762701a
5
5
  SHA512:
6
- metadata.gz: 69d052fe00161a40300410b95aea22dde2a4b6eb41bc320d915e72327324bad175b0c1ca1dd4953287e069cbc6755d222fe2de5346acda3b34ff5f9818b9606a
7
- data.tar.gz: 8087a4c82be53394574b1ab70affcdf3c5355a1db0c24f022ea012c771cc290d9a04d173e49cd10edd6b54d8a8aba1f0b78bc392124f9d401e6940452013d45f
6
+ metadata.gz: 956e76aadce5f21fd099ba72d38914f4a4a6981a1d4382eee34a7bc37a0d79cb7bedb6b93000146a752fdc829945a93c72ef873fc3012acda5f6ea50d58128ae
7
+ data.tar.gz: 8fb1c37bc2f065c08a08dbdc25f04dbfe2aab5d67eb57c297a50fb5665e9840014fe1c3bd7257fc2dfa463919507fdf450dcaa71d1c17dc5cde0441b949c108b
checksums.yaml.gz.sig CHANGED
Binary file
@@ -3,25 +3,26 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2024, by Samuel Williams.
5
5
 
6
+ require_relative '../../coder'
7
+
6
8
  module Async
7
9
  module Job
8
10
  module Backend
9
11
  module Inline
10
12
  class Server
11
- def initialize(handler)
12
- @handler = handler
13
- end
14
-
15
- def enqueue(job)
16
- Async do
17
- @handler.call(job)
18
- end
13
+ def initialize(delegate)
14
+ @delegate = delegate
19
15
  end
20
16
 
21
- def schedule(job, timestamp)
17
+ def call(job)
18
+ scheduled_at = Coder::Time(job["scheduled_at"])
19
+
22
20
  Async do
23
- sleep(timestamp - Time.now)
24
- @handler.call(job)
21
+ if scheduled_at
22
+ sleep(scheduled_at - Time.now)
23
+ end
24
+
25
+ @delegate.call(job)
25
26
  end
26
27
  end
27
28
  end
@@ -9,8 +9,8 @@ module Async
9
9
  module Job
10
10
  module Backend
11
11
  module Inline
12
- def self.new(handler)
13
- return Server.new(handler)
12
+ def self.new(delegate)
13
+ return Server.new(delegate)
14
14
  end
15
15
  end
16
16
  end
@@ -9,18 +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(handler, client, prefix)
19
- @handler = handler
19
+ def initialize(delegate, client, prefix: 'async-job', coder: Coder::DEFAULT, resolution: 10)
20
+ @delegate = delegate
20
21
 
21
22
  @id = SecureRandom.uuid
22
23
  @client = client
23
24
  @prefix = prefix
25
+ @coder = coder
26
+ @resolution = resolution
24
27
 
25
28
  @job_store = JobStore.new(@client, "#{@prefix}:jobs")
26
29
 
@@ -33,7 +36,7 @@ module Async
33
36
  def start
34
37
  Console.info(self, "Starting server...")
35
38
  # Start the delayed queue, which will move jobs to the ready queue when they are ready:
36
- @delayed_queue.start(@ready_queue)
39
+ @delayed_queue.start(@ready_queue, resolution: @resolution)
37
40
 
38
41
  # Start the processing queue, which will move jobs to the ready queue when they are abandoned:
39
42
  @processing_queue.start
@@ -43,12 +46,14 @@ module Async
43
46
  end
44
47
  end
45
48
 
46
- def enqueue(job)
47
- @ready_queue.add(job, @job_store)
48
- end
49
-
50
- def schedule(job, timestamp)
51
- @delayed_queue.add(job, timestamp, @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
52
57
  end
53
58
 
54
59
  protected
@@ -56,8 +61,8 @@ module Async
56
61
  def dequeue
57
62
  id = @processing_queue.fetch
58
63
  begin
59
- job = @job_store.get(id)
60
- @handler.call(job)
64
+ job = @coder.load(@job_store.get(id))
65
+ @delegate.call(job)
61
66
  @processing_queue.complete(id)
62
67
  rescue => error
63
68
  @processing_queue.retry(id)
@@ -10,9 +10,9 @@ module Async
10
10
  module Job
11
11
  module Backend
12
12
  module Redis
13
- def self.new(handler, endpoint: Async::Redis.local_endpoint, prefix: "async:job")
13
+ def self.new(delegate, endpoint: Async::Redis.local_endpoint, **options)
14
14
  client = Async::Redis::Client.new(endpoint)
15
- return Server.new(handler, client, prefix)
15
+ return Server.new(delegate, client, **options)
16
16
  end
17
17
  end
18
18
  end
@@ -1,16 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
1
6
  require 'async/queue'
2
7
 
3
8
  module Async
4
9
  module Job
5
10
  class Buffer
6
- def initialize
11
+ def initialize(delegate = nil)
7
12
  @jobs = Async::Queue.new
13
+ @delegate = delegate
8
14
  end
9
15
 
10
16
  attr :jobs
11
17
 
12
18
  def call(job)
13
19
  @jobs.enqueue(job)
20
+ @delegate&.call(job)
14
21
  end
15
22
 
16
23
  def pop
@@ -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.3.0"
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.3.0
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-25 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
@@ -84,6 +84,11 @@ files:
84
84
  - lib/async/job/backend/redis/ready_queue.rb
85
85
  - lib/async/job/backend/redis/server.rb
86
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
87
92
  - lib/async/job/generic.rb
88
93
  - lib/async/job/version.rb
89
94
  - license.md
@@ -92,7 +97,7 @@ homepage:
92
97
  licenses:
93
98
  - MIT
94
99
  metadata:
95
- documentation_uri: https://socketry.github.io/async-job
100
+ documentation_uri: https://socketry.github.io/async-job/
96
101
  post_install_message:
97
102
  rdoc_options: []
98
103
  require_paths:
metadata.gz.sig CHANGED
Binary file