postjob 0.5.1 → 0.5.2

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
- SHA1:
3
- metadata.gz: 5166951ca6511e2d599b44493288dc1ac43f712a
4
- data.tar.gz: f32f0a3255c782e5998d8c77b68b8e5656f356ae
2
+ SHA256:
3
+ metadata.gz: 4fcc6c18ad2be617abafbdfb1dc6258a435223a0fbd0110d86806b6da47fc5d6
4
+ data.tar.gz: 98a5384f074100eb291d6357993969fea6da7b058177ba513f79329bc558d9c9
5
5
  SHA512:
6
- metadata.gz: 40f67bc7082fc9df09bda3c4425297ffa7ce6ddaed0feef2bb849a3b9e5773a06a6b5349972c8f5c8f0d5c7cefb4cc7b1e0f64ce8da336d52fa0239a5e45fde1
7
- data.tar.gz: d5928e359abfc51dbeab4a1054d435dbba799ba5107296ccd2b65494e2c9853fca350ab88eba9f291ead1736026b74e04371d6830afad77a8b08bb60020a1ed8
6
+ metadata.gz: 212977ecccd8f11df6d0e4950155cc47f18c1d6c979b045109b5f981d5eb1e7a68c90aefae78708385458d102870f3d3c9357d8861fa9a54fd5f58c8db9f4f2b
7
+ data.tar.gz: c31025b4869355c9dd13ed5e119c43133b9ae794eecaa2c12f92eb07c1e476f48c32cefa182757358b7236b64febe34283183d5f492a71116e86569adb36e724
@@ -1,10 +1,14 @@
1
1
  module Postjob::CLI
2
2
  # Prints version info
3
3
  def version
4
- connect_to_database!
5
-
6
4
  gem_version = Gem.loaded_specs["postjob"].version
7
- puts "postjob/ruby: #{gem_version}"
8
- puts "postjob/queue: #{::Postjob::Queue.version}"
5
+ puts "This is postjob (ruby) #{gem_version}"
6
+
7
+ begin
8
+ connect_to_database!
9
+ puts "postjob/queue: #{::Postjob::Queue.version}"
10
+ rescue StandardError
11
+ Postjob.logger.warn "Cannot read postjob schema version. Database might not be configured or migrated."
12
+ end
9
13
  end
10
14
  end
data/lib/postjob/job.rb CHANGED
@@ -5,7 +5,7 @@ require_relative "./record"
5
5
  #
6
6
  class Postjob::Job < Postjob::Record
7
7
  def self.find(job_id)
8
- scope = Postjob::Queue.search(id: job_id)
8
+ scope = Postjob::Queue.search("postjobs", id: job_id)
9
9
  Simple::SQL.ask(scope, into: Postjob::Job)
10
10
  end
11
11
 
@@ -0,0 +1,23 @@
1
+ # This module holds cached database info, to be used by the search functions.
2
+ module Postjob::Queue::DatabaseInfo
3
+ extend self
4
+
5
+ # returns a Hash of column_name => column_type. Names are available both as
6
+ # Symbol and as String.
7
+ def column_types(table_name)
8
+ @column_types ||= {}
9
+ @column_types[table_name] ||= begin
10
+ column_info = ::Simple::SQL::Reflection.column_info(table_name)
11
+ hsh = {}
12
+ column_info.each do |column, rec|
13
+ hsh[column.to_sym] = hsh[column.to_s] = rec.data_type
14
+ end
15
+ hsh
16
+ end
17
+ end
18
+
19
+ def table_columns(table_name)
20
+ @table_columns ||= {}
21
+ @table_columns[table_name] ||= Simple::SQL::Reflection.columns(table_name)
22
+ end
23
+ end
@@ -1,82 +1,120 @@
1
+ require_relative "./database_info"
2
+
1
3
  module Postjob::Queue
2
- DEFAULT_ATTRIBUTES = [
3
- "workflow || COALESCE('@' || workflow_version, '') || args AS job",
4
- "COALESCE((results->0)::varchar, error_message) AS result",
5
- "(now() at time zone 'utc') - created_at AS age",
6
- "updated_at - created_at AS runtime"
7
- ]
8
-
9
- def default_attributes
10
- @default_attributes ||= begin
11
- column_names = Simple::SQL::Reflection.columns("#{SCHEMA_NAME}.postjobs")
12
- column_names + DEFAULT_ATTRIBUTES
13
- end
14
- end
4
+ class ScopeBuilder
5
+ SCHEMA_NAME = ::Postjob::Queue::SCHEMA_NAME
15
6
 
