litejob 0.2.1 → 0.2.3

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: 9235b916ceb5620f30e307e23adb1c3baf256625a3d3cedde1c2851faa9ed490
4
- data.tar.gz: 553500fbb22d4ef729a6059277cb56738c87b671e4009ac9c3b7174293f7bfb8
3
+ metadata.gz: bc3982eaa19478a86cd8b4c4a798ad53753e6352aef0a1ce8b71eb80f2583f9a
4
+ data.tar.gz: 2588cc9f482cd5b90711d732cc70f4e09b2291a105e44dfeba91ec6ce2e0de30
5
5
  SHA512:
6
- metadata.gz: 86125548df029a4a0035314ff34f3a429394079d56b88075e7b05ec72a4975a9b7ee32ed130bef939cfe4edd6a6cbc02b46458e011df847fd942efbbdbd36562
7
- data.tar.gz: 50f38790e97d1b19462a5926bd2a92fbcbc9485ccadf2c89a9c1f1effc4ccd566650bea57400793d3d11aec0390c647f81e04d2ead1963aa0889f9522918654a
6
+ metadata.gz: d2169928b281facd7d75ceff0c471ec9446d8376505acc78087996a51ebd2060669f5059ece4e75ea7f6e2a9423ba872d76d7465de476dd88f9b9ee9615c46a3
7
+ data.tar.gz: f3484b76fd14d868370934bb0bd919ce336fc394df830cb7236f6b3afe3e242be30f256a6598af9c7bef0881bf48562939bfd9267e335a0af2206bf70e5061c0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.3] - 2023-08-14
4
+
5
+ - Make queues and queue priorities optional when initializing a Server instance
6
+
7
+ ## [0.2.2] - 2023-08-14
8
+
9
+ - Add logging
10
+
3
11
  ## [0.2.1] - 2023-08-13
4
12
 
5
13
  - Fix some bugs with Litejob::Server
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- litejob (0.2.1)
5
- litequeue (>= 0.2.0)
4
+ litejob (0.2.3)
5
+ litequeue (>= 0.2.1)
6
6
  litescheduler (>= 0.2.1)
7
7
 
8
8
  GEM
@@ -16,7 +16,7 @@ GEM
16
16
  litedb (0.2.1)
17
17
  litescheduler (>= 0.2.0)
18
18
  sqlite3 (>= 1.5.0)
19
- litequeue (0.2.0)
19
+ litequeue (0.2.1)
20
20
  litedb (>= 0.2.1)
21
21
  litescheduler (0.2.1)
22
22
  minitest (5.19.0)
