litejob 0.1.0 → 0.2.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: f0cd6ee9a1f3896b0aa44f935258c92d3326661ffb09c42c143095fc60c8ea08
4
- data.tar.gz: 302ea0cbf170c7c07c4384983bb5801af794f2d185db77064ff1b855a3693c95
3
+ metadata.gz: 78cf5ea54febd08b520ed3d25c27ebbf3e83ec04653a52a351db0366dbf7d4bb
4
+ data.tar.gz: 8bfcfd530be21fc277140f160568df112173d88b85bd89fbb7d6ef26d612599f
5
5
  SHA512:
6
- metadata.gz: 8f3d9837dd1ef417645e6dd46e587a23565a0e0ac7e900410b44b009b89d4c8ecbe1b058aad6b5782df550992712ab25fde799b9c249bf153dc0c7beaa7016aa
7
- data.tar.gz: 2f9647c30eb7d8af132df8baf0266f409ed9523f1ec848a161919be3de7cec76de0b14003661b140c1b619f229c87df020d44fde6dae93e1db8fae4d38f81ada
6
+ metadata.gz: 84dc5fbc75d0ec788d6c2a2268cbf01232b7a4ca4ff01d2600348a25c2d2129ecae7d08d0bdf489039409cfa19a786da49981ac6ee21f2bc6407ddf427a5d688
7
+ data.tar.gz: 03c820e84b226b479b9d2f5180541c788f691c11ecd32b78b304cb6b49293f85f19d02287818907bfe8385bc5ed5c5d30b5e2b34799d65b7e710d692bfdda653
data/.standard.yml CHANGED
@@ -1,3 +1,6 @@
1
1
  # For available configuration options, see:
2
2
  # https://github.com/testdouble/standard
3
- ruby_version: 2.6
3
+ ruby_version: 3.2
4
+ ignore:
5
+ - 'test/**/*':
6
+ - Style/GlobalVars
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-08-13
4
+
5
+ - Initial release of usable code
6
+
3
7
  ## [0.1.0] - 2023-08-09
4
8
 
5
9
  - Initial release
data/Gemfile.lock CHANGED
@@ -1,15 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- litejob (0.1.0)
4
+ litejob (0.2.0)
5
+ litequeue (>= 0.2.0)
6
+ litescheduler (>= 0.2.1)
5
7
 
6
8
  GEM
7
9
  remote: https://rubygems.org/
8
10
  specs:
9
11
  ast (2.4.2)
12
+ docile (1.4.0)
10
13
  json (2.6.3)
11
14
  language_server-protocol (3.17.0.3)
12
15
  lint_roller (1.1.0)
16
+ litedb (0.2.1)
17
+ litescheduler (>= 0.2.0)
18
+ sqlite3 (>= 1.5.0)
19
+ litequeue (0.2.0)
20
+ litedb (>= 0.2.1)
21
+ litescheduler (0.2.1)
13
22
  minitest (5.19.0)
14
23
  parallel (1.23.0)
15
24
  parser (3.2.2.3)
@@ -36,6 +45,14 @@ GEM
36
45
  rubocop (>= 1.7.0, < 2.0)
37
46
  rubocop-ast (>= 0.4.0)
38
47
  ruby-progressbar (1.13.0)
48
+ simplecov (0.22.0)
49
+ docile (~> 1.1)
50
+ simplecov-html (~> 0.11)
51
+ simplecov_json_formatter (~> 0.1)
52
+ simplecov-html (0.12.3)
53
+ simplecov_json_formatter (0.1.4)
54
+ sqlite3 (1.6.3-arm64-darwin)
55
+ sqlite3 (1.6.3-x86_64-linux)
39
56
  standard (1.30.1)
40
57
  language_server-protocol (~> 3.17.0.2)
41
58
  lint_roller (~> 1.0)
@@ -52,11 +69,13 @@ GEM
52
69
 
53
70
  PLATFORMS
54
71
  arm64-darwin-21
72
+ x86_64-linux
55
73
 
56
74
  DEPENDENCIES
57
75
  litejob!
58
76
  minitest (~> 5.0)
59
77
  rake (~> 13.0)
78
+ simplecov
60
79
  standard (~> 1.3)
61
80
 
62
81
  BUNDLED WITH