16
- # Builds a search scope (see Simple::SQL::Scope) for the passed in filter criteria.
17
- #
18
- # Note that the search scope is unsorted.
19
- def search(filter = {})
20
- expect! filter => Hash
21
-
22
- # extract options
23
- filter = filter.dup
24
- root_only = filter.delete(:root_only) || false
25
- attributes = filter.delete(:attributes) || default_attributes
26
- expect! attributes => Array
27
-
28
- # build Scope
29
- scope = Simple::SQL::Scope.new("SELECT #{attributes.join(", ")} FROM #{SCHEMA_NAME}.postjobs")
30
- scope = scope.where("root_id=id") if root_only
31
- scope = apply_filters(scope, filter) if filter && !filter.empty?
32
- scope
33
- end
7
+ class << self
8
+ def build(model:, filter:, attributes:)
9
+ builder(model).send(:scope, filter: filter, attributes: attributes)
10
+ end
34
11
 
35
- private
12
+ private
36
13
 
37
- def apply_filters(scope, filter)
38
- filter.each_key do |key|
39
- expect! key => [Symbol, String]
14
+ # Returns the name of the column used for dynamic filtering.
15
+ def dynamic_filter(model)
16
+ case model
17
+ when "postjobs" then "tags"
18
+ else "attributes"
19
+ end
20
+ end
21
+
22
+ def builder(model)
23
+ @builders ||= {}
24
+ @builders[model] ||= new(model: model, dynamic_filter: dynamic_filter(model))
25
+ end
40
26
  end
41
27
 
42
- converted_filters = filter.inject({}) do |hsh, (key, value)|
43
- matches = convert_filter_value(value, key: key)
44
- matches = matches.first if matches.length == 1
45
- hsh.update key => matches
28
+ private
29
+
30
+ def initialize(model:, dynamic_filter:)
31
+ @model = model
32
+ @table_name = "#{SCHEMA_NAME}.#{model}"
33
+ @column_types = ::Postjob::Queue::DatabaseInfo.column_types(@table_name)
34
+ @dynamic_filter = dynamic_filter
46
35
  end
47
36
 
48
- column_filters, tags_filters = converted_filters.partition { |key, _| !column_types[key].nil? }
37
+ def scope(filter:, attributes:)
38
+ expect! filter => [Hash, nil]
39
+ expect! attributes => Array
49
40
 
50
- scope = scope.where(Hash[column_filters])
51
- scope = scope.where(tags: Hash[tags_filters])
52
- scope
53
- end
41
+ # build Scope
42
+ scope = Simple::SQL::Scope.new("SELECT #{attributes.join(", ")} FROM #{@table_name}")
43
+ scope = apply_filters(scope, filter) if filter && !filter.empty?
44
+ scope
45
+ end
46
+
47
+ # apply the filter hash onto the passed in scope. This matches all filters
48
+ # with a name which is matching a column name against the column values.
49
+ # It matches every other filter value against an entry in the
50
+ # dynamic_filter column.
51
+ def apply_filters(scope, filter)
52
+ filter.each_key do |key|
53
+ expect! key => [Symbol, String]
54
+ end
54
55
 
55
- class << self
56
- def column_types
57
- @column_types ||= _column_types
56
+ converted_filters = filter.inject({}) do |hsh, (key, value)|
57
+ matches = convert_filter_value(value, key: key)
58
+ matches = matches.first if matches.length == 1
59
+ hsh.update key => matches
60
+ end
61
+
62
+ column_filters, tags_filters = converted_filters.partition { |key, _| !@column_types[key].nil? }
63
+
64
+ scope = scope.where(Hash[column_filters]) unless column_filters.empty?
65
+ scope = scope.where(@dynamic_filter => Hash[tags_filters]) unless tags_filters.empty?
66
+
67
+ scope
58
68
  end
59
69
 
60
- private
70
+ def convert_filter_value(value, key:)
71
+ value = Array(value)
61
72
 
