pgdice 0.4.2 → 2.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +4 -1
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/gem-push.yml +20 -0
  6. data/.github/workflows/ruby.yml +62 -0
  7. data/.rubocop.yml +12 -2
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +21 -0
  10. data/README.md +22 -26
  11. data/SECURITY.md +15 -0
  12. data/examples/aws/README.md +28 -0
  13. data/examples/aws/cloudformation/scheduled_events.json +59 -0
  14. data/examples/aws/lib/sqs_listener/default_event_handler.rb +32 -0
  15. data/examples/aws/lib/sqs_listener/exceptions/unknown_task_error.rb +4 -0
  16. data/examples/aws/lib/sqs_listener/fallthrough_event_handler.rb +18 -0
  17. data/examples/aws/lib/sqs_listener/sqs_event_router.rb +32 -0
  18. data/examples/aws/lib/sqs_listener/typed_event_handler/task_event_handler.rb +46 -0
  19. data/examples/aws/lib/sqs_listener/typed_event_handler/tasks/database_tasks.rb +37 -0
  20. data/examples/aws/lib/sqs_listener.rb +47 -0
  21. data/examples/aws/lib/sqs_message_deleter.rb +32 -0
  22. data/examples/aws/lib/sqs_poller.rb +67 -0
  23. data/examples/aws/tasks/poll_sqs.rake +8 -0
  24. data/examples/aws/workers/pg_dice_worker.rb +54 -0
  25. data/lib/pgdice/approved_tables.rb +3 -2
  26. data/lib/pgdice/configuration.rb +5 -5
  27. data/lib/pgdice/configuration_file_loader.rb +1 -1
  28. data/lib/pgdice/database_connection_factory.rb +5 -6
  29. data/lib/pgdice/date_helper.rb +2 -2
  30. data/lib/pgdice/error.rb +2 -2
  31. data/lib/pgdice/partition_dropper.rb +1 -1
  32. data/lib/pgdice/partition_helper.rb +2 -2
  33. data/lib/pgdice/pg_slice_manager.rb +5 -5
  34. data/lib/pgdice/query_executor.rb +3 -3
  35. data/lib/pgdice/query_executor_factory.rb +20 -0
  36. data/lib/pgdice/table.rb +2 -2
  37. data/lib/pgdice/version.rb +1 -1
  38. data/lib/pgdice.rb +1 -0
  39. data/pgdice.gemspec +26 -22
  40. metadata +87 -49
  41. data/.circleci/config.yml +0 -66
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ae586abaf672ced69ab9bab60300d99827b9b39363077c1ead5376c74823ba3
4
- data.tar.gz: 5cd703d8edaaf0315e62a761ea1a34d7f8b9e07b7a9e5caa09505d16ce20030e
3
+ metadata.gz: 1a9a4ad3435e9d6a9590f0a40a9d9e0329a2d2719ca92b9781f10b301311d31b
4
+ data.tar.gz: 80840f0d48445932a5fd6fe1a908c7e51c5cbbac1d171ea1fdd3c406052b9f63
5
5
  SHA512:
6
- metadata.gz: 93ebdada6b6e423bb1b0befda9bae91b52d3e19b498c818d38dac31268f77b21462b75b17bfc4c95281915e38c786a8f16cc2297fb97090b1c2756730c4c8706
7
- data.tar.gz: 8c25ece5a48b2972b0571de6be7a0a6912c193af0ad3193224b1946bf70b04f1771b6d209ccdb7e720aeeb9b53f4ae3ab74542de1bae22d33a17f11dd8e3c00b
6
+ metadata.gz: e92d88f79eb9630b5e73e89b2b3191ba24699a6845e75e98cc2c25d9027adc4cbeb1981485f229daf5286e6b2e7f0b43b54d4c6c793fe3d33896be19ad1b8416
7
+ data.tar.gz: bfdac98da6f9b8b59f8188bfacb48106683761f651af387497bce7096047022b7e2b621803394779063afaf93626256a3b3fdfacd8fd5aca6d23890b08fe78c0
data/.codeclimate.yml CHANGED
@@ -2,4 +2,7 @@ version: "2"
2
2
  checks:
