db_blaster 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d35e663180807e21555f50de79be57ebaf73a7a2effbc86fda8cb2b59ea538bb
4
- data.tar.gz: fcd1df984229d196a858e7e81fc94503eabb476bb3569afc0ff66871f6ab0e6e
3
+ metadata.gz: 9e7b7ce6209a45251fe19c47d6248e932e042bca2ef60e4507b7365dd517555b
4
+ data.tar.gz: 774d0e7784134bfc8b14759cb7eb832851245d24a1afbcb7f1bd300be49f6537
5
5
  SHA512:
6
- metadata.gz: 270045284ee3ff5032bbb5b34de7bd3ad6add7252b6c7af05dfa6e1525f6b156c2d11138a62cbd1a3630606bb52e6df25b3e050ce24262ab99d4679b2ba84dd3
7
- data.tar.gz: af1f643cae181fdbfba7f6a5ec11acef540f33d5bda76ce9753e6e0065dd40ca8640af80fcc9bd530ada2645ef8b4131f4afc5cb00f5c0f439836ab700425de5
6
+ metadata.gz: 2ab4c82a9debbaeb34f40353d5e0fae79077920bd4509469ea3a4bf51788f504a813c20b57aced15c68025ae34de6dd3fb97e112d2240613adb33fddcd64f429
7
+ data.tar.gz: 600d83c0a603c961b68a5751dbfd5685c4aefd916484905abb00e4997977fed4f63c7f79adb18291a6b64d9dab3c89d48a89aa3b19b47218b5b5ab20fec3c8b0
data/README.md CHANGED
@@ -2,15 +2,15 @@
2
2
  # DbBlaster
