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 +4 -4
- data/.standard.yml +4 -1
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +20 -1
- data/README.md +5 -0
- data/Rakefile +28 -0
- data/lib/litejob/client.rb +44 -0
- data/lib/litejob/processor.rb +41 -0
- data/lib/litejob/server.rb +67 -0
- data/lib/litejob/version.rb +1 -1
- data/lib/litejob.rb +46 -2
- metadata +48 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78cf5ea54febd08b520ed3d25c27ebbf3e83ec04653a52a351db0366dbf7d4bb
|
4
|
+
data.tar.gz: 8bfcfd530be21fc277140f160568df112173d88b85bd89fbb7d6ef26d612599f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84dc5fbc75d0ec788d6c2a2268cbf01232b7a4ca4ff01d2600348a25c2d2129ecae7d08d0bdf489039409cfa19a786da49981ac6ee21f2bc6407ddf427a5d688
|
7
|
+
data.tar.gz: 03c820e84b226b479b9d2f5180541c788f691c11ecd32b78b304cb6b49293f85f19d02287818907bfe8385bc5ed5c5d30b5e2b34799d65b7e710d692bfdda653
|
data/.standard.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,15 +1,24 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
litejob (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
|
data/lib/litejob/version.rb
CHANGED
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
|
-
|
7
|
-
|
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.
|
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-
|
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
|