3
3
  argument-count:
4
4
  config:
5
- threshold: 5
5
+ threshold: 5
6
+
7
+ exclude_patterns:
8
+ - "examples/**/*"
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: needs investigation
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+
16
+ **Expected behavior**
17
+ A clear and concise description of what you expected to happen.
18
+
19
+ **Screenshots**
20
+ If applicable, add screenshots to help explain your problem.
21
+
22
+ **Please include this information**
23
+ - Postgres Version: [e.g. 10.6]
24
+ - PgDice Version [e.g. 0.14.3]
25
+
26
+ **Additional context**
27
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: needs investigation
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,20 @@
1
+ name: Publish Gem
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - uses: actions/checkout@v1
13
+
14
+ - name: Release Gem
15
+ if: contains(github.ref, 'refs/tags/v')
16
+ uses: cadwallion/publish-rubygems-action@master
17
+ env:
18
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
19
+ RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
20
+ RELEASE_COMMAND: rake release
@@ -0,0 +1,62 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby-version: ['3.0']
23
+
24
+ services:
25
+ # Label used to access the service container
26
+ postgres:
27
+ # Docker Hub image
28
+ image: postgres
29
+ # Provide the password for postgres
30
+ env:
31
+ POSTGRES_USER: pgdice
32
+ POSTGRES_DB: pgdice_test
33
+ POSTGRES_PASSWORD: password
34
+ # Set health checks to wait until postgres has started
35
+ options: >-
36
+ --health-cmd pg_isready
37
+ --health-interval 10s
38
+ --health-timeout 5s
39
+ --health-retries 5
40
+ ports:
41
+ - 5432:5432
42
+
43
+ steps:
44
+ - uses: actions/checkout@v2
45
+ - name: Set up Ruby
46
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
47
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
48
+ # uses: ruby/setup-ruby@v1
49
+ uses: ruby/setup-ruby@v1
50
+ with:
51
+ ruby-version: ${{ matrix.ruby-version }}
52
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
53
+ # - name: Rubocop # This is blowing up with some stupid panolint gem missing - I have no mention of this anywhere.
54
+ # run: bundle exec rubocop
55
+
56
+ - name: Run tests
57
+ run: bundle exec rake
58
+ env:
59
+ DATABASE_HOST: localhost
60
+ DATABASE_USERNAME: pgdice
61
+ DATABASE_PASSWORD: password
62
+ PGDICE_LOG_TARGET: STDOUT
data/.rubocop.yml CHANGED
@@ -1,4 +1,8 @@
1
- Metrics/LineLength:
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rake
4
+
5
+ Layout/LineLength:
2
6
  Max: 120
3
7
 
4
8
  Metrics/BlockLength:
@@ -8,4 +12,10 @@ Metrics/BlockLength:
8
12
  # TODO: Evaluate if this is an acceptable parameter list. It's a constructor after all.
9
13
  Metrics/ParameterLists:
10
14
  Exclude:
11
- - lib/pgdice/table.rb
15
+ - lib/pgdice/table.rb
16
+
17
+ AllCops:
18
+ NewCops: enable
19
+ SuggestExtensions: false
20
+ Exclude:
21
+ - 'examples/**/*'
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.5.1
1
+ ruby-3.0.3
data/CHANGELOG.md CHANGED
@@ -2,11 +2,32 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ ## [v2.0.0] : 2022-02-09
6
+ ### Changes
7
+ - Bump `pg` to `~> 1.3.1`
8
+ - Bump `pgslice` to `0.4.7`
9
+ - Bump various development dependencies
10
+ - Rubocop project
11
+ - Add `required_ruby_version >= 3.0.0`
12
+
13
+
14
+ ## [v1.0.1] : 2020-01-31
15
+ ### Changes
16
+ - Added better examples/docs.
17
+ - Bump `pg` to `~> 1.2.2`
18
+
19
+
20
+ ## [v0.4.3] : 2019-04-23
21
+ ### Changes
22
+ - Fix #31 where the new connection retry code was eagerly initializing the logger
23
+
24
+
5
25
  ## [v0.4.2] : 2019-04-22
