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.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +440 -0
  3. data/Rakefile +3 -0
  4. data/app/interactors/sbmt/outbox/base_create_item.rb +55 -0
  5. data/app/interactors/sbmt/outbox/create_inbox_item.rb +10 -0
  6. data/app/interactors/sbmt/outbox/create_outbox_item.rb +10 -0
  7. data/app/interactors/sbmt/outbox/dry_interactor.rb +16 -0
  8. data/app/interactors/sbmt/outbox/partition_strategies/hash_partitioning.rb +20 -0
  9. data/app/interactors/sbmt/outbox/partition_strategies/number_partitioning.rb +26 -0
  10. data/app/interactors/sbmt/outbox/process_item.rb +269 -0
  11. data/app/interactors/sbmt/outbox/retry_strategies/compacted_log.rb +41 -0
  12. data/app/interactors/sbmt/outbox/retry_strategies/exponential_backoff.rb +34 -0
  13. data/app/jobs/sbmt/outbox/base_delete_stale_items_job.rb +78 -0
  14. data/app/jobs/sbmt/outbox/delete_stale_inbox_items_job.rb +15 -0
  15. data/app/jobs/sbmt/outbox/delete_stale_outbox_items_job.rb +15 -0
  16. data/app/models/sbmt/outbox/base_item.rb +165 -0
  17. data/app/models/sbmt/outbox/base_item_config.rb +106 -0
  18. data/app/models/sbmt/outbox/inbox_item.rb +38 -0
  19. data/app/models/sbmt/outbox/inbox_item_config.rb +13 -0
  20. data/app/models/sbmt/outbox/outbox_item.rb +52 -0
  21. data/app/models/sbmt/outbox/outbox_item_config.rb +13 -0
  22. data/config/initializers/schked.rb +9 -0
  23. data/config/initializers/yabeda.rb +71 -0
  24. data/config/schedule.rb +9 -0
  25. data/exe/outbox +16 -0
  26. data/lib/generators/helpers/config.rb +46 -0
  27. data/lib/generators/helpers/initializer.rb +41 -0
  28. data/lib/generators/helpers/items.rb +17 -0
  29. data/lib/generators/helpers/migration.rb +73 -0
  30. data/lib/generators/helpers/paas.rb +17 -0
  31. data/lib/generators/helpers/values.rb +49 -0
  32. data/lib/generators/helpers.rb +8 -0
  33. data/lib/generators/outbox/install/USAGE +10 -0
  34. data/lib/generators/outbox/install/install_generator.rb +33 -0
  35. data/lib/generators/outbox/install/templates/Outboxfile +3 -0
  36. data/lib/generators/outbox/install/templates/outbox.rb +32 -0
  37. data/lib/generators/outbox/install/templates/outbox.yml +51 -0
  38. data/lib/generators/outbox/item/USAGE +12 -0
  39. data/lib/generators/outbox/item/item_generator.rb +94 -0
  40. data/lib/generators/outbox/item/templates/inbox_item.rb.tt +7 -0
  41. data/lib/generators/outbox/item/templates/outbox_item.rb.tt +16 -0
  42. data/lib/generators/outbox/transport/USAGE +19 -0
  43. data/lib/generators/outbox/transport/templates/inbox_transport.yml.erb +9 -0
  44. data/lib/generators/outbox/transport/templates/outbox_transport.yml.erb +10 -0
  45. data/lib/generators/outbox/transport/transport_generator.rb +60 -0
  46. data/lib/generators/outbox.rb +23 -0
  47. data/lib/sbmt/outbox/ascii_art.rb +62 -0
  48. data/lib/sbmt/outbox/cli.rb +100 -0
  49. data/lib/sbmt/outbox/database_switcher.rb +15 -0
  50. data/lib/sbmt/outbox/engine.rb +45 -0
  51. data/lib/sbmt/outbox/error_tracker.rb +26 -0
  52. data/lib/sbmt/outbox/errors.rb +14 -0
  53. data/lib/sbmt/outbox/instrumentation/open_telemetry_loader.rb +34 -0
  54. data/lib/sbmt/outbox/logger.rb +35 -0
  55. data/lib/sbmt/outbox/middleware/builder.rb +23 -0
  56. data/lib/sbmt/outbox/middleware/open_telemetry/tracing_create_item_middleware.rb +42 -0
  57. data/lib/sbmt/outbox/middleware/open_telemetry/tracing_item_process_middleware.rb +49 -0
  58. data/lib/sbmt/outbox/middleware/runner.rb +29 -0
  59. data/lib/sbmt/outbox/middleware/sentry/tracing_batch_process_middleware.rb +48 -0
  60. data/lib/sbmt/outbox/middleware/sentry/tracing_item_process_middleware.rb +65 -0
  61. data/lib/sbmt/outbox/middleware/sentry/transaction.rb +28 -0
  62. data/lib/sbmt/outbox/probes/probe.rb +38 -0
  63. data/lib/sbmt/outbox/redis_client_factory.rb +36 -0
  64. data/lib/sbmt/outbox/tasks/delete_failed_items.rake +17 -0
  65. data/lib/sbmt/outbox/tasks/retry_failed_items.rake +20 -0
  66. data/lib/sbmt/outbox/thread_pool.rb +108 -0
  67. data/lib/sbmt/outbox/throttler.rb +52 -0
  68. data/lib/sbmt/outbox/version.rb +7 -0
  69. data/lib/sbmt/outbox/worker.rb +233 -0
  70. data/lib/sbmt/outbox.rb +136 -0
  71. data/lib/sbmt-outbox.rb +3 -0
  72. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= namespaced_item_class_name %> < Sbmt::Outbox::InboxItem
5
+ self.table_name = "<%= migration_table_name %>"
6
+ end
7
+ <% 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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ class DatabaseSwitcher
6
+ def self.use_slave
7
+ yield
8
+ end
9
+
10
+ def self.use_master
11
+ yield
12
+ end
13
+ end
14
+ end
15
+ 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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ class Error < StandardError
6
+ end
7
+
8
+ class ConfigError < Error
9
+ end
10
+
11
+ class DatabaseError < Error
12
+ end
13
+ end
14
+ 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