conflow 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/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/lib/conflow.rb +2 -0
- data/lib/conflow/flow.rb +12 -1
- data/lib/conflow/flow/job_handler.rb +4 -2
- data/lib/conflow/redis/complete_job_script.rb +7 -4
- data/lib/conflow/redis/field.rb +0 -21
- data/lib/conflow/redis/model.rb +50 -8
- data/lib/conflow/redis/queue_jobs_script.rb +31 -0
- data/lib/conflow/redis/set_field.rb +43 -0
- data/lib/conflow/redis/sorted_set_field.rb +0 -37
- data/lib/conflow/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b85fd52b7c1a2161135fc389c45b3f1e6754462a17ef30463435f1c919685950
|
4
|
+
data.tar.gz: a5f05aa2c69b8426a72dc1070834ec74120a5053a74f93250098765c461105ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3291b765c557576651568d735b3bad21e7a191919a858e9365bbf7da97d632d09f99f3c7a2f35d3133b03d3e82a9802489dd1b63542bd1343325f89c74c22845
|
7
|
+
data.tar.gz: a6797479cf68b310791680b2709ed54ebb9e372e26e87a1ffb2e685418a33eefa1b621bf5f68aa6ec05024c54d58f427123335ba204049a3fd7bfba4aa6d026e
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/lib/conflow.rb
CHANGED
@@ -12,6 +12,7 @@ require "conflow/redis/value_field"
|
|
12
12
|
require "conflow/redis/hash_field"
|
13
13
|
require "conflow/redis/array_field"
|
14
14
|
require "conflow/redis/sorted_set_field"
|
15
|
+
require "conflow/redis/set_field"
|
15
16
|
require "conflow/redis/field_builder"
|
16
17
|
require "conflow/redis/model"
|
17
18
|
require "conflow/redis/identifier"
|
@@ -20,6 +21,7 @@ require "conflow/redis/findable"
|
|
20
21
|
require "conflow/redis/script"
|
21
22
|
require "conflow/redis/add_job_script"
|
22
23
|
require "conflow/redis/complete_job_script"
|
24
|
+
require "conflow/redis/queue_jobs_script"
|
23
25
|
|
24
26
|
require "conflow/job"
|
25
27
|
require "conflow/flow/job_handler"
|
data/lib/conflow/flow.rb
CHANGED
@@ -16,6 +16,10 @@ module Conflow
|
|
16
16
|
# are fulfilled.
|
17
17
|
# @return [Conflow::Redis::SortedSetField] Set of jobs to be performed
|
18
18
|
#
|
19
|
+
# @!attribute [r] queued_jobs
|
20
|
+
# Set of jobs that are currently queued (and not yet finished).
|
21
|
+
# @return [Conflow::Redis::SetField] Set of queued jobs
|
22
|
+
#
|
19
23
|
# @!method queue(job)
|
20
24
|
# @abstract
|
21
25
|
# Queues job to be performed. Both id of the flow and id of the job must be preserved
|
@@ -35,7 +39,8 @@ module Conflow
|
|
35
39
|
include JobHandler
|
36
40
|
|
37
41
|
has_many :jobs, Conflow::Job
|
38
|
-
field :
|
42
|
+
field :queued_jobs, :set
|
43
|
+
field :indegree, :sorted_set
|
39
44
|
|
40
45
|
# Create new flow with given parameters
|
41
46
|
# @example Simple configurable flow
|
@@ -52,6 +57,12 @@ module Conflow
|
|
52
57
|
new.tap { |flow| flow.configure(*args) }
|
53
58
|
end
|
54
59
|
|
60
|
+
# Returns whether or not the flow is finished (all jobs were processed)
|
61
|
+
# @return [Boolean] true if no pending jobs
|
62
|
+
def finished?
|
63
|
+
queued_jobs.size.zero? && indegree.size.zero?
|
64
|
+
end
|
65
|
+
|
55
66
|
# @abstract
|
56
67
|
# Override this method in order to contain your flow definition inside the class.
|
57
68
|
# This method will be called if flow is created using {.create} method.
|
@@ -18,18 +18,21 @@ module Conflow
|
|
18
18
|
after = prepare_dependencies(after)
|
19
19
|
|
20
20
|
call_script(Conflow::Redis::AddJobScript, job, after: after)
|
21
|
+
queue_available_jobs
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
def finish(job, result = nil)
|
25
26
|
send(job.hook.to_s, result) unless job.hook.nil?
|
26
27
|
call_script(Conflow::Redis::CompleteJobScript, job)
|
28
|
+
queue_available_jobs
|
29
|
+
destroy! if finished?
|
27
30
|
end
|
28
31
|
|
29
32
|
private
|
30
33
|
|
31
34
|
def queue_available_jobs
|
32
|
-
|
35
|
+
call_script(Conflow::Redis::QueueJobsScript)&.each do |job_id|
|
33
36
|
queue Conflow::Job.new(job_id)
|
34
37
|
end
|
35
38
|
end
|
@@ -44,7 +47,6 @@ module Conflow
|
|
44
47
|
|
45
48
|
def call_script(script, *args)
|
46
49
|
script.call(self, *args)
|
47
|
-
queue_available_jobs
|
48
50
|
end
|
49
51
|
|
50
52
|
def prepare_dependencies(dependencies)
|
@@ -6,15 +6,18 @@ module Conflow
|
|
6
6
|
class CompleteJobScript < Script
|
7
7
|
self.script = <<~LUA
|
8
8
|
local indegree_set = KEYS[1]
|
9
|
-
local
|
9
|
+
local queued_jobs = KEYS[2]
|
10
|
+
local job_key = KEYS[3]
|
11
|
+
local job_id = ARGV[1]
|
10
12
|
|
11
|
-
local successors = redis.call('lrange',
|
13
|
+
local successors = redis.call('lrange', job_key .. ':successor_ids', 0, -1)
|
12
14
|
|
13
15
|
for i=1,#successors do
|
14
16
|
redis.call('zincrby', indegree_set, -1, successors[i])
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
redis.call('srem', queued_jobs, job_id)
|
20
|
+
return redis.call('set', job_key .. ':status', '1')
|
18
21
|
LUA
|
19
22
|
|
20
23
|
class << self
|
@@ -24,7 +27,7 @@ module Conflow
|
|
24
27
|
# @param flow [Conflow::Flow] Flow to which job belongs to
|
25
28
|
# @param job [Conflow::Job] Job to be marked as completed
|
26
29
|
def call(flow, job)
|
27
|
-
super([flow.indegree.key, job.key])
|
30
|
+
super([flow.indegree.key, flow.queued_jobs.key, job.key], [job.id])
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
data/lib/conflow/redis/field.rb
CHANGED
@@ -20,27 +20,6 @@ module Conflow
|
|
20
20
|
def command(command, args)
|
21
21
|
redis.with { |conn| conn.send(command, *args) }
|
22
22
|
end
|
23
|
-
|
24
|
-
def transaction(max_retries: 5)
|
25
|
-
result = redis.with do |conn|
|
26
|
-
retryable(max_retries, conn) do
|
27
|
-
conn.watch(key) do
|
28
|
-
conn.multi { |multi| yield(multi) }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
raise ::Redis::CommandError, "Transaction failed #{max_retries} times" unless result
|
34
|
-
result
|
35
|
-
end
|
36
|
-
|
37
|
-
def retryable(max_retries, *args)
|
38
|
-
loop do
|
39
|
-
result = yield(*args)
|
40
|
-
break result if result || max_retries.zero?
|
41
|
-
max_retries -= 1
|
42
|
-
end
|
43
|
-
end
|
44
23
|
end
|
45
24
|
end
|
46
25
|
end
|
data/lib/conflow/redis/model.rb
CHANGED
@@ -16,16 +16,49 @@ module Conflow
|
|
16
16
|
other.is_a?(Model) ? key == other.key : super
|
17
17
|
end
|
18
18
|
|
19
|
+
# Removes this and all related models.
|
20
|
+
# @param execute [Boolean] if false, only returns keys that would be removed
|
21
|
+
def destroy!(execute: true)
|
22
|
+
keys = self.class.fields.map { |name| send(name).key }
|
23
|
+
related_keys = self.class.relations.map(&method(:collect_relation_keys))
|
24
|
+
|
25
|
+
(keys + related_keys).flatten.tap do |key_list|
|
26
|
+
command :del, [key_list] if execute && key_list.any?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def collect_relation_keys(relation)
|
33
|
+
send(relation).map { |object| object.destroy!(execute: false) }
|
34
|
+
end
|
35
|
+
|
19
36
|
# Methods for defining fields on model
|
20
37
|
module ClassMethods
|
38
|
+
# Maps types (option for {Conflow::Redis::Model::ClassMethods#field}) to specific type field
|
39
|
+
ALLOWED_TYPES = {
|
40
|
+
hash: Conflow::Redis::HashField,
|
41
|
+
array: Conflow::Redis::ArrayField,
|
42
|
+
value: Conflow::Redis::ValueField,
|
43
|
+
sorted_set: Conflow::Redis::SortedSetField,
|
44
|
+
set: Conflow::Redis::SetField
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# Copies field and relations information
|
48
|
+
def inherited(subclass)
|
49
|
+
subclass.instance_variable_set("@fields", fields.dup)
|
50
|
+
subclass.instance_variable_set("@relations", relations.dup)
|
51
|
+
end
|
52
|
+
|
21
53
|
# Defines Redis field accessors.
|
22
54
|
# @param name [Symbol] name of the field
|
23
|
-
# @param type [:hash, :array, :value, :sorted_set] type of the new field
|
55
|
+
# @param type [:hash, :array, :value, :sorted_set, :set] type of the new field
|
24
56
|
#
|
25
57
|
# @see Conflow::Redis::HashField
|
26
58
|
# @see Conflow::Redis::ArrayField
|
27
59
|
# @see Conflow::Redis::ValueField
|
28
60
|
# @see Conflow::Redis::SortedSetField
|
61
|
+
# @see Conflow::Redis::SetField
|
29
62
|
# @example
|
30
63
|
# model_class.field :data, :hash
|
31
64
|
# instance = model_class.new
|
@@ -33,13 +66,11 @@ module Conflow
|
|
33
66
|
# instance.hash = { something: "else"}
|
34
67
|
# instance.hash["something"] #=> "else"
|
35
68
|
def field(name, type)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
else raise ArgumentError, "Unknown type: #{type}. Should be one of: [:hash, :array]"
|
42
|
-
end
|
69
|
+
type_class = ALLOWED_TYPES[type]
|
70
|
+
raise ArgumentError, "Unknown type: #{type}. Should be one of: #{ALLOWED_TYPES.keys.inspect}" unless type_class
|
71
|
+
|
72
|
+
fields << name
|
73
|
+
FieldBuilder.new(name, type_class).call(self)
|
43
74
|
end
|
44
75
|
|
45
76
|
# Convienience method for defining relation-like accessor.
|
@@ -47,8 +78,19 @@ module Conflow
|
|
47
78
|
# has_many :jobs, Conflow::Job # defines #job_ids and #jobs
|
48
79
|
def has_many(name, klass, field_name: "#{name.to_s.chop}_ids")
|
49
80
|
field(field_name, :array)
|
81
|
+
relations << name
|
50
82
|
define_method(name) { send(field_name).map { |id| klass.new(id) } }
|
51
83
|
end
|
84
|
+
|
85
|
+
# @return [Array<Symbol>] fields defined on this class
|
86
|
+
def fields
|
87
|
+
@fields ||= []
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [Array<Symbol>] relations defined on this class
|
91
|
+
def relations
|
92
|
+
@relations ||= []
|
93
|
+
end
|
52
94
|
end
|
53
95
|
end
|
54
96
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Conflow
|
4
|
+
module Redis
|
5
|
+
# Removes jobs from indegree set, adds them to queued list, and returns IDs of queued jobs.
|
6
|
+
class QueueJobsScript < Script
|
7
|
+
self.script = <<~LUA
|
8
|
+
local indegree_set = KEYS[1]
|
9
|
+
local queued_set = KEYS[2]
|
10
|
+
|
11
|
+
local ids = redis.call('zrangebyscore', indegree_set, 0, 0)
|
12
|
+
redis.call('zremrangebyscore', indegree_set, 0, 0)
|
13
|
+
|
14
|
+
if #ids ~= 0 then
|
15
|
+
redis.call('sadd', queued_set, unpack(ids))
|
16
|
+
end
|
17
|
+
|
18
|
+
return ids
|
19
|
+
LUA
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# Call the script.
|
23
|
+
# Script removes jobs which have score 0 in Flow's indegree set and moves them to queued_jobs list.
|
24
|
+
# @param flow [Conflow::Flow] Flow from which jobs should be enqueued
|
25
|
+
def call(flow)
|
26
|
+
super([flow.indegree.key, flow.queued_jobs.key])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Conflow
|
4
|
+
module Redis
|
5
|
+
# Represents Redis list. It's methods mirror most used Set methods.
|
6
|
+
class SetField < Field
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# Adds one or more elements to the set.
|
10
|
+
# @param values [String] list of values to be added
|
11
|
+
# @return [String] Redis response
|
12
|
+
#
|
13
|
+
# @example Adding multiple fields
|
14
|
+
# field.add(:last, :first, :other, :first)
|
15
|
+
def add(*values)
|
16
|
+
command :sadd, [key, values]
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] Number of elements in the set
|
20
|
+
def size
|
21
|
+
command :scard, [key]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Removes old values from the set and overrides them with new.
|
25
|
+
# @param enumerable [Enumerable] new values of the set
|
26
|
+
# @return [String] Redis response
|
27
|
+
def overwrite(enumerable)
|
28
|
+
redis.with do |conn|
|
29
|
+
conn.pipelined do
|
30
|
+
conn.del(key)
|
31
|
+
conn.sadd(key, enumerable)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns array with set values
|
37
|
+
# @return [Array] values from the set
|
38
|
+
def to_a
|
39
|
+
command :smembers, [key]
|
40
|
+
end; alias to_ary to_a
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -55,36 +55,6 @@ module Conflow
|
|
55
55
|
command :zrem, [key, value]
|
56
56
|
end
|
57
57
|
|
58
|
-
# Return elements with given score
|
59
|
-
# @param score [Numeric, Hash]
|
60
|
-
# - when Numeric, only elements with that exact score will be returned
|
61
|
-
# - when Hash, elements within min..max range will be returned. See {https://redis.io/commands/zrange Redis docs}
|
62
|
-
# @option score [String, Numeric] :min minimal score
|
63
|
-
# @option score [String, Numeric] :max maximal score
|
64
|
-
# @return [Array<String>] Elements with given score
|
65
|
-
#
|
66
|
-
# @example with specific score
|
67
|
-
# field.where(score: 2) #=> ["first", "tie"]
|
68
|
-
# @example with only min set
|
69
|
-
# field.where(score: { min: 3 }) #=> ["last", "second"]
|
70
|
-
# @example with both min and max set
|
71
|
-
# field.where(score: { min: 3, max: "(10" }) #=> ["last"]
|
72
|
-
def where(score:)
|
73
|
-
command :zrangebyscore, [key, *prepare_score_bounds(score)]
|
74
|
-
end
|
75
|
-
|
76
|
-
# Removes elements of the set with given score and returns them.
|
77
|
-
# See {where} for details on how to choose score.
|
78
|
-
# @return [Array<String>] Elements with given score
|
79
|
-
def delete_if(score:)
|
80
|
-
score_bounds = prepare_score_bounds(score)
|
81
|
-
|
82
|
-
transaction do |conn|
|
83
|
-
conn.zrangebyscore key, *score_bounds
|
84
|
-
conn.zremrangebyscore key, *score_bounds
|
85
|
-
end[0]
|
86
|
-
end
|
87
|
-
|
88
58
|
# Returns first *n* elements of the sorted set
|
89
59
|
# @param num [Integer] amount of elements to be returned. Defaults to 1.
|
90
60
|
# @return [String, Array<String>] first *num* elements from the set
|
@@ -129,13 +99,6 @@ module Conflow
|
|
129
99
|
result[index * 2 + 1] = value
|
130
100
|
end
|
131
101
|
end
|
132
|
-
|
133
|
-
def prepare_score_bounds(score)
|
134
|
-
case score
|
135
|
-
when Hash then { min: "-inf", max: "+inf" }.merge(score).values
|
136
|
-
when Numeric then [score, score]
|
137
|
-
end
|
138
|
-
end
|
139
102
|
end
|
140
103
|
end
|
141
104
|
end
|
data/lib/conflow/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: conflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michał Begejowicz
|
@@ -158,7 +158,9 @@ files:
|
|
158
158
|
- lib/conflow/redis/hash_field.rb
|
159
159
|
- lib/conflow/redis/identifier.rb
|
160
160
|
- lib/conflow/redis/model.rb
|
161
|
+
- lib/conflow/redis/queue_jobs_script.rb
|
161
162
|
- lib/conflow/redis/script.rb
|
163
|
+
- lib/conflow/redis/set_field.rb
|
162
164
|
- lib/conflow/redis/sorted_set_field.rb
|
163
165
|
- lib/conflow/redis/value_field.rb
|
164
166
|
- lib/conflow/version.rb
|