sbmt-outbox 5.0.0
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 +7 -0
- data/README.md +440 -0
- data/Rakefile +3 -0
- data/app/interactors/sbmt/outbox/base_create_item.rb +55 -0
- data/app/interactors/sbmt/outbox/create_inbox_item.rb +10 -0
- data/app/interactors/sbmt/outbox/create_outbox_item.rb +10 -0
- data/app/interactors/sbmt/outbox/dry_interactor.rb +16 -0
- data/app/interactors/sbmt/outbox/partition_strategies/hash_partitioning.rb +20 -0
- data/app/interactors/sbmt/outbox/partition_strategies/number_partitioning.rb +26 -0
- data/app/interactors/sbmt/outbox/process_item.rb +269 -0
- data/app/interactors/sbmt/outbox/retry_strategies/compacted_log.rb +41 -0
- data/app/interactors/sbmt/outbox/retry_strategies/exponential_backoff.rb +34 -0
- data/app/jobs/sbmt/outbox/base_delete_stale_items_job.rb +78 -0
- data/app/jobs/sbmt/outbox/delete_stale_inbox_items_job.rb +15 -0
- data/app/jobs/sbmt/outbox/delete_stale_outbox_items_job.rb +15 -0
- data/app/models/sbmt/outbox/base_item.rb +165 -0
- data/app/models/sbmt/outbox/base_item_config.rb +106 -0
- data/app/models/sbmt/outbox/inbox_item.rb +38 -0
- data/app/models/sbmt/outbox/inbox_item_config.rb +13 -0
- data/app/models/sbmt/outbox/outbox_item.rb +52 -0
- data/app/models/sbmt/outbox/outbox_item_config.rb +13 -0
- data/config/initializers/schked.rb +9 -0
- data/config/initializers/yabeda.rb +71 -0
- data/config/schedule.rb +9 -0
- data/exe/outbox +16 -0
- data/lib/generators/helpers/config.rb +46 -0
- data/lib/generators/helpers/initializer.rb +41 -0
- data/lib/generators/helpers/items.rb +17 -0
- data/lib/generators/helpers/migration.rb +73 -0
- data/lib/generators/helpers/paas.rb +17 -0
- data/lib/generators/helpers/values.rb +49 -0
- data/lib/generators/helpers.rb +8 -0
- data/lib/generators/outbox/install/USAGE +10 -0
- data/lib/generators/outbox/install/install_generator.rb +33 -0
- data/lib/generators/outbox/install/templates/Outboxfile +3 -0
- data/lib/generators/outbox/install/templates/outbox.rb +32 -0
- data/lib/generators/outbox/install/templates/outbox.yml +51 -0
- data/lib/generators/outbox/item/USAGE +12 -0
- data/lib/generators/outbox/item/item_generator.rb +94 -0
- data/lib/generators/outbox/item/templates/inbox_item.rb.tt +7 -0
- data/lib/generators/outbox/item/templates/outbox_item.rb.tt +16 -0
- data/lib/generators/outbox/transport/USAGE +19 -0
- data/lib/generators/outbox/transport/templates/inbox_transport.yml.erb +9 -0
- data/lib/generators/outbox/transport/templates/outbox_transport.yml.erb +10 -0
- data/lib/generators/outbox/transport/transport_generator.rb +60 -0
- data/lib/generators/outbox.rb +23 -0
- data/lib/sbmt/outbox/ascii_art.rb +62 -0
- data/lib/sbmt/outbox/cli.rb +100 -0
- data/lib/sbmt/outbox/database_switcher.rb +15 -0
- data/lib/sbmt/outbox/engine.rb +45 -0
- data/lib/sbmt/outbox/error_tracker.rb +26 -0
- data/lib/sbmt/outbox/errors.rb +14 -0
- data/lib/sbmt/outbox/instrumentation/open_telemetry_loader.rb +34 -0
- data/lib/sbmt/outbox/logger.rb +35 -0
- data/lib/sbmt/outbox/middleware/builder.rb +23 -0
- data/lib/sbmt/outbox/middleware/open_telemetry/tracing_create_item_middleware.rb +42 -0
- data/lib/sbmt/outbox/middleware/open_telemetry/tracing_item_process_middleware.rb +49 -0
- data/lib/sbmt/outbox/middleware/runner.rb +29 -0
- data/lib/sbmt/outbox/middleware/sentry/tracing_batch_process_middleware.rb +48 -0
- data/lib/sbmt/outbox/middleware/sentry/tracing_item_process_middleware.rb +65 -0
- data/lib/sbmt/outbox/middleware/sentry/transaction.rb +28 -0
- data/lib/sbmt/outbox/probes/probe.rb +38 -0
- data/lib/sbmt/outbox/redis_client_factory.rb +36 -0
- data/lib/sbmt/outbox/tasks/delete_failed_items.rake +17 -0
- data/lib/sbmt/outbox/tasks/retry_failed_items.rake +20 -0
- data/lib/sbmt/outbox/thread_pool.rb +108 -0
- data/lib/sbmt/outbox/throttler.rb +52 -0
- data/lib/sbmt/outbox/version.rb +7 -0
- data/lib/sbmt/outbox/worker.rb +233 -0
- data/lib/sbmt/outbox.rb +136 -0
- data/lib/sbmt-outbox.rb +3 -0
- metadata +594 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
default: &default
|
2
|
+
owner: owner-team
|
3
|
+
bucket_size: 16
|
4
|
+
|
5
|
+
probes:
|
6
|
+
port: SET-UP-YOUR-HEALTHCHECK-PORT-HERE
|
7
|
+
|
8
|
+
kafka:
|
9
|
+
required_acks: -1
|
10
|
+
max_retries: 1
|
11
|
+
|
12
|
+
# outbox_items:
|
13
|
+
# outbox_item:
|
14
|
+
# owner: outbox-item-owner-team
|
15
|
+
# partition_size: 2
|
16
|
+
# partition_strategy: number
|
17
|
+
# retention: P1W
|
18
|
+
# retry_strategies:
|
19
|
+
# - exponential_backoff
|
20
|
+
# - compacted_log
|
21
|
+
# transports:
|
22
|
+
# kafka_producer:
|
23
|
+
# topic: "outbox_item_topic"
|
24
|
+
# kafka:
|
25
|
+
# required_acks: -1
|
26
|
+
|
27
|
+
# inbox_items:
|
28
|
+
# inbox_item:
|
29
|
+
# owner: inbox-item-owner-team
|
30
|
+
# partition_size: 2
|
31
|
+
# partition_strategy: number
|
32
|
+
# retention: P1W
|
33
|
+
# retry_strategies:
|
34
|
+
# - exponential_backoff
|
35
|
+
# transports:
|
36
|
+
# import_order:
|
37
|
+
# source: "kafka_consumer"
|
38
|
+
|
39
|
+
development:
|
40
|
+
<<: *default
|
41
|
+
|
42
|
+
test:
|
43
|
+
<<: *default
|
44
|
+
bucket_size: 2
|
45
|
+
|
46
|
+
staging:
|
47
|
+
<<: *default
|
48
|
+
|
49
|
+
production:
|
50
|
+
<<: *default
|
51
|
+
bucket_size: 256
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Description:
|
2
|
+
Generates Outbox item (model, migration, patches outbox.yml, etc)
|
3
|
+
|
4
|
+
Example:
|
5
|
+
bin/rails generate outbox:item Model::InboxItem --kind inbox
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
app/models/model/inbox_item.rb
|
9
|
+
db/migrations/XXX_create_model_inbox_items.rb
|
10
|
+
This will modify:
|
11
|
+
configs/values.yml
|
12
|
+
config/outbox.yml
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "generators/outbox"
|
4
|
+
|
5
|
+
module Outbox
|
6
|
+
module Generators
|
7
|
+
class ItemGenerator < NamedBase
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
class_option :kind, type: :string, desc: "Either inbox or outbox", banner: "inbox/outbox", required: true
|
11
|
+
class_option :skip_migration, type: :boolean, default: false, desc: "Skip creating migration"
|
12
|
+
class_option :skip_model, type: :boolean, default: false, desc: "Skip creating model class"
|
13
|
+
class_option :skip_config, type: :boolean, default: false, desc: "Skip modifying config/outbox.yml"
|
14
|
+
class_option :skip_values, type: :boolean, default: false, desc: "Skip modifying configs/values.yaml"
|
15
|
+
|
16
|
+
def check_kind!
|
17
|
+
return if options[:kind].in?(%w[inbox outbox])
|
18
|
+
|
19
|
+
raise Rails::Generators::Error, "Unknown item kind. " \
|
20
|
+
"Please specify `--kind inbox` or `--kind outbox`"
|
21
|
+
end
|
22
|
+
|
23
|
+
def check!
|
24
|
+
check_config!
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_item_name
|
28
|
+
return if file_name.underscore.match?(%r{#{options[:kind]}_item$})
|
29
|
+
|
30
|
+
continue = yes?(
|
31
|
+
"Warning: your item's name doesn't match the conventional" \
|
32
|
+
" name format (i.e. Some#{options[:kind].camelize}Item). Continue?"
|
33
|
+
)
|
34
|
+
return if continue
|
35
|
+
|
36
|
+
raise Rails::Generators::Error, "Aborting"
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_migration
|
40
|
+
return if options[:skip_migration]
|
41
|
+
|
42
|
+
create_migration_file(migration_class_name, migration_table_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_model
|
46
|
+
return if options[:skip_model]
|
47
|
+
|
48
|
+
template "#{options[:kind]}_item.rb", File.join("app/models", "#{item_path}.rb")
|
49
|
+
end
|
50
|
+
|
51
|
+
def patch_config
|
52
|
+
return if options[:skip_config]
|
53
|
+
|
54
|
+
item_template_data = if options[:kind] == "inbox"
|
55
|
+
<<~RUBY
|
56
|
+
#{item_path}:
|
57
|
+
partition_size: 1
|
58
|
+
partition_strategy: hash
|
59
|
+
retention: P1W
|
60
|
+
max_retries: 7
|
61
|
+
retry_strategies:
|
62
|
+
- exponential_backoff
|
63
|
+
# # see README to learn more about transport configuration
|
64
|
+
# transports: {}
|
65
|
+
RUBY
|
66
|
+
else
|
67
|
+
<<~RUBY
|
68
|
+
#{item_path}:
|
69
|
+
partition_size: 1
|
70
|
+
partition_strategy: number
|
71
|
+
retention: P3D
|
72
|
+
max_retries: 7
|
73
|
+
retry_strategies:
|
74
|
+
- exponential_backoff
|
75
|
+
# # see README to learn more about transport configuration
|
76
|
+
# transports: {}
|
77
|
+
RUBY
|
78
|
+
end
|
79
|
+
|
80
|
+
add_item_to_config("#{options[:kind]}_items", item_template_data)
|
81
|
+
end
|
82
|
+
|
83
|
+
def patch_values
|
84
|
+
return if options[:skip_values]
|
85
|
+
return unless paas_app?
|
86
|
+
|
87
|
+
# e.g. order/inbox_item => inbox-order-inbox-items
|
88
|
+
deployment_name = "#{options[:kind]}-" + dasherize_item(item_path)
|
89
|
+
|
90
|
+
add_item_to_values(deployment_name.pluralize, item_path)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
<% module_namespacing do -%>
|
4
|
+
class <%= namespaced_item_class_name %> < Sbmt::Outbox::OutboxItem
|
5
|
+
self.table_name = "<%= migration_table_name %>"
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def extra_options
|
10
|
+
{
|
11
|
+
# Kafka partition key
|
12
|
+
key: event_key
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
<% end -%>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Description:
|
2
|
+
Configures a transport inside the config/outbox.yml file
|
3
|
+
Arguments:
|
4
|
+
- item name, either CamelCased or under_scored, required
|
5
|
+
- transport name, i.e. sbmt/kafka_producer or import_order, required
|
6
|
+
Options:
|
7
|
+
- --kind inbox/outbox - transport type, required
|
8
|
+
- --topic, i.e. my-topic-name, optional
|
9
|
+
- --event_name, i.e. order_created, affects only outbox transports, optional
|
10
|
+
- --source, i.e. kafka, affects only inbox transports, optional
|
11
|
+
- --target, i.e order, affects only inbox transports, optional
|
12
|
+
|
13
|
+
Example:
|
14
|
+
bin/rails g outbox:transport MyOutboxItem sbmt/kafka_producer --kind outbox
|
15
|
+
|
16
|
+
The following lines will be inserted to config/outbox.yml under the `outbox_items/my_outbox_item` section:
|
17
|
+
transports:
|
18
|
+
sbmt/kafka_producer:
|
19
|
+
topic: "need to add a topic"
|
@@ -0,0 +1,9 @@
|
|
1
|
+
transports:
|
2
|
+
<%= transport.underscore %>:
|
3
|
+
<%- if options[:source].present? -%>
|
4
|
+
source: "<%= options[:source] %>"
|
5
|
+
<%- elsif options[:target].present? -%>
|
6
|
+
target: "<%= options[:target] %>"
|
7
|
+
<%- else -%>
|
8
|
+
# configure source, target and other options here
|
9
|
+
<%- end -%>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%- topic = options[:topic].presence || "need to add a topic" -%>
|
2
|
+
transports:
|
3
|
+
<%- if options[:event_name].present? -%>
|
4
|
+
- class: "<%= transport.underscore %>"
|
5
|
+
event_name: "<%= options[:event_name] %>"
|
6
|
+
topic: "<%= topic %>"
|
7
|
+
<%- else -%>
|
8
|
+
<%= transport.underscore %>:
|
9
|
+
topic: "<%= topic %>"
|
10
|
+
<%- end -%>
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "generators/outbox"
|
4
|
+
|
5
|
+
module Outbox
|
6
|
+
module Generators
|
7
|
+
class TransportGenerator < NamedBase
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
argument :transport, required: true, type: :string, banner: "sbmt/kafka_producer"
|
11
|
+
|
12
|
+
class_option :kind, type: :string, desc: "Either inbox or outbox", banner: "inbox/outbox", required: true
|
13
|
+
class_option :topic, type: :string, banner: "my-topic-name"
|
14
|
+
class_option :source, type: :string, banner: "kafka"
|
15
|
+
class_option :target, type: :string, banner: "order"
|
16
|
+
class_option :event_name, type: :string, default: "", banner: "order_created"
|
17
|
+
|
18
|
+
def check_name!
|
19
|
+
return if options[:kind].in?(%w[inbox outbox])
|
20
|
+
|
21
|
+
raise Rails::Generators::Error, "Unknown transport type. Should be inbox or outbox"
|
22
|
+
end
|
23
|
+
|
24
|
+
def check!
|
25
|
+
check_config!
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_item_exists!
|
29
|
+
return if item_exists?
|
30
|
+
|
31
|
+
raise Rails::Generators::Error, "Item `#{item_name}` does not exist in the `#{options[:kind]}_items` " \
|
32
|
+
"section of #{CONFIG_PATH}. You may insert this item by running " \
|
33
|
+
"`bin/rails g outbox:item #{item_name} --kind #{options[:kind]}`"
|
34
|
+
end
|
35
|
+
|
36
|
+
def insert_transport
|
37
|
+
data = optimize_indentation(template, 6)
|
38
|
+
insert_into_file CONFIG_PATH, data, after: "#{item_name.underscore}:\n", force: true
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def item_name
|
44
|
+
name
|
45
|
+
end
|
46
|
+
|
47
|
+
def item_exists?
|
48
|
+
File.binread(CONFIG_PATH).match?(%r{#{options[:kind]}_items:.*#{item_name.underscore}:}m)
|
49
|
+
end
|
50
|
+
|
51
|
+
def template_path
|
52
|
+
File.join(TransportGenerator.source_root, "#{options[:kind]}_transport.yml.erb")
|
53
|
+
end
|
54
|
+
|
55
|
+
def template
|
56
|
+
ERB.new(File.read(template_path), trim_mode: "%-").result(binding)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require_relative "helpers"
|
5
|
+
|
6
|
+
module Outbox
|
7
|
+
module Generators
|
8
|
+
class Base < Rails::Generators::Base
|
9
|
+
include Helpers::Config
|
10
|
+
include Helpers::Initializer
|
11
|
+
include Helpers::Paas
|
12
|
+
end
|
13
|
+
|
14
|
+
class NamedBase < Rails::Generators::NamedBase
|
15
|
+
include Helpers::Config
|
16
|
+
include Helpers::Initializer
|
17
|
+
include Helpers::Items
|
18
|
+
include Helpers::Migration
|
19
|
+
include Helpers::Paas
|
20
|
+
include Helpers::Values
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sbmt
|
4
|
+
module Outbox
|
5
|
+
module AsciiArt
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def logo
|
9
|
+
<<~'TEXT'
|
10
|
+
|
11
|
+
______________________
|
12
|
+
/\ _______ _______ \
|
13
|
+
/ \ /\ \/\ \ \
|
14
|
+
/ _ \ \ \ \ \ \
|
15
|
+
/ /\ \ \ \ \ \ \
|
16
|
+
/ / \ \ \______\ \______\ \
|
17
|
+
/ / \ \ / _____/_/ _____/_ \
|
18
|
+
/ / \_ \ /\ \/\ \ \
|
19
|
+
/ _\ / _ \ \ \ \ \ \
|
20
|
+
/ /\ \ / /\ \ \ \ \ \ \
|
21
|
+
/ / \ \ / / \ \ \______\ \______\ \
|
22
|
+
/ / \ \/_/ \ \ / / / / \
|
23
|
+
/ / \_ / \_ \_____________________\
|
24
|
+
\ \ / _\ / / /
|
25
|
+
\ \ / /\ \ / / \______\ \______\ /
|
26
|
+
\ \ / / \ \ / / / / / / /
|
27
|
+
\ \/_/ \ \/_ / / / / / /
|
28
|
+
\ / \_ / / / / / /
|
29
|
+
\ \ / / \/______/\/______/ /
|
30
|
+
\ \ / / \______\ \______\ /
|
31
|
+
\ \ / / / / / / /
|
32
|
+
\ \/_ / / / / / /
|
33
|
+
\ / / / / / /
|
34
|
+
\ / \/______/\/______/ /
|
35
|
+
\/_____________________/
|
36
|
+
|
37
|
+
TEXT
|
38
|
+
end
|
39
|
+
|
40
|
+
def shutdown
|
41
|
+
<<~'TEXT'
|
42
|
+
|
43
|
+
_
|
44
|
+
| |
|
45
|
+
| |===( ) //////
|
46
|
+
|_| ||| | o o|
|
47
|
+
||| ( c ) ____
|
48
|
+
||| \= / || \_
|
49
|
+
|||||| || |
|
50
|
+
|||||| ...||__/|-"
|
51
|
+
|||||| __|________|__
|
52
|
+
||| |______________|
|
53
|
+
||| || || || ||
|
54
|
+
||| || || || ||
|
55
|
+
-------------------|||-------------||-||------||-||-------
|
56
|
+
|__> || || || ||
|
57
|
+
|
58
|
+
TEXT
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "sbmt/outbox/ascii_art"
|
5
|
+
|
6
|
+
module Sbmt
|
7
|
+
module Outbox
|
8
|
+
class CLI < Thor
|
9
|
+
def self.exit_on_failure?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
default_command :start
|
14
|
+
|
15
|
+
desc "start", "Start outbox worker"
|
16
|
+
option :box,
|
17
|
+
aliases: "-b",
|
18
|
+
repeatable: true,
|
19
|
+
desc: "Outbox/Inbox processors to start in format foo_name:1,2,n bar_name:1,2,n"
|
20
|
+
option :concurrency,
|
21
|
+
aliases: "-c",
|
22
|
+
type: :numeric,
|
23
|
+
default: 10,
|
24
|
+
desc: "Number of threads"
|
25
|
+
def start
|
26
|
+
load_environment
|
27
|
+
|
28
|
+
worker = Sbmt::Outbox::Worker.new(
|
29
|
+
boxes: format_boxes(options[:box]),
|
30
|
+
concurrency: options[:concurrency]
|
31
|
+
)
|
32
|
+
|
33
|
+
Sbmt::Outbox.current_worker = worker
|
34
|
+
|
35
|
+
watch_signals(worker)
|
36
|
+
|
37
|
+
$stdout.puts AsciiArt.logo
|
38
|
+
$stdout.puts "Outbox/Inbox worker has been started"
|
39
|
+
$stdout.puts "Version: #{VERSION}"
|
40
|
+
$stdout.puts "Starting probes..."
|
41
|
+
Sbmt::Outbox::Probes::Probe.run_probes
|
42
|
+
|
43
|
+
worker.start
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def load_environment
|
49
|
+
load(lookup_outboxfile)
|
50
|
+
|
51
|
+
require "sbmt/outbox"
|
52
|
+
require "sbmt/outbox/worker"
|
53
|
+
end
|
54
|
+
|
55
|
+
def lookup_outboxfile
|
56
|
+
file_path = ENV["OUTBOXFILE"] || "#{Dir.pwd}/Outboxfile"
|
57
|
+
|
58
|
+
raise "Cannot locate Outboxfile at #{file_path}" unless File.exist?(file_path)
|
59
|
+
|
60
|
+
file_path
|
61
|
+
end
|
62
|
+
|
63
|
+
def format_boxes(val)
|
64
|
+
if val.nil?
|
65
|
+
fetch_all_boxes
|
66
|
+
else
|
67
|
+
extract_boxes(val)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def fetch_all_boxes
|
72
|
+
Outbox.outbox_item_classes + Outbox.inbox_item_classes
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_boxes(boxes)
|
76
|
+
boxes.map do |name, acc|
|
77
|
+
item_class = Sbmt::Outbox.item_classes_by_name[name]
|
78
|
+
raise "Cannot locate box #{name}" unless item_class
|
79
|
+
item_class
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def watch_signals(worker)
|
84
|
+
# ctrl+c
|
85
|
+
Signal.trap("INT") do
|
86
|
+
$stdout.puts AsciiArt.shutdown
|
87
|
+
$stdout.puts "Going to shut down..."
|
88
|
+
worker.stop
|
89
|
+
end
|
90
|
+
|
91
|
+
# normal kill with number 15
|
92
|
+
Signal.trap("TERM") do
|
93
|
+
$stdout.puts AsciiArt.shutdown
|
94
|
+
$stdout.puts "Going to shut down..."
|
95
|
+
worker.stop
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/engine"
|
4
|
+
|
5
|
+
module Sbmt
|
6
|
+
module Outbox
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
isolate_namespace Sbmt::Outbox
|
9
|
+
|
10
|
+
config.outbox = ActiveSupport::OrderedOptions.new.tap do |c|
|
11
|
+
c.active_record_base_class = "ApplicationRecord"
|
12
|
+
c.active_job_base_class = "ApplicationJob"
|
13
|
+
c.error_tracker = "Sbmt::Outbox::ErrorTracker"
|
14
|
+
c.outbox_item_classes = []
|
15
|
+
c.inbox_item_classes = []
|
16
|
+
c.paths = []
|
17
|
+
c.redis = {url: ENV.fetch("REDIS_URL", "redis://127.0.0.1:6379")}
|
18
|
+
c.process_items = ActiveSupport::OrderedOptions.new.tap do |c|
|
19
|
+
c.general_timeout = 120
|
20
|
+
c.cutoff_timeout = 60
|
21
|
+
c.batch_size = 200
|
22
|
+
end
|
23
|
+
c.worker = ActiveSupport::OrderedOptions.new.tap do |c|
|
24
|
+
c.rate_limit = 20
|
25
|
+
c.rate_interval = 60
|
26
|
+
c.shuffle_jobs = true
|
27
|
+
end
|
28
|
+
c.database_switcher = "Sbmt::Outbox::DatabaseSwitcher"
|
29
|
+
c.batch_process_middlewares = []
|
30
|
+
c.item_process_middlewares = []
|
31
|
+
c.create_item_middlewares = []
|
32
|
+
|
33
|
+
if defined?(::Sentry)
|
34
|
+
c.batch_process_middlewares.push("Sbmt::Outbox::Middleware::Sentry::TracingBatchProcessMiddleware")
|
35
|
+
c.item_process_middlewares.push("Sbmt::Outbox::Middleware::Sentry::TracingItemProcessMiddleware")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
rake_tasks do
|
40
|
+
load "sbmt/outbox/tasks/retry_failed_items.rake"
|
41
|
+
load "sbmt/outbox/tasks/delete_failed_items.rake"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sbmt
|
4
|
+
module Outbox
|
5
|
+
class ErrorTracker
|
6
|
+
class << self
|
7
|
+
def error(message, params = {})
|
8
|
+
unless defined?(Sentry)
|
9
|
+
Outbox.logger.log_error(message, params)
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
Sentry.with_scope do |scope|
|
14
|
+
scope.set_contexts(contexts: params)
|
15
|
+
|
16
|
+
if message.is_a?(Exception)
|
17
|
+
Sentry.capture_exception(message, level: :error)
|
18
|
+
else
|
19
|
+
Sentry.capture_message(message, level: :error)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "opentelemetry"
|
4
|
+
require "opentelemetry-common"
|
5
|
+
require "opentelemetry-instrumentation-base"
|
6
|
+
|
7
|
+
require_relative "../middleware/open_telemetry/tracing_create_item_middleware"
|
8
|
+
require_relative "../middleware/open_telemetry/tracing_item_process_middleware"
|
9
|
+
|
10
|
+
module Sbmt
|
11
|
+
module Outbox
|
12
|
+
module Instrumentation
|
13
|
+
class OpenTelemetryLoader < ::OpenTelemetry::Instrumentation::Base
|
14
|
+
install do |_config|
|
15
|
+
require_dependencies
|
16
|
+
|
17
|
+
::Sbmt::Outbox.config.create_item_middlewares.push("Sbmt::Outbox::Middleware::OpenTelemetry::TracingCreateItemMiddleware")
|
18
|
+
::Sbmt::Outbox.config.item_process_middlewares.push("Sbmt::Outbox::Middleware::OpenTelemetry::TracingItemProcessMiddleware")
|
19
|
+
end
|
20
|
+
|
21
|
+
present do
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def require_dependencies
|
28
|
+
require_relative "../middleware/open_telemetry/tracing_create_item_middleware"
|
29
|
+
require_relative "../middleware/open_telemetry/tracing_item_process_middleware"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sbmt
|
4
|
+
module Outbox
|
5
|
+
class Logger
|
6
|
+
delegate :logger, to: :Rails
|
7
|
+
|
8
|
+
def log_info(message, **params)
|
9
|
+
with_tags(**params) do
|
10
|
+
logger.info(message)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_error(message, **params)
|
15
|
+
with_tags(**params) do
|
16
|
+
logger.error(message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_success(message, **params)
|
21
|
+
log_info(message, status: "success", **params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_failure(message, **params)
|
25
|
+
log_error(message, status: "failure", **params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_tags(**params)
|
29
|
+
logger.tagged(**params) do
|
30
|
+
yield
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sbmt
|
4
|
+
module Outbox
|
5
|
+
module Middleware
|
6
|
+
class Builder
|
7
|
+
def initialize(middlewares)
|
8
|
+
middlewares.each { |middleware| stack << middleware }
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(...)
|
12
|
+
Runner.new(stack.dup).call(...)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def stack
|
18
|
+
@stack ||= []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|