sbmt-outbox 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|