3
3
  ![Image of DB to SNS](https://lucid.app/publicSegments/view/c70feed3-2f48-46ee-8734-423474488feb/image.png)
4
4
 
5
- DbBlaster publishes changed database rows to AWS SNS. The first time `DbBlaster::PublishAllJob.perform_later` is ran,
6
- the entire database will be incrementally published to SNS. Subsequent runs will publish rows whose `updated_at` column
5
+ DbBlaster can either publish changed database rows to AWS SNS or push the changes to S#. The first time `DbBlaster::PublishAllJob.perform_later` is ran,
6
+ the entire database will be incrementally published. Subsequent runs will publish rows whose `updated_at` column
7
7
  is more recent than the last run.
8
8
 
9
9
  Consuming the published messages is functionality not provided by DbBlaster.
10
10
 
11
11
  ## Usage
12
12
 
13
- Update `config/initializers/db_blaster_config.rb` with valid AWS credentials, topics, and options.
13
+ Update `config/initializers/db_blaster_config.rb` with valid AWS credentials and options. Either `sns_topic` or `s3_bucket` must be set!
14
14
 
15
15
  Schedule `DbBlaster::PublishAllJob.perform_later` to run periodically with something
16
16
  like [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron) or [whenever](https://github.com/javan/whenever)
@@ -13,8 +13,12 @@ module DbBlaster
13
13
  .build_all(DbBlaster.configuration))
14
14
 
15
15
  DbBlaster::SourceTable.pluck(:id).each do |source_table_id|
16
- PublishSourceTableJob.perform_later(source_table_id)
16
+ PublishSourceTableJob.perform_later(source_table_id, batch_start_time)
17
17
  end
18
18
  end
19
+
20
+ def batch_start_time
21
+ @batch_start_time ||= DateTime.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')
22
+ end
19
23
  end
20
24
  end
@@ -6,11 +6,11 @@ module DbBlaster
6
6
  class PublishSourceTableJob < ApplicationJob
7
7
  queue_as 'default'
8
8
 
9
- def perform(source_table_id)
9
+ def perform(source_table_id, batch_start_time)
10
10
  source_table = SourceTable.find_by(id: source_table_id)
11
11
  return unless source_table
12
12
 
13
- PublishSourceTable.execute(source_table)
13
+ PublishSourceTable.execute(source_table: source_table, batch_start_time: batch_start_time)
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # publish records to SNS topic
4
+ module DbBlaster
5
+ # Base class for publishing
6
+ class BasePublisher
7
+ attr_reader :source_table, :records, :batch_start_time
8
+
9
+ def initialize(source_table, records, batch_start_time)
10
+ @source_table = source_table
11
+ @records = records
12
+ @batch_start_time = batch_start_time
13
+ end
14
+
15
+ def self.publish(source_table:, records:, batch_start_time:)
16
+ publisher_class =
17
+ if DbBlaster.configuration.sns_topic
18
+ SnsPublisher
19
+ else
20
+ S3Publisher
21
+ end
22
+ publisher_class.new(source_table, records, batch_start_time).publish
23
+ end
24
+
25
+ def publish
26
+ raise NotImplementedError
27
+ end
28
+ end
29
+ end
@@ -2,17 +2,49 @@
2
2
 
3
3
  module DbBlaster
4
4
  # Configuration class for providing credentials, topics, and customizations.
5
+ # Either the `sns_topic` or `s3_bucket' must be set
5
6
  class Configuration
6
7
  DEFAULT_BATCH_SIZE = 100
7
8
  DEFAULT_MAX_MESSAGE_SIZE_IN_KILOBYTES = 256 # max size allowed by AWS SNS
9
+ DEFAULT_S3_KEY = '<batch_timestamp>/db_blaster/<table_name>/<uuid>.json'
8
10
 
9
11
  # The required configuration fields
10
- REQUIRED_FIELDS = %i[aws_access_key aws_access_secret aws_region sns_topic].freeze
12
+ REQUIRED_FIELDS = %i[aws_access_key aws_access_secret aws_region].freeze
13
+
14
+ # exactly one of these fields needs to be set
15
+ EITHER_OR_FIELDS = %i[sns_topic s3_bucket].freeze
11
16
 
12
17
  # The topic to which messages will be published
13
18
  attr_accessor :sns_topic
19
+ # The s3 bucket name
20
+ attr_accessor :s3_bucket
14
21
  attr_accessor :aws_access_key, :aws_access_secret, :aws_region
15
22
 
23
+ # Optional
24
+ # Applicable only when `sns_topic` is set
25
+ # Extra [SNS message_attributes](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html)
26
+ # Attributes set here will be included in every published message
27
+ # example: config.extra_sns_message_attributes = {'infra_id' => {data_type: 'String', value: '061'}}
28
+ attr_accessor :extra_sns_message_attributes
29
+
30
+ # Optional
31
+ # Applicable only when `s3_bucket' is set
32
+ # The value set here will be included in every payload pushed to S3
33
+ # example: config.s3_meta = {'infra_id' => '061', 'src_app' => 'kcp-api'}}
34
+ # The resulting JSON:
35
+ # {"meta" : {"infra_id" : "061", "src_app" : "kcp-api", "src_table" : "the-table"}, "records" : [] }
36
+ attr_accessor :s3_meta
37
+
38
+ # Optional
39
+ # Applicable only when `s3_bucket` is set
40
+ # The S3 key. The following values will get substituted:
41
+ # <batch_timestamp> - a timestamp signifying the beginning of the batch processing
42
+ # <timestamp> - the current time
43
+ # <table_name> - the name of the table associated with the S3 body
44
+ # <uuid> - a universal identifier
45
+ # '<batch_timestamp>/kcp-api/001/<table_name>/<uuid>.json'
46
+ attr_accessor :s3_key
47
+
16
48
  # Global list of column names not to include in published SNS messages
17
49
  # example: config.ignored_column_names = ['email', 'phone_number']
18
50
  attr_accessor :ignored_column_names
@@ -34,12 +66,6 @@ module DbBlaster
34
66
  # { source_table_name: 'comments', ignored_column_names: ['tags'] }]
35
67
  attr_accessor :source_table_options
36
68
 
37
- # Optional
38
- # Extra [SNS message_attributes](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html)
39
- # Attributes set here will be included in every published message
40
- # example: config.extra_sns_message_attributes = {'infra_id' => {data_type: 'String', value: '061'}}
41
- attr_accessor :extra_sns_message_attributes
42
-
43
69
  # Optional
44
70
  # db_blaster will select and then publish `batch_size` rows at a time
45
71
  # Default value is 100
@@ -52,12 +78,22 @@ module DbBlaster
52
78
 
53
79
  # Raises error if a required field is not set
54
80
  def verify!
55
- no_values = REQUIRED_FIELDS.select do |attribute|
81
+ verify_required
82
+ verify_either_or
83
+ end
84
+
85
+ def verify_either_or
86
+ either_or = EITHER_OR_FIELDS.select do |attribute|
56
87
  send(attribute).nil? || send(attribute).strip.empty?
57
88
  end
58
- return if no_values.empty?
89
+ raise "only one of [#{either_or.join(', ')}] should be set" unless either_or.length == 1
90
+ end
59
91
 
60
- raise "missing configuration values for [#{no_values.join(', ')}]"
92
+ def verify_required
93
+ no_values = REQUIRED_FIELDS.select do |attribute|
94
+ send(attribute).nil? || send(attribute).strip.empty?
95
+ end
96
+ raise "missing configuration values for [#{no_values.join(', ')}]" unless no_values.empty?
61
97
  end
62
98
  end
63
99
  end
@@ -4,14 +4,15 @@ module DbBlaster
4
4
  # Given a `source_table` providing the table name,
5
5
  # finds rows in `batch_size` chunks that are published to SNS
6
6
  class PublishSourceTable
7
- attr_reader :source_table
7
+ attr_reader :source_table, :batch_start_time
8
8
 
9
- def initialize(source_table)
9
+ def initialize(source_table, batch_start_time)
10
10
  @source_table = source_table
11
+ @batch_start_time = batch_start_time
11
12
  end
12
13
 
13
- def self.execute(source_table)
14
- new(source_table).execute
14
+ def self.execute(source_table:, batch_start_time:)
15
+ new(source_table, batch_start_time).execute
15
16
  end
16
17
 
17
18
  def execute
@@ -20,7 +21,7 @@ module DbBlaster
20
21
  # pessimistically lock row for the duration
21
22
  source_table.with_lock do
22
23
  Finder.find(source_table) do |records|
23
- Publisher.publish(source_table, records)
24
+ BasePublisher.publish(source_table: source_table, records: records, batch_start_time: batch_start_time)
24
25
  source_table.update(last_published_updated_at: records.last['updated_at'])
25
26
  end
26
27
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DbBlaster
4
+ # Builds the key to be used for the uploaded S3 Object
5
+ class S3KeyBuilder
6
+ attr_reader :source_table_name, :batch_start_time
7
+
8
+ def initialize(source_table_name, batch_start_time)
9
+ @source_table_name = source_table_name
10
+ @batch_start_time = batch_start_time
11
+ end
12
+
13
+ def self.build(source_table_name:, batch_start_time:)
14
+ new(source_table_name, batch_start_time).build
15
+ end
16
+
17
+ def build
18
+ key = starting_key
19
+ substitutions.each do |replace, value|
20
+ key = key.gsub(replace, value)
21
+ end
22
+ key
23
+ end
24
+
25
+ def substitutions
26
+ { '<batch_timestamp>' => batch_start_time,
27
+ '<uuid>' => SecureRandom.uuid,
28
+ '<table_name>' => source_table_name }
29
+ end
30
+
31
+ def starting_key
32
+ DbBlaster.configuration.s3_key.presence || Configuration::DEFAULT_S3_KEY
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-s3'
4
+
5
+ module DbBlaster
6
+ # Pushes records to S3
7
+ class S3Publisher < BasePublisher
8
+ def publish
9
+ client.put_object(bucket: DbBlaster.configuration.s3_bucket,
10
+ key: S3KeyBuilder.build(source_table_name: source_table.name,
11
+ batch_start_time: batch_start_time),
12
+ body: content.to_json)
13
+ end
14
+
15
+ def content
16
+ { meta: meta,
17
+ records: records }
18
+ end
19
+
20
+ def meta
21
+ (DbBlaster.configuration.s3_meta || {}).merge(source_table: source_table.name)
22
+ end
23
+
24
+ def client
25
+ @client ||= Aws::S3::Client.new(region: DbBlaster.configuration.aws_region,
26
+ credentials: Aws::Credentials.new(DbBlaster.configuration.aws_access_key,
27
+ DbBlaster.configuration.aws_access_secret))
28
+ end
29
+ end
30
+ end
@@ -1,20 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # publish records to SNS topic
4
- module DbBlaster
5
- # Publishes records to AWS SNS
6
- class Publisher
7
- attr_reader :source_table, :records
8
-
9
- def initialize(source_table, records)
10
- @source_table = source_table
11
- @records = records
12
- end
3
+ require 'aws-sdk-sns'
13
4
 
14
- def self.publish(source_table, records)
15
- new(source_table, records).publish
16
- end
5
+ # frozen_string_literal: true
17
6
 
7
+ module DbBlaster
8
+ # Publishes records to AWS SNS
9
+ class SnsPublisher < BasePublisher
18
10
  def publish
19
11
  topic.publish(message_attributes: message_attributes,
20
12
  message: records.to_json)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DbBlaster
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
data/lib/db_blaster.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aws-sdk-sns'
4
3
  require 'db_blaster/version'
5
4
  require 'db_blaster/engine'
6
5
  require 'db_blaster/one_record_too_large_error'
@@ -8,7 +7,10 @@ require 'db_blaster/available_tables'
8
7
  require 'db_blaster/configuration'
9
8
  require 'db_blaster/source_table_configuration'
10
9
  require 'db_blaster/source_table_configuration_builder'
11
- require 'db_blaster/publisher'
10
+ require 'db_blaster/base_publisher'
11
+ require 'db_blaster/s3_key_builder'
12
+ require 'db_blaster/s3_publisher'
13
+ require 'db_blaster/sns_publisher'
12
14
  require 'db_blaster/publish_source_table'
13
15
  require 'db_blaster/chunker'
14
16
  require 'db_blaster/finder_sql'
@@ -1,12 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Either `sns_topic` or `s3_bucket` must be set
3
4
  DbBlaster.configure do |config|
4
- # SNS topic to receive database changes
5
+ # SNS topic to receive database changes. Either `sns_topic` or `s3_bucket` must be set
5
6
  config.sns_topic = 'the-topic'
6
7
  config.aws_access_key = 'access-key'
7
8
  config.aws_access_secret = 'secret'
8
9
  config.aws_region = 'region'
9
10
 
11
+ # Optional
12
+ # Applicable only when `sns_topic` is set
13
+ # Extra [SNS message_attributes](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html)
14
+ # Attributes set here will be included in every published message
15
+ # config.extra_sns_message_attributes = {'infra_id' => {data_type: 'String', value: '061'}}
16
+
17
+ # S3 bucket where JSON will be pushed. Either `sns_topic` or `s3_bucket` must be set
18
+ # config.s3_bucket = 'bucket-name'
19
+
20
+ # Optional
21
+ # Applicable only when `s3_bucket` is set
22
+ # The S3 key path. The following values will get substituted:
23
+ # <batch_timestamp> - a timestamp signifying the beginning of the batch processing
24
+ # <timestamp> - the current time
25
+ # <table_name> - the name of the table associated with the S3 body
26
+ # <uuid> - a universal identifier
27
+ # config.s3_key = '<batch_timestamp>/kcp-api/001/<table_name>/<uuid>.json'
28
+
29
+ # Optional
30
+ # Applicable only when `s3_bucket' is set
31
+ # Extra meta values sent along with each payload
32
+ # example: config.s3_meta = {'infra_id' => '061'}
33
+ # The resulting JSON:
34
+ # {"meta" : {"infra_id" : "061", "src_app" : "kcp-api", "src_table" : "the-table"}, "records" : [] }
35
+ # config.s3_meta = {'infra_id' => '061'}
36
+
10
37
  # Optional
11
38
  # db_blaster will select and then publish `batch_size` rows at a time
12
39
  # config.batch_size = 100
@@ -16,11 +43,6 @@ DbBlaster.configure do |config|
16
43
  # Default value is 256
17
44
  # config.max_message_size_in_kilobytes = 256
18
45
 
19
- # Optional
20
- # Extra [SNS message_attributes](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html)
21
- # Attributes set here will be included in every published message
22
- # config.extra_sns_message_attributes = {'infra_id' => {data_type: 'String', value: '061'}}
23
-
24
46
  # Global list of column names not to include in published SNS messages
25
47
  # example: config.ignored_column_names = ['email', 'phone_number']
26
48
  # config.ignored_column_names = ['email', 'phone_number']
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db_blaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Perry Hertler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-12 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-s3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: aws-sdk-sns
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +133,7 @@ files:
119
133
  - db/migrate/20210727222252_create_source_tables.rb
120
134
  - lib/db_blaster.rb
121
135
  - lib/db_blaster/available_tables.rb
136
+ - lib/db_blaster/base_publisher.rb
122
137
  - lib/db_blaster/chunker.rb
123
138
  - lib/db_blaster/configuration.rb
124
139
  - lib/db_blaster/engine.rb
@@ -126,8 +141,10 @@ files:
126
141
  - lib/db_blaster/finder_sql.rb
127
142
  - lib/db_blaster/one_record_too_large_error.rb
128
143
  - lib/db_blaster/publish_source_table.rb
129
- - lib/db_blaster/publisher.rb
130
144
  - lib/db_blaster/rspec.rb
145
+ - lib/db_blaster/s3_key_builder.rb
146
+ - lib/db_blaster/s3_publisher.rb
147
+ - lib/db_blaster/sns_publisher.rb
131
148
  - lib/db_blaster/source_table_configuration.rb
132
149
  - lib/db_blaster/source_table_configuration_builder.rb
133
150
  - lib/db_blaster/version.rb