data/README.md CHANGED
@@ -3,27 +3,71 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/litejob.svg)](https://rubygems.org/gems/litejob)
4
4
  [![Gem Downloads](https://img.shields.io/gem/dt/litejob)](https://rubygems.org/gems/litejob)
5
5
  ![Tests](https://github.com/litestack-ruby/litejob/actions/workflows/main.yml/badge.svg)
6
- ![Coverage](https://img.shields.io/badge/code_coverage-100%25-brightgreen)
6
+ ![Coverage](https://img.shields.io/badge/code_coverage-70%25-red)
7
7
 
8
- TODO: Delete this and the text below, and describe your gem
9
-
10
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/litejob`. To experiment with that code, run `bin/console` for an interactive prompt.
8
+ Litejob is a Ruby module that enables seamless integration of the Litequeue job queueing system into Ruby applications. By including the Litejob module in a class and implementing the `#perform` method, developers can easily enqueue and process jobs asynchronously.
11
9
 
12
10
  ## Installation
13
11
 
14
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
15
-
16
12
  Install the gem and add to the application's Gemfile by executing:
17
13
 
18
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
14
+ $ bundle add litejob
19
15
 
20
16
  If bundler is not being used to manage dependencies, install the gem by executing:
21
17
 
22
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
18
+ $ gem install litejob
23
19
 
24
20
  ## Usage
25
21
 
26
- TODO: Write usage instructions here
22
+ When a job is enqueued, Litejob creates a new instance of the class and passes it any necessary arguments. The class's `#perform` method is then called asynchronously to process the job. This allows the application to continue running without waiting for the job to finish, improving overall performance and responsiveness.
23
+
24
+ One of the main benefits of using Litejob is its simplicity. Because it integrates directly with Litequeue, developers do not need to worry about managing job queues or processing logic themselves. Instead, they can focus on implementing the `#perform` method to handle the specific job tasks.
25
+
26
+ Litejob also provides a number of useful features, including the ability to set job priorities, retry failed jobs, and limit the number of retries. These features can be configured using simple configuration options in the class that includes the Litejob module.
27
+
28
+ Overall, Litejob is a powerful and flexible module that allows developers to easily integrate Litequeue job queueing into their Ruby applications. By enabling asynchronous job processing, Litejob can help improve application performance and scalability, while simplifying the development and management of background job processing logic.
29
+
30
+ ```ruby
31
+ class EasyJob
32
+ include ::Litejob
33
+
34
+ def perform(params)
35
+ # do stuff
36
+ end
37
+ end
38
+ ```
39
+
40
+ Then later you can perform a job asynchronously:
41
+
42
+ ```ruby
43
+ EasyJob.perform_async(params) # perform a job synchronously
44
+ ```
45
+
46
+ Or perform it at a specific time:
47
+
48
+ ```ruby
49
+ EasyJob.perform_at(time, params) # perform a job at a specific time
50
+ ```
51
+
52
+ Or perform it after a certain delay:
53
+
54
+ ```ruby
55
+ EasyJob.perform_in(delay, params) # perform a job after a certain delay
56
+ ```
57
+
58
+ You can also specify a specific queue to be used
59
+
60
+ ```ruby
61
+ class EasyJob
62
+ include ::Litejob
63
+
64
+ self.queue = :urgent
65
+
66
+ def perform(params)
67
+ # do stuff
68
+ end
69
+ end
70
+ ```
27
71
 
28
72
  ## Development
29
73
 
@@ -33,7 +77,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
33
77
 
34
78
  ## Contributing
35
79
 
36
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/litejob. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/litejob/blob/main/CODE_OF_CONDUCT.md).
80
+ Bug reports and pull requests are welcome on GitHub at https://github.com/litestack-ruby/litejob. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/litestack-ruby/litejob/blob/main/CODE_OF_CONDUCT.md).
37
81
 
38
82
  ## License
39
83
 
@@ -41,4 +85,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
41
85
 
42
86
  ## Code of Conduct
43
87
 
44
- Everyone interacting in the Litejob project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/litejob/blob/main/CODE_OF_CONDUCT.md).
88
+ Everyone interacting in the Litejob project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/litestack-ruby/litejob/blob/main/CODE_OF_CONDUCT.md).
@@ -15,11 +15,14 @@ module Litejob
15
15
  attempts = options[:attempts] || 5
16
16
  queue = options[:queue]
17
17
  payload = JSON.dump({class: jobclass, params: params, attempts: attempts, queue: queue})
18
- atomic_push(payload, delay, queue)
18
+ job_id, job_queue = atomic_push(payload, delay, queue)
19
+ Litejob.logger.info("[litejob]:[ENQ] queue=#{job_queue} class=#{jobclass} job=#{job_id}")
20
+ [job_id, job_queue]
19
21
  end
20
22
 
21
23
  def delete(id)
22
24
  payload = @queue.delete(id)
25
+ Litejob.logger.info("[litejob]:[DEL] job=#{id}")
23
26
  JSON.parse(payload)
24
27
  end
25
28
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "client"
4
+
5
+ module Litejob
6
+ # Litejob::Concern is responsible for providing an interface to job classes
7
+ module Concern
8
+ def perform_async(*params)
9
+ @litejob_options ||= {}
10
+ client.push(name, params, @litejob_options.merge(delay: 0, queue: queue_name))
11
+ end
12
+
13
+ def perform_at(time, *params)
14
+ @litejob_options ||= {}
15
+ delay = time.to_i - Time.now.to_i
16
+ client.push(name, params, @litejob_options.merge(delay: delay, queue: queue_name))
17
+ end
18
+
19
+ def perform_in(delay, *params)
20
+ @litejob_options ||= {}
21
+ client.push(name, params, @litejob_options.merge(delay: delay, queue: queue_name))
22
+ end
23
+ alias_method :perform_after, :perform_in
24
+
25
+ def delete(id)
26
+ client.delete(id)
27
+ end
28
+
29
+ def queue_as(queue_name)
30
+ @queue_name = queue_name.to_s
31
+ end
32
+
33
+ def litejob_options(options)
34
+ @litejob_options = options
35
+ end
36
+
37
+ private
38
+
39
+ def queue_name
40
+ @queue_name || "default"
41
+ end
42
+
43
+ def client
44
+ @client ||= Client.new
45
+ end
46
+ end
47
+ end
@@ -6,36 +6,67 @@ require "litequeue"
6
6
  module Litejob
7
7
  # Litejob::Processor is responsible for processing job payloads
8
8
  class Processor
9
- def initialize(payload)
10
- @payload = payload
11
- @queue = Litequeue.instance
9
+ def initialize(queue, id, serialized_job)
10
+ @queue = queue
11
+ @id = id
12
+ @serialized_job = serialized_job
13
+ @job_hash = JSON.parse(@serialized_job)
14
+ @litequeue = Litequeue.instance
15
+
16
+ set_log_context!(queue: @queue, class: @job_hash["class"], job: @id)
12
17
  end
13
18
 
14
19
  def repush(id, job, delay = 0, queue = nil)
15
- @queue.repush(id, JSON.dump(job), queue: queue, delay: delay)
20
+ @litequeue.repush(id, JSON.dump(job), queue: queue, delay: delay)
16
21
  end
17
22
 
18
23
  def process!
19
- id, serialized_job = @payload
20
- job_hash = JSON.parse(serialized_job)
21
- klass = Object.const_get(job_hash["class"])
24
+ log(:deq)
25
+ klass = Object.const_get(@job_hash["class"])
22
26
  instance = klass.new
23
27
 
24
28
  begin
25
- instance.perform(*job_hash["params"])
26
- rescue
27
- if job_hash["retries_left"] == 0
28
- repush(id, job_hash, 0, "_dead")
29
+ instance.perform(*@job_hash["params"])
30
+ log(:end)
31
+ rescue => e
32
+ if @job_hash["retries_left"] == 0
33
+ err(e, "retries exhausted, moving to _dead queue")
34
+ repush(@id, @job_hash, 0, "_dead")
29
35
  else
30
- job_hash["retries_left"] ||= job_hash["attempts"]
31
- job_hash["retries_left"] -= 1
32
- retry_delay = (job_hash["attempts"] - job_hash["retries_left"]) * 0.1
33
- repush(id, job_hash, retry_delay, job_hash["queue"])
36
+ @job_hash["retries_left"] ||= @job_hash["attempts"]
37
+ @job_hash["retries_left"] -= 1
38
+ retry_delay = (@job_hash["attempts"] - @job_hash["retries_left"]) * 0.1
39
+ err(e, "retrying in #{retry_delay} seconds")
40
+ repush(@id, @job_hash, retry_delay, @job_hash["queue"])
34
41
  end
35
42
  end
36
- rescue => exception # standard:disable Lint/UselessRescue
43
+ rescue => e
37
44
  # this is an error in the extraction of job info, retrying here will not be useful
38
- raise exception
45
+ err(e, "while processing job=#{@serialized_job}")
46
+ raise e
47
+ end
48
+
49
+ private
50
+
51
+ def set_log_context!(**attributes)
52
+ @log_context = attributes.map { |k, v| [k, v].join("=") }.join(" ")
53
+ end
54
+
55
+ def log(event, msg: nil)
56
+ prefix = "[litejob]:[#{event.to_s.upcase}]"
57
+
58
+ Litejob.logger.info [prefix, @log_context, msg].compact.join(" ")
59
+ end
60
+
61
+ def err(exception, msg = nil)
62
+ prefix = "[litejob]:[ERR]"
63
+ error_context = if exception.message == exception.class.name
64
+ "failed with #<#{exception.class.name}>"
65
+ else
66
+ "failed with #{exception.inspect}"
67
+ end
68
+
69
+ Litejob.logger.error [prefix, @log_context, error_context, msg].compact.join(" ")
39
70
  end
40
71
  end
41
72
  end
@@ -6,16 +6,14 @@ require_relative "processor"
6
6
 
7
7
  module Litejob
8
8
  # Litejob::Server is responsible for popping job payloads from the SQLite queue.
9
- # :nocov:
10
9
  class Server
11
- # TODO: make queues use [["default", 1]]
12
- # TODO: make queue priorities optional
13
- def initialize(queues)
10
+ def initialize(queues = ["default"])
14
11
  @queue = Litequeue.instance
15
12
  @scheduler = Litescheduler.instance
16
13
  @queues = queues
17
14
  # group and order queues according to their priority
18
15
  @prioritized_queues = queues.each_with_object({}) do |(name, priority, spawns), memo|
16
+ priority ||= 5
19
17
  memo[priority] ||= []
20
18
  memo[priority] << [name, spawns == "spawn"]
21
19
  end.sort_by do |priority, _|
@@ -37,6 +35,7 @@ module Litejob
37
35
 
38
36
  def run!
39
37
  @scheduler.spawn do
38
+ Litejob.logger.info("[litejob]:[RUN] id=#{@scheduler.context.object_id}")
40
39
  worker_sleep_index = 0
41
40
  while @running
42
41
  processed = 0
@@ -47,7 +46,8 @@ module Litejob
47
46
  batched += 1
48
47
  processed += 1
49
48
 
50
- processor = Processor.new(payload)
49
+ id, serialized_job = payload
50
+ processor = Processor.new(queue, id, serialized_job)
51
51
  processor.process!
52
52
 
53
53
  # give other contexts a chance to run here
@@ -66,5 +66,4 @@ module Litejob
66
66
  end
67
67
  end
68
68
  end
69
- # :nocov:
70
69
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litejob
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/litejob.rb CHANGED
@@ -1,53 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "litejob/version"
4
- require_relative "litejob/client"
4
+ require_relative "litejob/concern"
5
5
  require_relative "litejob/server"
6
+ require "logger"
6
7
 
7
- # Litejob is responsible for providing an interface to job classes
8
8
  module Litejob
9
9
  def self.included(klass)
10
- klass.extend(ClassMethods)
10
+ klass.extend(Concern)
11
11
  end
12
12
 
13
- module ClassMethods
14
- def perform_async(*params)
15
- @litejob_options ||= {}
16
- client.push(name, params, @litejob_options.merge(delay: 0, queue: queue_name))
17
- end
13
+ Configuration = Struct.new(:logger)
18
14
 
19
- def perform_at(time, *params)
20
- @litejob_options ||= {}
21
- delay = time.to_i - Time.now.to_i
22
- client.push(name, params, @litejob_options.merge(delay: delay, queue: queue_name))
23
- end
24
-
25
- def perform_in(delay, *params)
26
- @litejob_options ||= {}
27
- client.push(name, params, @litejob_options.merge(delay: delay, queue: queue_name))
28
- end
29
- alias_method :perform_after, :perform_in
30
-
31
- def delete(id)
32
- client.delete(id)
33
- end
34
-
35
- def queue_as(queue_name)
36
- @queue_name = queue_name.to_s
37
- end
38
-
39
- def litejob_options(options)
40
- @litejob_options = options
41
- end
42
-
43
- private
15
+ def self.configuration
16
+ @configuration ||= Configuration.new(
17
+ _logger = Logger.new($stdout)
18
+ )
19
+ end
44
20
 
45
- def queue_name
46
- @queue_name || "default"
47
- end
21
+ def self.configure
22
+ yield(configuration)
23
+ end
48
24
 
49
- def client
50
- @client ||= Client.new
51
- end
25
+ def self.logger
26
+ configuration.logger
52
27
  end
53
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litejob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Hassan
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-08-13 00:00:00.000000000 Z
12
+ date: 2023-08-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: litescheduler
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: 0.2.0
34
+ version: 0.2.1
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: 0.2.0
41
+ version: 0.2.1
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: simplecov
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -71,6 +71,7 @@ files:
71
71
  - Rakefile
72
72
  - lib/litejob.rb
73
73
  - lib/litejob/client.rb
74
+ - lib/litejob/concern.rb
74
75
  - lib/litejob/processor.rb
75
76
  - lib/litejob/server.rb
76
77
  - lib/litejob/version.rb