62
- def _column_types
63
- column_info = ::Simple::SQL::Reflection.column_info("#{SCHEMA_NAME}.postjobs")
64
- hsh = {}
65
- column_info.each do |column, rec|
66
- hsh[column.to_sym] = hsh[column.to_s] = rec.data_type
73
+ case @column_types[key]
74
+ when "bigint" then value.map { |v| Integer(v) }
75
+ when "integer" then value.map { |v| Integer(v) }
76
+ when nil then value.map(&:to_s) + value.grep(/\A-?\d+\z/).map(&:to_i)
77
+ else value.map(&:to_s)
67
78
  end
68
- hsh
69
79
  end
70
80
  end
71
81
 
72
- def convert_filter_value(value, key:)
73
- value = Array(value)
82
+ # Builds a search scope (see Simple::SQL::Scope) for the passed in filter criteria.
83
+ #
84
+ # Parameters:
85
+ #
86
+ # - model: the name of the postjob model, e.g. "postjobs", "events", ..
87
+ # - filter: a Hash of filter values and other options. options are denoted
88
+ # by a Symbol key.
89
+ #
90
+ # Note that the search scope is unsorted.
91
+ def search(model, filter = {})
92
+ expect! model => String
93
+ expect! filter => [Hash, nil]
74
94
 
75
- case Postjob::Queue.column_types[key]
76
- when "bigint" then value.map { |v| Integer(v) }
77
- when "integer" then value.map { |v| Integer(v) }
78
- when nil then value.map(&:to_s) + value.grep(/\A-?\d+\z/).map(&:to_i)
79
- else value.map(&:to_s)
80
- end
95
+ filter = filter ? filter.dup : {}
96
+
97
+ root_only = filter.delete(:root_only)
98
+ attributes = filter.delete(:attributes) || default_search_attributes(model)
99
+
100
+ scope = ScopeBuilder.build(model: model, filter: filter, attributes: attributes)
101
+ scope = scope.where("root_id=id") if root_only && model == "postjobs"
102
+ scope
103
+ end
104
+
105
+ private
106
+
107
+ # describes which attributes should be returned in addition to a model's columns.
108
+ EXTRA_SEARCH_ATTRIBUTES = {
109
+ "postjobs" => [
110
+ "workflow || COALESCE('@' || workflow_version, '') || args AS job",
111
+ "COALESCE((results->0)::varchar, error_message) AS result",
112
+ "(now() at time zone 'utc') - created_at AS age",
113
+ "updated_at - created_at AS runtime"
114
+ ]
115
+ }
116
+
117
+ def default_search_attributes(model)
118
+ DatabaseInfo.table_columns("#{SCHEMA_NAME}.#{model}") + (EXTRA_SEARCH_ATTRIBUTES[model] || [])
81
119
  end
82
120
  end
@@ -14,14 +14,14 @@ end
14
14
 
15
15
  # A worker worker_session
16
16
  class Postjob::WorkerSession < Postjob::Record
17
- HOST_ID_STORE = ".postjob.host_id"
17
+ HOST_ID_PATH = ".postjob.host_id"
18
18
 
19
19
  class << self
20
20
  # Starts a worker session.
21
21
  def start!(workflows_with_versions)
22
22
  worker_session = nil
23
23
 
24
- AtomicStore.with(HOST_ID_STORE) do |host_id|
24
+ AtomicStore.with(HOST_ID_PATH) do |host_id|
25
25
  host_id ||= ::Postjob::Host.register
26
26
  Postjob.logger.debug "Starting worker_session w/host_id #{host_id.inspect}"
27
27
  worker_session = ::Postjob::Queue.start_worker_session(workflows_with_versions, host_id: host_id)
@@ -27,7 +27,7 @@ describe "Postjob::Queue.search" do
27
27
 
28
28
  context "attributes" do
29
29
  it "returns a specific set of attributes" do
30
- scope = Postjob::Queue.search(id: id)
30
+ scope = Postjob::Queue.search("postjobs", id: id)
31
31
  result = Simple::SQL.ask scope, into: Hash
32
32
 