6
26
  ### Changes
7
27
  - Fix #19 where PgDice wouldn't recover from a broken PG::Connection
8
28
  by adding new retry behavior
9
29
 
30
+
10
31
  ## [v0.4.1] : 2019-03-21
11
32
  ### Changes
12
33
  - Fix bug where partitioning by months would break when the month was < 10
data/README.md CHANGED
@@ -1,6 +1,4 @@
1
- [![CircleCI](https://circleci.com/gh/IlluminusLimited/pgdice.svg?style=shield)](https://circleci.com/gh/IlluminusLimited/pgdice)
2
- [![Coverage Status](https://coveralls.io/repos/github/IlluminusLimited/pgdice/badge.svg?branch=master)](https://coveralls.io/github/IlluminusLimited/pgdice?branch=master)
3
- [![Maintainability](https://api.codeclimate.com/v1/badges/311e005a14749bf2f826/maintainability)](https://codeclimate.com/github/IlluminusLimited/pgdice/maintainability)
1
+ [![Ruby](https://github.com/IlluminusLimited/pgdice/actions/workflows/ruby.yml/badge.svg)](https://github.com/IlluminusLimited/pgdice/actions/workflows/ruby.yml)[![Maintainability](https://api.codeclimate.com/v1/badges/311e005a14749bf2f826/maintainability)](https://codeclimate.com/github/IlluminusLimited/pgdice/maintainability)
4
2
  [![Test Coverage](https://api.codeclimate.com/v1/badges/311e005a14749bf2f826/test_coverage)](https://codeclimate.com/github/IlluminusLimited/pgdice/test_coverage)
5
3
  [![Gem Version](https://badge.fury.io/rb/pgdice.svg)](https://badge.fury.io/rb/pgdice)
6
4
 
@@ -12,18 +10,9 @@ PgDice is a utility for creating and maintaining partitioned database tables tha
12
10
  PgDice is intended to be used by scheduled background jobs in frameworks like [Sidekiq](https://github.com/mperham/sidekiq)
13
11
  where logging and clear exception messages are crucial.
14
12
 
13
+ # Maintenance status
15
14
 
16
- ## Disclaimer
17
-
18
- There are some features in this gem which allow you to drop database tables.
19
-
20
- If you choose to use this software without a __tested and working__ backup and restore strategy in place then you
21
- are a fool and will pay the price for your negligence. THIS SOFTWARE IS PROVIDED "AS IS",
22
- WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. By using this software you agree that the creator,
23
- maintainers and any affiliated parties CANNOT BE HELD LIABLE FOR DATA LOSS OR LOSSES OF ANY KIND.
24
-
25
- See the [LICENSE](LICENSE) for more information.
26
-
15
+ This project is stable and used daily in production.
27
16
 
28
17
  # Installation
29
18
 
@@ -69,7 +58,6 @@ PgDice.configure do |config|
69
58
  end
70
59
  ```
71
60
 
72
-
73
61
  ### Configuration Parameters
74
62
 
75
63
  - `database_url` - **Required**: The postgres database url to connect to.
@@ -179,7 +167,6 @@ PgDice.undo_partitioning!('comments')
179
167
  This method will revert the changes made by partitioning a table. Don't rely on this
180
168
  in production if you mess up; you need to test everything thoroughly.
181
169
 
182
-
183
170
  ## Maintaining partitioned tables
184
171
 
185
172
  ### Adding more tables
@@ -196,7 +183,6 @@ PgDice.add_new_partitions('comments')
196
183
  that the partitioned table was defined with.
197
184
  - The example `comments` table we have been using was configured to always keep `7` future partitions above.
198
185
 
199
-
200
186
  ### Listing droppable partitions
201
187
 
202
188
  Sometimes you just want to know what's out there and if there are tables ready to be dropped.
@@ -213,7 +199,6 @@ PgDice.list_droppable_partitions_by_batch_size('comments')
213
199
 
214
200
  This method will show partitions that are within the configured `batch_size`.
215
201
 
216
-
217
202
  #### Notes on `list_droppable_partitions`
218
203
 
219
204
  - This method uses the `past` value from the `PgDice::Table` to determine which tables are eligible for dropping.
@@ -235,7 +220,6 @@ PgDice.drop_old_partitions('comments')
235
220
  for the `PgDice::Table`.
236
221
  - The example `comments` table has been configured with `past: 90` tables.
237
222
  So if there were 100 tables older than `today` it would drop up to `batch_size` tables.
238
-
239
223
 
240
224
  # Validation
241
225
 
@@ -247,7 +231,7 @@ To validate that your expected number of tables exist, you can run:
247
231
  PgDice.assert_tables('comments')
248
232
  ```
249
233
 
250
- An [InsufficientTablesError](lib/pgdice.rb) will be raised if any conditions are not met.
234
+ An [InsufficientTablesError](lib/pgdice/error.rb) will be raised if any conditions are not met.
251
235
 
252
236
  This will check that there are 7 future tables from now and that there are 90 past tables
253
237
  per our configuration above.
@@ -268,7 +252,6 @@ PgDice.approved_tables
268
252
 
269
253
  The [ApprovedTables](lib/pgdice/approved_tables.rb) object responds to the most common enumerable methods.
270
254
 
271
-
272
255
  # Miscellaneous Notes
273
256
 
274
257
  All methods for `PgDice` take a hash which will override whatever values would have been automatically supplied.
@@ -279,6 +262,12 @@ PgDice.list_droppable_partitions('comments', past: 60)
279
262
  ```
280
263
  This example would use `60` instead of the configured value of `90` from the `comments` table we configured above.
281
264
 
265
+ # Examples
266
+
267
+ 1. [Here's an example on how to use PgDice in AWS](examples/aws) and the [README](examples/aws/README.md) which will guide
268
+ you through what is going on.
269
+
270
+ 1. [Here's an example on how to write a config.yml for PgDice](examples/config.yml)
282
271
 
283
272
  # FAQ
284
273
 
@@ -292,7 +281,7 @@ This example would use `60` instead of the configured value of `90` from the `co
292
281
  password = config[Rails.env]["password"]
293
282
 
294
283
  "postgres://#{username}:#{password}@#{host}/#{database}"
295
- end
284
+ end
296
285
  ```
297
286
 
298
287
  1. I'm seeing off-by-one errors for my `assert_tables` calls?
@@ -303,7 +292,7 @@ end
303
292
 
304
293
  1. Full `PG::Connection` support (no more database URLs).
305
294
  1. Non time-range based partitioning. [PgParty](https://github.com/rkrage/pg_party) might be a good option!
306
-
295
+ 1. Hourly partitioning
307
296
 
308
297
  # Development
309
298
 
@@ -312,7 +301,6 @@ You can also run `bin/console` for an interactive prompt that will allow you to
312
301
 
313
302
  To install this gem onto your local machine, run `bundle exec rake install`.
314
303
 
315
-
316
304
  ## Running tests
317
305
 
318
306
  You're going to need to have postgres 10 or greater installed.
@@ -323,7 +311,6 @@ Run the following commands from your terminal. Don't run these on anything but a
323
311
  1. `createdb pgdice_test`
324
312
  1. Now you can run the tests via `guard` or `rake test`
325
313
 
326
-
327
314
  ## Contributing
328
315
 
329
316
  Bug reports and pull requests are welcome on GitHub at
@@ -331,11 +318,20 @@ Bug reports and pull requests are welcome on GitHub at
331
318
  to be a safe, welcoming space for collaboration, and contributors are expected to adhere to
332
319
  the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
333
320
 
334
-
335
321
  # License
336
322
 
337
323
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
338
324
 
325
+ # Disclaimer
326
+
327
+ There are some features in this gem which allow you to drop database tables, due to the dangerous nature of
328
+ dropping database tables, please ensure you have a tested and working backup and restore strategy.
329
+
330
+ THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
331
+ By using this software you agree that the creator, maintainers, and any affiliated parties
332
+ CANNOT BE HELD LIABLE FOR DATA LOSS OR LOSSES OF ANY KIND.
333
+
334
+ See the [LICENSE](LICENSE) for more information.
339
335
 
340
336
  # Code of Conduct
341
337
 
data/SECURITY.md ADDED
@@ -0,0 +1,15 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ The versions here are currently supported for security patches.
6
+
7
+ | Version | Supported |
8
+ | ------- | ------------------ |
9
+ | latest | :white_check_mark: |
10
+ | < 1.0 | :x: |
11
+
12
+
13
+ ## Reporting a Vulnerability
14
+
15
+ Please open a new issue
@@ -0,0 +1,28 @@
1
+ # How can I use PgDice in production?
2
+
3
+ This collection of files is how I use PgDice in production. I'll describe the architecture here so you'll have a place
4
+ to start.
5
+
6
+ 1. `tasks/poll_sqs.rake` is run using some sort of process manager like systemd on the ec2 instance. I like to run
7
+ the poll_sqs stuff on my Sidekiq instances because they are the ones who eventually handle the work anyway.
8
+
9
+ 1. `lib/sqs_poller.rb` is used to handle the looping logic for the rake task. It invokes `lib/sqs_listener.rb` for each
10
+ iteration.
11
+
12
+ 1. `lib/sqs_listener.rb` calls AWS SQS to receive messages and then passes each one into the `lib/sqs_listener/sqs_event_router.rb`
13
+ to be routed to the correct message handler.
14
+
15
+ 1. Inside `lib/sqs_listener/sqs_event_router.rb` the message is parsed and passed through a case statement.
16
+ This could be abstracted better but for now if the message has a field of `event_type` and a value of `"task"` then
17
+ the router will send it off to the `TaskEventHandler` which in this case is
18
+ `lib/sqs_listener/typed_event_handler/task_event_handler.rb`
19
+
20
+ 1. In the `TaskEventHandler` the task is sent to a handler which responds to the task specified in the message body field `task`.
21
+
22
+ 1. The handler for the task (in this case, `DatabaseTasks`) handles the parameters for invoking the Sidekiq worker: `PgDiceWorker`
23
+
24
+ 1. Finally, the `PgDiceWorker` is called and handles invoking `PgDice` based on the parameters passed in.
25
+
26
+
27
+ Hopefully that wasn't too confusing. There's a lot of steps in here because the system that uses PgDice handles lots
28
+ of different types of SQS events and needs to be as resilient as possible.
@@ -0,0 +1,59 @@
1
+ {
2
+ "Description": "Deployment stack",
3
+ "Parameters": {
4
+ "PgDiceEnabled": {
5
+ "Type": "String",
6
+ "Description": "The ENABLED/DISABLED state of the cloudwatch scheduled events for PgDice."
7
+ }
8
+ },
9
+ "Resources": {
10
+ "PgDiceDailyAddPartitions": {
11
+ "DependsOn": "IncomingSQS",
12
+ "Type": "AWS::Events::Rule",
13
+ "Properties": {
14
+ "State":{
15
+ "Ref": "PgDiceEnabled"
16
+ },
17
+ "Description": " PgDice daily add partitions",
18
+ "Name": "PgDiceDailyAddPartitions",
19
+ "ScheduleExpression": "rate(1 day)",
20
+ "Targets": [
21
+ {
22
+ "Arn": {
23
+ "Fn::GetAtt": [
24
+ "IncomingSQS",
25
+ "Arn"
26
+ ]
27
+ },
28
+ "Id": "PgDiceDailyAddPartitionsId",
29
+ "Input": "{\"event_type\":\"task\",\"task\":\"add_new_partitions\"}"
30
+ }
31
+ ]
32
+ }
33
+ },
34
+ "PgDiceDailyDropPartitions": {
35
+ "DependsOn": "IncomingSQS",
36
+ "Type": "AWS::Events::Rule",
37
+ "Properties": {
38
+ "State":{
39
+ "Ref": "PgDiceEnabled"
40
+ },
41
+ "Description": " PgDice daily drop partitions",
42
+ "Name": "PgDiceDailyDropPartitions",
43
+ "ScheduleExpression": "rate(1 day)",
44
+ "Targets": [
45
+ {
46
+ "Arn": {
47
+ "Fn::GetAtt": [
48
+ "IncomingSQS",
49
+ "Arn"
50
+ ]
51
+ },
52
+ "Id": "PgDiceDailyDropPartitionsId",
53
+ "Input": "{\"event_type\":\"task\",\"task\":\"drop_old_partitions\"}"
54
+ }
55
+ ]
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DefaultEventHandler
4
+ attr_reader :logger
5
+
6
+ def initialize(opts = {})
7
+ @logger = opts[:logger] ||= Sidekiq.logger
8
+ @fallthrough_event_handler = opts[:fallthrough_event_handler] ||= FallthroughEventHandler.new(logger: logger)
9
+ end
10
+
11
+ def handle_message(message)
12
+ # Since 'message' is a JSON formatted string, parse the JSON and then get the values under the 'Records' key
13
+ # When JSON parses a string it returns a Ruby Hash (just like a Java HashMap)
14
+ records = JSON.parse(message.body)['Records']
15
+ if records
16
+ process_records(records, message)
17
+ else
18
+ # If the message body doesn't have any entries under the 'Records' key then we don't know what to do.
19
+ @fallthrough_event_handler.call(message)
20
+ end
21
+ rescue StandardError => e
22
+ # If any errors are raised processing this message then call the fallthrough because something went wrong.
23
+ logger.error { "Caught error while handling incoming message. Calling fallthrough_event_handler. Error: #{e}" }
24
+ @fallthrough_event_handler.call(message)
25
+ end
26
+
27
+ private
28
+
29
+ def process_records(records, message)
30
+ # Process default event
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UnknownTaskError < StandardError
4
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FallthroughEventHandler
4
+ attr_reader :logger
5
+
6
+ def initialize(opts = {})
7
+ @logger = opts[:logger] ||= Sidekiq.logger
8
+ @sqs_message_deleter = opts[:sqs_message_deleter] ||= SqsMessageDeleter.new(logger: logger)
9
+ end
10
+
11
+ def call(message)
12
+ logger.warn do
13
+ "Received sqs message we don't know how to process. Message: #{message}"
14
+ end
15
+
16
+ @sqs_message_deleter.call(message.receipt_handle)
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Responsible for routing incoming SQS events to the correct handler
4
+ class SqsEventRouter
5
+ attr_reader :logger
6
+
7
+ def initialize(opts = {})
8
+ @logger = opts[:logger] ||= Sidekiq.logger
9
+ @task_event_handler = opts[:task_event_handler] ||= TaskEventHandler.new(logger: logger)
10
+ @default_event_handler = opts[:default_event_handler] ||= DefaultEventHandler.new(logger: logger)
11
+ @sqs_message_deleter = opts[:sqs_message_deleter] ||= SqsMessageDeleter.new(logger: logger)
12
+ end
13
+
14
+ # Handles incoming sqs event, looking for a field of 'event_type'
15
+ # See scheduled_events.json for details on how to create task events from cloudwatch
16
+ def handle_message(message)
17
+ message_body = JSON.parse(message.body).with_indifferent_access
18
+ event_type = message_body[:event_type]
19
+
20
+ logger.tagged(message.receipt_handle) do
21
+ logger.debug { "The received message was: #{message}" }
22
+
23
+ case event_type
24
+ when 'task'
25
+ @task_event_handler.run_task(message_body)
26
+ @sqs_message_deleter.call(message.receipt_handle)
27
+ else
28
+ @default_event_handler.handle_message(message)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TaskEventHandler
4
+ attr_reader :logger
5
+
6
+ def initialize(opts = {})
7
+ @logger = opts[:logger] ||= Sidekiq.logger
8
+ @task_handlers = [opts[:task_handlers] ||= initialize_default_handlers].flatten.compact
9
+ end
10
+
11
+ def run_task(message_body_hash)
12
+ task = message_body_hash.fetch(:task).to_sym
13
+ logger.debug { "Running task: #{task}. Searching for task in: #{@task_handlers}" }
14
+
15
+ task_handlers = resolve_task_handlers(task)
16
+
17
+ if task_handlers.blank?
18
+ raise UnknownTaskError, "Could not find task: #{task} in any of the available task_handlers: #{@task_handlers}"
19
+ end
20
+
21
+ invoke_task_handler(task_handlers.first, task, message_body_hash.fetch(:parameters, {}))
22
+ end
23
+
24
+ private
25
+
26
+ def resolve_task_handlers(task)
27
+ task_handlers = @task_handlers.select { |task_handler| task_handler.respond_to?(task) }
28
+
29
+ task_handlers.each do |task_handler|
30
+ logger.debug { "Found task handler: #{task_handler.class} that can handle task: #{task}" }
31
+ end
32
+ task_handlers
33
+ end
34
+
35
+ def invoke_task_handler(task_handler, task, params)
36
+ logger.debug { "Invoking handler: #{task_handler.class}##{task} with params: #{params}" }
37
+ task_handler.public_send(task, params)
38
+ end
39
+
40
+ def initialize_default_handlers
41
+ [
42
+ DatabaseTasks.new
43
+ # Other tasks go here
44
+ ]
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tasks that we can use to maintain partitioned tables over time
4
+ # You can override the default params hash by passing it in to the method calls.
5
+ # The default params are defined inside each method.
6
+ #
7
+ # Also, as far as the string keys for hashes go:
8
+ # https://github.com/mperham/sidekiq/wiki/Best-Practices
9
+ # Sidekiq job parameters must be JSON serializable. That means Ruby symbols are
10
+ # lost when they are sent through JSON!
11
+ class DatabaseTasks
12
+ def initialize(opts = {})
13
+ @pgdice = opts[:pgdice] ||= PgDice
14
+ @task_runner = opts[:task_runner] ||= ->(method, params) { PgdiceWorker.perform_async(method, params) }
15
+ end
16
+
17
+ def add_new_partitions(params = {})
18
+ all_params = { 'table_names' => table_names, 'only' => 'future', 'validate' => true }.merge(params)
19
+ @task_runner.call('add_new_partitions', all_params)
20
+ end
21
+
22
+ def drop_old_partitions(params = {})
23
+ all_params = { 'table_names' => table_names, 'only' => 'past', 'validate' => true }.merge(params)
24
+ @task_runner.call('drop_old_partitions', all_params)
25
+ end
26
+
27
+ def assert_tables(params = {})
28
+ all_params = { 'table_names' => table_names, 'validate' => false }.merge(params)
29
+ @task_runner.call('assert_tables', all_params)
30
+ end
31
+
32
+ private
33
+
34
+ def table_names
35
+ @pgdice.approved_tables.map(&:name)
36
+ end
37
+ end