litejob 0.1.0 → 0.2.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: 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