33
33
  expected_keys = [
@@ -47,7 +47,7 @@ describe "Postjob::Queue.search" do
47
47
  end
48
48
 
49
49
  it "with attributes: argument it only returns the specified attributes" do
50
- scope = Postjob::Queue.search(attributes: %w(id))
50
+ scope = Postjob::Queue.search("postjobs", attributes: %w(id))
51
51
  result = Simple::SQL.ask scope, into: Hash
52
52
 
53
53
  expected_keys = [
@@ -64,7 +64,7 @@ describe "Postjob::Queue.search" do
64
64
  end
65
65
 
66
66
  it "returns all entries" do
67
- scope = Postjob::Queue.search
67
+ scope = Postjob::Queue.search("postjobs")
68
68
  result = Simple::SQL.all(scope, into: OpenStruct)
69
69
  expect(result.length).to eq(2)
70
70
  end
@@ -72,46 +72,46 @@ describe "Postjob::Queue.search" do
72
72
 
73
73
  describe "filtering" do
74
74
  it "can find a job by id" do
75
- scope = Postjob::Queue.search(id: id)
75
+ scope = Postjob::Queue.search("postjobs", id: id)
76
76
  result = Simple::SQL.ask scope, into: Hash
77
77
  expect(result[:id]).to eq(id)
78
78
  end
79
79
 
80
80
  it "positive filters by tags" do
81
- scope = Postjob::Queue.search("initial" => "yay")
81
+ scope = Postjob::Queue.search("postjobs", "initial" => "yay")
82
82
  result = Simple::SQL.all scope, into: OpenStruct
83
83
  expect(result.length).to eq(1)
84
84
  expect(result.first.id).to eq(id)
85
85
  end
86
86
 
87
87
  it "negative filters by tags" do
88
- scope = Postjob::Queue.search("initial" => "nay")
88
+ scope = Postjob::Queue.search("postjobs", "initial" => "nay")
89
89
  result = Simple::SQL.all(scope)
90
90
  expect(result.length).to eq(0)
91
91
  end
92
92
 
93
93
  it "filter tags against empty array" do
94
- scope = Postjob::Queue.search("initial" => [])
94
+ scope = Postjob::Queue.search("postjobs", "initial" => [])
95
95
  result = Simple::SQL.all(scope)
96
96
  expect(result.length).to eq(0)
97
97
  end
98
98
 
99
99
  it "filter tags against multiple matches" do
100
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ])
100
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ])
101
101
  result = Simple::SQL.all(scope)
102
102
  expect(result.length).to eq(1)
103
103
  end
104
104
 
105
105
  it "filter tags against a combination of multiple matches" do
106
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ], "multi" => "manyay")
106
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ], "multi" => "manyay")
107
107
  result = Simple::SQL.all(scope)
108
108
  expect(result.length).to eq(1)
109
109
 
110
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ], "multi" => "nay")
110
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ], "multi" => "nay")
111
111
  result = Simple::SQL.all(scope)
112
112
  expect(result.length).to eq(0)
113
113
 
114
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ], "multi" => ["nay", "manyay"])
114
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ], "multi" => ["nay", "manyay"])
115
115
  result = Simple::SQL.all(scope)
116
116
  expect(result.length).to eq(1)
117
117
  end
data/spec/spec_helper.rb CHANGED
@@ -40,6 +40,6 @@ RSpec.configure do |config|
40
40
  config.after {}
41
41
 
42
42
  config.before(:all) do
43
- FileUtils.rm_rf ::Postjob::WorkerSession::HOST_ID_STORE
43
+ FileUtils.rm_rf ::Postjob::WorkerSession::HOST_ID_PATH
44
44
  end
45
45
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postjob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -261,6 +261,7 @@ files:
261
261
  - lib/postjob/migrations/018_heartbeat.sql
262
262
  - lib/postjob/migrations/019_heartbeat_indices.sql
263
263
  - lib/postjob/queue.rb
264
+ - lib/postjob/queue/database_info.rb
264
265
  - lib/postjob/queue/encoder.rb
265
266
  - lib/postjob/queue/notifications.rb
266
267
  - lib/postjob/queue/search.rb
@@ -314,7 +315,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
315
  version: '0'
315
316
  requirements: []
316
317
  rubyforge_project:
317
- rubygems_version: 2.5.1
318
+ rubygems_version: 2.7.7
318
319
  signing_key:
319
320
  specification_version: 4
320
321
  summary: restartable, asynchronous, and distributed processes