esse-async_indexing 0.0.2 → 0.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +18 -12
- data/README.md +50 -41
- data/lib/esse/async_indexing/actions/{batch_delete.rb → bulk_delete.rb} +2 -2
- data/lib/esse/async_indexing/actions/{batch_import.rb → bulk_import.rb} +1 -1
- data/lib/esse/async_indexing/actions/{batch_import_all.rb → bulk_import_all.rb} +1 -1
- data/lib/esse/async_indexing/actions/{batch_update.rb → bulk_update.rb} +6 -4
- data/lib/esse/async_indexing/actions/{update_lazy_document_attribute.rb → bulk_update_lazy_attribute.rb} +1 -1
- data/lib/esse/async_indexing/actions/delete_document.rb +1 -1
- data/lib/esse/async_indexing/actions/index_document.rb +1 -1
- data/lib/esse/async_indexing/actions/update_document.rb +1 -1
- data/lib/esse/async_indexing/actions/upsert_document.rb +1 -2
- data/lib/esse/async_indexing/actions.rb +5 -7
- data/lib/esse/async_indexing/active_record_callbacks/lazy_update_attribute.rb +8 -5
- data/lib/esse/async_indexing/cli/async_import.rb +27 -18
- data/lib/esse/async_indexing/cli/async_update_lazy_attributes.rb +78 -0
- data/lib/esse/async_indexing/cli.rb +23 -9
- data/lib/esse/async_indexing/config.rb +0 -3
- data/lib/esse/async_indexing/configuration.rb +54 -18
- data/lib/esse/async_indexing/jobs/bulk_update_lazy_attribute_job.rb +7 -0
- data/lib/esse/async_indexing/jobs/import_all_job.rb +1 -1
- data/lib/esse/async_indexing/jobs/import_ids_job.rb +42 -0
- data/lib/esse/async_indexing/{workers.rb → jobs.rb} +7 -24
- data/lib/esse/async_indexing/tasks.rb +82 -0
- data/lib/esse/async_indexing/version.rb +1 -1
- data/lib/esse/async_indexing.rb +15 -20
- data/lib/esse/plugins/async_indexing.rb +18 -59
- metadata +19 -32
- data/lib/esse/async_indexing/actions/bulk_update_lazy_document_attribute.rb +0 -20
- data/lib/esse/async_indexing/actions/import_batch_id.rb +0 -21
- data/lib/esse/async_indexing/adapters/adapter.rb +0 -29
- data/lib/esse/async_indexing/adapters/faktory.rb +0 -114
- data/lib/esse/async_indexing/adapters/sidekiq.rb +0 -94
- data/lib/esse/async_indexing/adapters.rb +0 -12
- data/lib/esse/async_indexing/configuration/base.rb +0 -65
- data/lib/esse/async_indexing/configuration/faktory.rb +0 -6
- data/lib/esse/async_indexing/configuration/sidekiq.rb +0 -12
- data/lib/esse/async_indexing/jobs/bulk_update_lazy_document_attribute_job.rb +0 -7
- data/lib/esse/async_indexing/jobs/import_batch_id_job.rb +0 -34
- data/lib/esse/async_indexing/jobs/update_lazy_document_attribute_job.rb +0 -7
- data/lib/esse/async_indexing/testing.rb +0 -79
- data/lib/esse/async_indexing/worker.rb +0 -85
- data/lib/esse/async_indexing/workers/faktory.rb +0 -28
- data/lib/esse/async_indexing/workers/shared_class_methods.rb +0 -26
- data/lib/esse/async_indexing/workers/sidekiq.rb +0 -28
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing::Actions
|
4
|
-
class BulkUpdateLazyDocumentAttribute
|
5
|
-
def self.call(index_class_name, repo_name, attr_name, batch_id, options = {})
|
6
|
-
_index_class, repo_class = CoerceIndexRepository.call(index_class_name, repo_name)
|
7
|
-
queue = Esse::RedisStorage::Queue.for(repo: repo_class, attribute_name: attr_name)
|
8
|
-
|
9
|
-
kwargs = Esse::HashUtils.deep_transform_keys(options, &:to_sym)
|
10
|
-
|
11
|
-
attr_name = repo_class.lazy_document_attributes.keys.find { |key| key.to_s == attr_name.to_s }
|
12
|
-
updated_ids = []
|
13
|
-
queue.fetch(batch_id) do |ids|
|
14
|
-
updated_ids = ids
|
15
|
-
repo_class.update_documents_attribute(attr_name, ids, **kwargs)
|
16
|
-
end
|
17
|
-
updated_ids
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing::Actions
|
4
|
-
class ImportBatchId
|
5
|
-
def self.call(index_class_name, repo_name, batch_id, options = {})
|
6
|
-
_index_class, repo_class = CoerceIndexRepository.call(index_class_name, repo_name)
|
7
|
-
queue = Esse::RedisStorage::Queue.for(repo: repo_class)
|
8
|
-
|
9
|
-
kwargs = Esse::HashUtils.deep_transform_keys(options, &:to_sym)
|
10
|
-
kwargs[:context] ||= {}
|
11
|
-
result = 0
|
12
|
-
ids_from_batch = []
|
13
|
-
queue.fetch(batch_id) do |ids|
|
14
|
-
ids_from_batch = ids
|
15
|
-
kwargs[:context][:id] = ids
|
16
|
-
result = repo_class.import(**kwargs)
|
17
|
-
end
|
18
|
-
[result, ids_from_batch]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing
|
4
|
-
module Adapters
|
5
|
-
class Adapter
|
6
|
-
# Push the worker job to the service
|
7
|
-
# @param _worker [Esse::AsyncIndexing::Worker] An instance of background worker
|
8
|
-
# @abstract Child classes should override this method
|
9
|
-
def self.push(_worker)
|
10
|
-
raise NotImplemented
|
11
|
-
end
|
12
|
-
|
13
|
-
# Coerces the raw payload into an instance of Worker
|
14
|
-
# @param payload [Object] the object that should be coerced to a Worker
|
15
|
-
# @options options [Hash] list of options that will be passed along to the Worker instance
|
16
|
-
# @return [Esse::AsyncIndexing::Worker] and instance of Esse::AsyncIndexing::Worker
|
17
|
-
# @abstract Child classes should override this method
|
18
|
-
def self.coerce_to_worker(payload, **options)
|
19
|
-
raise NotImplemented
|
20
|
-
end
|
21
|
-
|
22
|
-
protected
|
23
|
-
|
24
|
-
def normalize_before_push
|
25
|
-
# noop
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,114 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing
|
4
|
-
module Adapters
|
5
|
-
# This is a Faktory adapter that converts Esse::AsyncIndexing::Worker object into a faktory readable format
|
6
|
-
# and then push the jobs into the service.
|
7
|
-
class Faktory < Adapter
|
8
|
-
attr_reader :worker, :queue
|
9
|
-
|
10
|
-
def initialize(worker)
|
11
|
-
@worker = worker
|
12
|
-
@queue = worker.options.fetch(:queue, "default")
|
13
|
-
|
14
|
-
@payload = worker.payload.merge(
|
15
|
-
"jobtype" => worker.worker_class,
|
16
|
-
"queue" => @queue,
|
17
|
-
"retry" => parse_retry(worker.options[:retry])
|
18
|
-
)
|
19
|
-
@payload["created_at"] ||= Time.now.to_f
|
20
|
-
end
|
21
|
-
|
22
|
-
# Coerces the raw payload into an instance of Worker
|
23
|
-
# @param payload [Hash] The job as json from redis
|
24
|
-
# @options options [Hash] list of options that will be passed along to the Worker instance
|
25
|
-
# @return [Esse::AsyncIndexing::Worker] and instance of Esse::AsyncIndexing::Worker
|
26
|
-
def self.coerce_to_worker(payload, **options)
|
27
|
-
raise(Error, "invalid payload") unless payload.is_a?(Hash)
|
28
|
-
raise(Error, "invalid payload") unless payload["jobtype"].is_a?(String)
|
29
|
-
|
30
|
-
options[:retry] ||= payload["retry"] if payload.key?("retry")
|
31
|
-
options[:queue] ||= payload["queue"] if payload.key?("queue")
|
32
|
-
|
33
|
-
Esse::AsyncIndexing.worker(payload["jobtype"], **options, service: :faktory).tap do |worker|
|
34
|
-
worker.with_args(*Array(payload["args"])) if payload.key?("args")
|
35
|
-
worker.with_job_jid(payload["jid"]) if payload.key?("jid")
|
36
|
-
worker.created_at(payload["created_at"]) if payload.key?("created_at")
|
37
|
-
worker.enqueued_at(payload["enqueued_at"]) if payload.key?("enqueued_at")
|
38
|
-
worker.at(payload["at"]) if payload.key?("at")
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Initializes adapter and push job into the faktory service
|
43
|
-
#
|
44
|
-
# @param worker [Esse::AsyncIndexing::Worker] An instance of Esse::AsyncIndexing::Worker
|
45
|
-
# @return [Hash] Job payload
|
46
|
-
# @see push method for more details
|
47
|
-
def self.push(worker)
|
48
|
-
new(worker).push
|
49
|
-
end
|
50
|
-
|
51
|
-
# Push job to Faktory
|
52
|
-
# * If job has the 'at' key. Then schedule it
|
53
|
-
# * Otherwise enqueue for immediate execution
|
54
|
-
#
|
55
|
-
# @raise [Esse::AsyncIndexing::Error] raise and error when faktory dependency is not loaded
|
56
|
-
# @return [Hash] Payload that was sent to server
|
57
|
-
def push
|
58
|
-
unless Object.const_defined?(:Faktory)
|
59
|
-
raise Esse::AsyncIndexing::Error, <<~ERR
|
60
|
-
Faktory client for ruby is not loaded. You must install and require https://github.com/contribsys/faktory_worker_ruby.
|
61
|
-
ERR
|
62
|
-
end
|
63
|
-
normalize_before_push
|
64
|
-
|
65
|
-
pool = Thread.current[:faktory_via_pool] || ::Faktory.server_pool
|
66
|
-
::Faktory.client_middleware.invoke(@payload, pool) do
|
67
|
-
pool.with do |c|
|
68
|
-
c.push(@payload)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
@payload
|
72
|
-
end
|
73
|
-
|
74
|
-
protected
|
75
|
-
|
76
|
-
# Convert worker retry value acording to the Go struct datatype.
|
77
|
-
#
|
78
|
-
# * 25 is the default.
|
79
|
-
# * 0 means the job is completely ephemeral. No matter if it fails or succeeds, it will be discarded.
|
80
|
-
# * -1 means the job will go straight to the Dead set if it fails, no retries.
|
81
|
-
def parse_retry(value)
|
82
|
-
case value
|
83
|
-
when Numeric then value.to_i
|
84
|
-
when false then -1
|
85
|
-
else
|
86
|
-
25
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def parse_time(value)
|
91
|
-
case value
|
92
|
-
when Numeric then Time.at(value).to_datetime.rfc3339(9)
|
93
|
-
when Time then value.to_datetime.rfc3339(9)
|
94
|
-
when DateTime then value.rfc3339(9)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_json(value)
|
99
|
-
MultiJson.dump(value, mode: :compat)
|
100
|
-
end
|
101
|
-
|
102
|
-
def normalize_before_push
|
103
|
-
@payload["enqueued_at"] ||= Time.now.to_f
|
104
|
-
{"created_at" => false, "enqueued_at" => false, "at" => true}.each do |field, past_remove|
|
105
|
-
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
106
|
-
if (time = @payload.delete(field)) &&
|
107
|
-
(!past_remove || (past_remove && time > Time.now.to_f))
|
108
|
-
@payload[field] = parse_time(time)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing
|
4
|
-
module Adapters
|
5
|
-
# This is a Sidekiq adapter that converts Esse::AsyncIndexing::Worker object into a sidekiq readable format
|
6
|
-
# and then push the jobs into the service.
|
7
|
-
class Sidekiq < Adapter
|
8
|
-
attr_reader :worker, :queue
|
9
|
-
|
10
|
-
def initialize(worker)
|
11
|
-
@worker = worker
|
12
|
-
@queue = worker.options.fetch(:queue, "default")
|
13
|
-
|
14
|
-
@payload = worker.payload.merge(
|
15
|
-
"class" => worker.worker_class,
|
16
|
-
"retry" => worker.options.fetch(:retry, true),
|
17
|
-
"queue" => @queue
|
18
|
-
)
|
19
|
-
@payload["created_at"] ||= Time.now.to_f
|
20
|
-
end
|
21
|
-
|
22
|
-
# Coerces the raw payload into an instance of Worker
|
23
|
-
# @param payload [Hash] The job as json from redis
|
24
|
-
# @options options [Hash] list of options that will be passed along to the Worker instance
|
25
|
-
# @return [Esse::AsyncIndexing::Worker] and instance of Esse::AsyncIndexing::Worker
|
26
|
-
def self.coerce_to_worker(payload, **options)
|
27
|
-
raise(Error, "invalid payload") unless payload.is_a?(Hash)
|
28
|
-
raise(Error, "invalid payload") unless payload["class"].is_a?(String)
|
29
|
-
|
30
|
-
options[:retry] ||= payload["retry"] if payload.key?("retry")
|
31
|
-
options[:queue] ||= payload["queue"] if payload.key?("queue")
|
32
|
-
|
33
|
-
Esse::AsyncIndexing.worker(payload["class"], **options, service: :sidekiq).tap do |worker|
|
34
|
-
worker.with_args(*Array(payload["args"])) if payload.key?("args")
|
35
|
-
worker.with_job_jid(payload["jid"]) if payload.key?("jid")
|
36
|
-
worker.created_at(payload["created_at"]) if payload.key?("created_at")
|
37
|
-
worker.enqueued_at(payload["enqueued_at"]) if payload.key?("enqueued_at")
|
38
|
-
worker.at(payload["at"]) if payload.key?("at")
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Initializes adapter and push job into the sidekiq service
|
43
|
-
#
|
44
|
-
# @param worker [Esse::AsyncIndexing::Worker] An instance of Esse::AsyncIndexing::Worker
|
45
|
-
# @return [Hash] Job payload
|
46
|
-
# @see push method for more details
|
47
|
-
def self.push(worker)
|
48
|
-
new(worker).push
|
49
|
-
end
|
50
|
-
|
51
|
-
# Push sidekiq to the Sidekiq(Redis actually).
|
52
|
-
# * If job has the 'at' key. Then schedule it
|
53
|
-
# * Otherwise enqueue for immediate execution
|
54
|
-
#
|
55
|
-
# @return [Hash] Payload that was sent to redis
|
56
|
-
def push
|
57
|
-
normalize_before_push
|
58
|
-
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
59
|
-
if (timestamp = @payload.delete("at")) && (timestamp > Time.now.to_f)
|
60
|
-
Esse.config.async_indexing.sidekiq.redis_pool.with do |redis|
|
61
|
-
redis.zadd(scheduled_queue_name, timestamp.to_f.to_s, to_json(@payload))
|
62
|
-
end
|
63
|
-
else
|
64
|
-
Esse.config.async_indexing.sidekiq.redis_pool.with do |redis|
|
65
|
-
redis.lpush(immediate_queue_name, to_json(@payload))
|
66
|
-
end
|
67
|
-
end
|
68
|
-
@payload
|
69
|
-
end
|
70
|
-
|
71
|
-
protected
|
72
|
-
|
73
|
-
def namespace
|
74
|
-
Esse.config.async_indexing.sidekiq.namespace
|
75
|
-
end
|
76
|
-
|
77
|
-
def scheduled_queue_name
|
78
|
-
"#{namespace}:schedule"
|
79
|
-
end
|
80
|
-
|
81
|
-
def immediate_queue_name
|
82
|
-
"#{namespace}:queue:#{queue}"
|
83
|
-
end
|
84
|
-
|
85
|
-
def to_json(value)
|
86
|
-
MultiJson.dump(value, mode: :compat)
|
87
|
-
end
|
88
|
-
|
89
|
-
def normalize_before_push
|
90
|
-
@payload["enqueued_at"] = Time.now.to_f
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Esse::AsyncIndexing::Configuration::Base
|
4
|
-
class << self
|
5
|
-
private
|
6
|
-
|
7
|
-
def attribute_accessor(field, validator: nil, normalizer: nil, default: nil)
|
8
|
-
normalizer ||= :"normalize_#{field}"
|
9
|
-
validator ||= :"validate_#{field}"
|
10
|
-
|
11
|
-
define_method(field) do
|
12
|
-
unless instance_variable_defined?(:"@#{field}")
|
13
|
-
return if default.nil?
|
14
|
-
|
15
|
-
send(:"#{field}=", default.respond_to?(:call) ? default.call : default)
|
16
|
-
end
|
17
|
-
instance_variable_get(:"@#{field}")
|
18
|
-
end
|
19
|
-
|
20
|
-
define_method(:"#{field}=") do |value|
|
21
|
-
value = send(normalizer, field, value) if respond_to?(normalizer, true)
|
22
|
-
send(validator, field, value) if respond_to?(validator, true)
|
23
|
-
|
24
|
-
instance_variable_set(:"@#{field}", value)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# A Hash with all workers definitions. The worker class name must be the main hash key
|
30
|
-
# Example:
|
31
|
-
# "FaktoryIndexWorker":
|
32
|
-
# retry: false
|
33
|
-
# queue: "indexing"
|
34
|
-
# adapter: "faktory"
|
35
|
-
# "FaktoryBatchIndexWorker":
|
36
|
-
# retry: 5
|
37
|
-
# queue: "batch_index"
|
38
|
-
# adapter: "faktory"
|
39
|
-
attribute_accessor :workers, default: {}
|
40
|
-
|
41
|
-
def worker_options(class_name)
|
42
|
-
class_name = class_name.to_s
|
43
|
-
if strict? && !workers.key?(class_name)
|
44
|
-
raise Esse::AsyncIndexing::NotDefinedWorkerError.new(class_name)
|
45
|
-
end
|
46
|
-
|
47
|
-
workers.fetch(class_name, {})
|
48
|
-
end
|
49
|
-
|
50
|
-
def strict?
|
51
|
-
true
|
52
|
-
end
|
53
|
-
|
54
|
-
protected
|
55
|
-
|
56
|
-
def normalize_workers(_, value)
|
57
|
-
return unless value.is_a?(Hash)
|
58
|
-
|
59
|
-
hash = Esse::AsyncIndexing::Workers::DEFAULT.values.map { |v| [v, {}] }.to_h
|
60
|
-
value.each do |class_name, opts|
|
61
|
-
hash[class_name.to_s] = Esse::HashUtils.deep_transform_keys(opts.to_h, &:to_sym)
|
62
|
-
end
|
63
|
-
hash
|
64
|
-
end
|
65
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing
|
4
|
-
class Configuration::Sidekiq < Configuration::Base
|
5
|
-
attribute_accessor :redis
|
6
|
-
attribute_accessor :namespace, default: "sidekiq"
|
7
|
-
|
8
|
-
def redis_pool
|
9
|
-
@redis_pool ||= Esse::RedisStorage::Pool.new(redis)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,7 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Esse::AsyncIndexing::Jobs::BulkUpdateLazyDocumentAttributeJob
|
4
|
-
def perform(index_class_name, repo_name, attribute_name, batch_id, options = {})
|
5
|
-
Esse::AsyncIndexing::Actions::BulkUpdateLazyDocumentAttribute.call(index_class_name, repo_name, attribute_name, batch_id, options)
|
6
|
-
end
|
7
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Esse::AsyncIndexing::Jobs::ImportBatchIdJob
|
4
|
-
LAZY_ATTR_WORKER = "Esse::AsyncIndexing::Jobs::BulkUpdateLazyDocumentAttributeJob"
|
5
|
-
|
6
|
-
def perform(index_class_name, repo_name, batch_id, options = {})
|
7
|
-
total, ids = Esse::AsyncIndexing::Actions::ImportBatchId.call(index_class_name, repo_name, batch_id, options)
|
8
|
-
|
9
|
-
options = Esse::HashUtils.deep_transform_keys(options, &:to_s)
|
10
|
-
return total if total.zero?
|
11
|
-
return total if lazy_already_imported?(options)
|
12
|
-
return total unless self.class.respond_to?(:bg_worker_options)
|
13
|
-
|
14
|
-
_index_class, repo_class = Esse::AsyncIndexing::Actions::CoerceIndexRepository.call(index_class_name, repo_name)
|
15
|
-
|
16
|
-
repo_class.lazy_document_attributes.each_key do |attr_name|
|
17
|
-
queue = Esse::RedisStorage::Queue.for(repo: repo_class, attribute_name: attr_name)
|
18
|
-
queue.enqueue(id: batch_id, values: ids)
|
19
|
-
Esse::AsyncIndexing.worker(LAZY_ATTR_WORKER, service: self.class.bg_worker_options[:service])
|
20
|
-
.with_args(index_class_name, repo_name, attr_name.to_s, batch_id, options)
|
21
|
-
.push
|
22
|
-
end
|
23
|
-
total
|
24
|
-
end
|
25
|
-
|
26
|
-
protected
|
27
|
-
|
28
|
-
# The `import` action already eager or lazy load the document attributes when some of these options are set.
|
29
|
-
def lazy_already_imported?(options)
|
30
|
-
eager = options.delete("eager_include_document_attributes") || false
|
31
|
-
lazy = options.delete("lazy_update_document_attributes") || false
|
32
|
-
eager || lazy
|
33
|
-
end
|
34
|
-
end
|
@@ -1,7 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Esse::AsyncIndexing::Jobs::UpdateLazyDocumentAttributeJob
|
4
|
-
def perform(index_class_name, repo_name, attribute_name, ids, options = {})
|
5
|
-
Esse::AsyncIndexing::Actions::UpdateLazyDocumentAttribute.call(index_class_name, repo_name, attribute_name, ids, options)
|
6
|
-
end
|
7
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse
|
4
|
-
module AsyncIndexing
|
5
|
-
class Testing
|
6
|
-
class << self
|
7
|
-
def enable!
|
8
|
-
Thread.current[:esse_async_indexing_testing] = true
|
9
|
-
end
|
10
|
-
|
11
|
-
def disable!
|
12
|
-
Thread.current[:esse_async_indexing_testing] = false
|
13
|
-
end
|
14
|
-
|
15
|
-
def enabled?
|
16
|
-
Thread.current[:esse_async_indexing_testing] == true
|
17
|
-
end
|
18
|
-
|
19
|
-
def disabled?
|
20
|
-
!enabled?
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
Esse::AsyncIndexing::Testing.disable!
|
28
|
-
|
29
|
-
module Esse::AsyncIndexing::Jobs
|
30
|
-
class << self
|
31
|
-
def jobs
|
32
|
-
@jobs ||= []
|
33
|
-
end
|
34
|
-
|
35
|
-
def push(job)
|
36
|
-
jobs.push(job)
|
37
|
-
end
|
38
|
-
|
39
|
-
def clear
|
40
|
-
jobs.clear
|
41
|
-
end
|
42
|
-
|
43
|
-
def size
|
44
|
-
jobs.size
|
45
|
-
end
|
46
|
-
|
47
|
-
def jobs_for(service: nil, class_name: nil)
|
48
|
-
filtered = jobs
|
49
|
-
if service
|
50
|
-
filtered = filtered.select { |job| job["service"] == service.to_s }
|
51
|
-
end
|
52
|
-
if class_name
|
53
|
-
filtered = filtered.select { |job| job["__class_name__"] == class_name.to_s }
|
54
|
-
end
|
55
|
-
filtered
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
module Esse::AsyncIndexing::JobsInterceptorAdapter
|
61
|
-
def push
|
62
|
-
return super unless Esse::AsyncIndexing::Testing.enabled?
|
63
|
-
|
64
|
-
normalize_before_push
|
65
|
-
test_payload = @payload.dup
|
66
|
-
if @payload["jobtype"]
|
67
|
-
test_payload["service"] = "faktory"
|
68
|
-
test_payload["__class_name__"] = @payload["jobtype"]
|
69
|
-
else
|
70
|
-
test_payload["service"] = "sidekiq"
|
71
|
-
test_payload["__class_name__"] = @payload["class"]
|
72
|
-
end
|
73
|
-
Esse::AsyncIndexing::Jobs.push(test_payload)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
Esse::AsyncIndexing::SERVICES.each_value do |adapter|
|
78
|
-
adapter.prepend(Esse::AsyncIndexing::JobsInterceptorAdapter)
|
79
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing
|
4
|
-
class Worker
|
5
|
-
attr_reader :options, :payload, :worker_class, :service
|
6
|
-
|
7
|
-
attr_reader :arguments
|
8
|
-
|
9
|
-
def initialize(worker_class, service: nil, **options)
|
10
|
-
@worker_class = worker_class
|
11
|
-
@service = service
|
12
|
-
@options = options
|
13
|
-
@payload = {}
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.coerce(service:, payload:, **opts)
|
17
|
-
Esse::AsyncIndexing::SERVICES.fetch(service).coerce_to_worker(payload, **opts)
|
18
|
-
end
|
19
|
-
|
20
|
-
%i[created_at enqueued_at].each do |method_name|
|
21
|
-
define_method method_name do |value|
|
22
|
-
@payload[method_name.to_s] =
|
23
|
-
case value
|
24
|
-
when Numeric then value.to_f
|
25
|
-
when String then Time.parse(value).to_f
|
26
|
-
when Time, DateTime then value.to_f
|
27
|
-
else
|
28
|
-
raise ArgumentError, format("The %<v>p is not a valid value for %<m>s.", v: value, m: method_name)
|
29
|
-
end
|
30
|
-
|
31
|
-
self
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Adds arguments to the job
|
36
|
-
# @return self
|
37
|
-
def with_args(*args)
|
38
|
-
@payload["args"] = args
|
39
|
-
|
40
|
-
self
|
41
|
-
end
|
42
|
-
|
43
|
-
# Schedule the time when a job will be executed. Jobs which are scheduled in the past are enqueued for immediate execution.
|
44
|
-
# @param timestamp [Numeric] timestamp, numeric or something that acts numeric.
|
45
|
-
# @return self
|
46
|
-
def in(timestamp)
|
47
|
-
now = Time.now.to_f
|
48
|
-
timestamp = Time.parse(timestamp) if timestamp.is_a?(String)
|
49
|
-
int = timestamp.respond_to?(:strftime) ? timestamp.to_f : now + timestamp.to_f
|
50
|
-
return self if int <= now
|
51
|
-
|
52
|
-
@payload["at"] = int
|
53
|
-
@payload["created_at"] = now
|
54
|
-
|
55
|
-
self
|
56
|
-
end
|
57
|
-
alias_method :at, :in
|
58
|
-
|
59
|
-
def with_job_jid(jid = nil)
|
60
|
-
@payload["jid"] ||= jid || Esse::AsyncIndexing.jid
|
61
|
-
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
|
-
# @return Response of service
|
66
|
-
# @see Esse::AsyncIndexing::Adapters::** for more details
|
67
|
-
def push
|
68
|
-
unless Esse::AsyncIndexing::SERVICES.key?(service)
|
69
|
-
raise Esse::AsyncIndexing::Error, format("Service %<service>p is not implemented. Please use one of #{Esse::AsyncIndexing::SERVICES.keys.map(&:inspect).join(" or ")}.", service: service)
|
70
|
-
end
|
71
|
-
@payload["created_at"] ||= Time.now.to_f
|
72
|
-
worker_to_push = with_job_jid
|
73
|
-
Esse::AsyncIndexing::SERVICES[service].push(worker_to_push)
|
74
|
-
end
|
75
|
-
|
76
|
-
def eql?(other)
|
77
|
-
return false unless other.is_a?(self.class)
|
78
|
-
|
79
|
-
worker_class == other.worker_class &&
|
80
|
-
payload == other.payload &&
|
81
|
-
options == other.options
|
82
|
-
end
|
83
|
-
alias_method :==, :eql?
|
84
|
-
end
|
85
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frizen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "shared_class_methods"
|
4
|
-
|
5
|
-
module Esse::AsyncIndexing
|
6
|
-
module Workers
|
7
|
-
module Faktory
|
8
|
-
def self.extended(base)
|
9
|
-
base.include(::Faktory::Job) if defined?(::Faktory)
|
10
|
-
base.extend SharedClassMethods
|
11
|
-
base.extend ClassMethods
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def service_worker_options
|
16
|
-
default_queue = Esse.config.async_indexing.faktory.workers.dig(name, :queue)
|
17
|
-
default_retry = Esse.config.async_indexing.faktory.workers.dig(name, :retry)
|
18
|
-
default_queue ||= ::Faktory.default_job_options["queue"] if defined?(::Faktory)
|
19
|
-
default_retry ||= ::Faktory.default_job_options["retry"] if defined?(::Faktory)
|
20
|
-
{
|
21
|
-
queue: default_queue || "default",
|
22
|
-
retry: default_retry || 25
|
23
|
-
}
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frizen_string_literal: true
|
2
|
-
|
3
|
-
module Esse::AsyncIndexing
|
4
|
-
module Workers
|
5
|
-
module SharedClassMethods
|
6
|
-
def perform_async(*args)
|
7
|
-
build_worker.with_args(*args).push
|
8
|
-
end
|
9
|
-
|
10
|
-
def perform_in(interval, *args)
|
11
|
-
build_worker.with_args(*args).at(interval).push
|
12
|
-
end
|
13
|
-
alias_method :perform_at, :perform_in
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
def service_worker_options
|
18
|
-
{}
|
19
|
-
end
|
20
|
-
|
21
|
-
def build_worker
|
22
|
-
Esse::AsyncIndexing.worker(name, **service_worker_options.merge(bg_worker_options))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frizen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "shared_class_methods"
|
4
|
-
|
5
|
-
module Esse::AsyncIndexing
|
6
|
-
module Workers
|
7
|
-
module Sidekiq
|
8
|
-
def self.extended(base)
|
9
|
-
base.include(::Sidekiq::Worker) if defined?(::Sidekiq)
|
10
|
-
base.extend SharedClassMethods
|
11
|
-
base.extend ClassMethods
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def service_worker_options
|
16
|
-
default_queue = Esse.config.async_indexing.sidekiq.workers.dig(name, :queue)
|
17
|
-
default_retry = Esse.config.async_indexing.sidekiq.workers.dig(name, :retry)
|
18
|
-
default_queue ||= ::Sidekiq.default_worker_options["queue"] if defined?(::Sidekiq)
|
19
|
-
default_retry ||= ::Sidekiq.default_worker_options["retry"] if defined?(::Sidekiq)
|
20
|
-
{
|
21
|
-
queue: default_queue || "default",
|
22
|
-
retry: default_retry || 15
|
23
|
-
}
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|