data/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Litejob
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/litejob.svg)](https://rubygems.org/gems/litejob)
4
+ [![Gem Downloads](https://img.shields.io/gem/dt/litejob)](https://rubygems.org/gems/litejob)
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)
7
+
3
8
  TODO: Delete this and the text below, and describe your gem
4
9
 
5
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.
data/Rakefile CHANGED
@@ -9,6 +9,34 @@ Rake::TestTask.new(:test) do |t|
9
9
  t.test_files = FileList["test/**/test_*.rb"]
10
10
  end
11
11
 
12
+ desc "Update the README code coverage badge"
13
+ task :update_readme_coverage_badge do
14
+ require "json"
15
+
16
+ next unless File.exist?("coverage/.last_run.json")
17
+
18
+ last_run_coverage = JSON.load_file("coverage/.last_run.json")
19
+ line_coverage = last_run_coverage.dig("result", "line")
20
+ branch_coverage = last_run_coverage.dig("result", "branch")
21
+ average_coverage = [(branch_coverage * 1), (line_coverage * 1.5)].sum.fdiv(2.5).round
22
+ badge_color = if average_coverage >= 75
23
+ :brightgreen
24
+ else
25
+ :red
26
+ end
27
+
28
+ coverage_badge_re = /!\[Coverage\]\(https:\/\/img.shields.io\/badge\/code_coverage-(.*?)\)/
29
+ last_run_coverage_badge = "![Coverage](https://img.shields.io/badge/code_coverage-#{average_coverage}%25-#{badge_color})"
30
+
31
+ new_readme = File.read("README.md").gsub(coverage_badge_re, last_run_coverage_badge)
32
+
33
+ File.write("README.md", new_readme)
34
+
35
+ puts "Updated README code coverage badge to show #{average_coverage}% coverage."
36
+ end
37
+
38
+ task cov: %i[test update_readme_coverage_badge]
39
+
12
40
  require "standard/rake"
13
41
 
14
42
  task default: %i[test standard]
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "litequeue"
5
+
6
+ module Litejob
7
+ # Litejob::Client is responsible for pushing job payloads to the SQLite queue.
8
+ class Client
9
+ def initialize
10
+ @queue = Litequeue.instance
11
+ end
12
+
13
+ def push(jobclass, params, options = {})
14
+ delay = options[:delay] || 0
15
+ attempts = options[:attempts] || 5
16
+ queue = options[:queue]
17
+ payload = JSON.dump({class: jobclass, params: params, attempts: attempts, queue: queue})
18
+ atomic_push(payload, delay, queue)
19
+ end
20
+
21
+ def delete(id)
22
+ payload = @queue.delete(id)
23
+ JSON.parse(payload)
24
+ end
25
+
26
+ private
27
+
28
+ def atomic_push(payload, delay, queue)
29
+ retryable = true
30
+ begin
31
+ @queue.push(payload, queue: queue, delay: delay)
32
+ rescue => exception
33
+ # Retry once retryable exceptions
34
+ # https://github.com/sparklemotion/sqlite3-ruby/blob/master/lib/sqlite3/errors.rb
35
+ if retryable && exception.is_a?(SQLite3::BusyException)
36
+ retryable = false
37
+ retry
38
+ else
39
+ raise exception
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "litequeue"
5
+
6
+ module Litejob
7
+ # Litejob::Processor is responsible for processing job payloads
8
+ class Processor
9
+ def initialize(payload)
10
+ @payload = payload
11
+ @queue = Litequeue.instance
12
+ end
13
+
14
+ def repush(id, job, delay = 0, queue = nil)
15
+ @queue.repush(id, JSON.dump(job), queue: queue, delay: delay)
16
+ end
17
+
18
+ def process!
19
+ id, serialized_job = @payload
20
+ job_hash = JSON.parse(serialized_job)
21
+ klass = Object.const_get(job_hash["class"])
22
+ instance = klass.new
23
+
24
+ begin
25
+ instance.perform(*job_hash["params"])
26
+ rescue
27
+ if job_hash["retries_left"] == 0
28
+ repush(id, job_hash, 0, "_dead")
29
+ 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"])
34
+ end
35
+ end
36
+ rescue => exception # standard:disable Lint/UselessRescue
37
+ # this is an error in the extraction of job info, retrying here will not be useful
38
+ raise exception
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "litequeue"
4
+ require "litescheduler"
5
+
6
+ module Litejob
7
+ # Litejob::Server is responsible for popping job payloads from the SQLite queue.
8
+ # :nocov:
9
+ class Server
10
+ def initialize(queues)
11
+ @queue = Litequeue.instance
12
+ @scheduler = Litescheduler.instance
13
+ @queues = queues
14
+ # group and order queues according to their priority
15
+ @prioritized_queues = queues.each_with_object({}) do |(name, priority, spawns), memo|
16
+ memo[priority] ||= []
17
+ memo[priority] << [name, spawns == "spawn"]
18
+ end.sort_by do |priority, _|
19
+ -priority
20
+ end
21
+ @running = true
22
+ @sleep_intervals = [0.001, 0.005, 0.025, 0.125, 0.625, 1.0, 2.0]
23
+ run!
24
+ end
25
+
26
+ def pop(queue)
27
+ result = @queue.pop(queue: queue)
28
+
29
+ return result[0] if result.length == 1
30
+ return false if result.empty?
31
+
32
+ result
33
+ end
34
+
35
+ def run!
36
+ @scheduler.spawn do
37
+ worker_sleep_index = 0
38
+ while @running
39
+ processed = 0
40
+ @prioritized_queues.each do |priority, queues|
41
+ queues.each do |queue, spawns|
42
+ batched = 0
43
+ while (batched < priority) && (payload = pop(queue))
44
+ batched += 1
45
+ processed += 1
46
+
47
+ processor = Processor.new(payload)
48
+ processor.process!
49
+
50
+ # give other contexts a chance to run here
51
+ @scheduler.switch
52
+ end
53
+ end
54
+
55
+ if processed == 0
56
+ sleep @sleep_intervals[worker_sleep_index]
57
+ worker_sleep_index += 1 if worker_sleep_index < @sleep_intervals.length - 1
58
+ else
59
+ worker_sleep_index = 0 # reset the index
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ # :nocov:
67
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litejob
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/litejob.rb CHANGED
@@ -1,8 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "litejob/version"
4
+ require_relative "litejob/client"
4
5
 
6
+ # Litejob is responsible for providing an interface to job classes
5
7
  module Litejob
6
- class Error < StandardError; end
7
- # Your code goes here...
8
+ def self.included(klass)
9
+ klass.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def perform_async(*params)
14
+ @litejob_options ||= {}
15
+ client.push(name, params, @litejob_options.merge(delay: 0, queue: queue_name))
16
+ end
17
+
18
+ def perform_at(time, *params)
19
+ @litejob_options ||= {}
20
+ delay = time.to_i - Time.now.to_i
21
+ client.push(name, params, @litejob_options.merge(delay: delay, queue: queue_name))
22
+ end
23
+
24
+ def perform_in(delay, *params)
25
+ @litejob_options ||= {}
26
+ client.push(name, params, @litejob_options.merge(delay: delay, queue: queue_name))
27
+ end
28
+ alias_method :perform_after, :perform_in
29
+
30
+ def delete(id)
31
+ client.delete(id)
32
+ end
33
+
34
+ def queue_as(queue_name)
35
+ @queue_name = queue_name.to_s
36
+ end
37
+
38
+ def litejob_options(options)
39
+ @litejob_options = options
40
+ end
41
+
42
+ private
43
+
44
+ def queue_name
45
+ @queue_name || "default"
46
+ end
47
+
48
+ def client
49
+ @client ||= Client.new
50
+ end
51
+ end
8
52
  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.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Hassan
@@ -9,8 +9,50 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-08-09 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2023-08-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: litescheduler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.2.1
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.2.1
28
+ - !ruby/object:Gem::Dependency
29
+ name: litequeue
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.2.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.2.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: simplecov
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
14
56
  description:
15
57
  email:
16
58
  - oldmoe@gmail.com
@@ -28,6 +70,9 @@ files:
28
70
  - README.md
29
71
  - Rakefile
30
72
  - lib/litejob.rb
73
+ - lib/litejob/client.rb
74
+ - lib/litejob/processor.rb
75
+ - lib/litejob/server.rb
31
76
  - lib/litejob/version.rb
32
77
  - sig/litejob.rbs
33
78
  homepage: https://github.com/litestack-ruby/litejob