phobos_db_checkpoint 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7eed2349cc4d3b82e46efd24c3689b4ea7b6d168
4
+ data.tar.gz: ea0c58b7e369b9533f10e14bd6869ebdbcd3fad7
5
+ SHA512:
6
+ metadata.gz: 8d16920bea6ffd2378dbb1041ad55ef47b6224e071b684e6cd636b35ed3235bf5c3415a6d9c5d30349e4b2c2a39377eacba4778059e52734c2dde08b9300d4a9
7
+ data.tar.gz: 841e2434154e57bd8b8c4fd1bc19a7640863d2233fbe378267fd25f88e08773b42639f1981dda459370786b962b2e2256ccc9d447af72c48159e9aa873c9be9b
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ spec/tmp/
11
+ .DS_Store
12
+ spec/examples.txt
13
+ spec/setup/
14
+ spec/tmp/
15
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in phobos_db_checkpoint.gemspec
4
+
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
data/README.md ADDED
@@ -0,0 +1,156 @@
1
+ ![CircleCI](https://circleci.com/gh/klarna/phobos_db_checkpoint/tree/master.svg?style=shield&circle-token=a69fda09f130a862b69f6a7e8be834f884829ccd)
2
+
3
+ # Phobos DB Checkpoint
4
+
5
+ Phobos DB Checkpoint is a plugin to [Phobos](https://github.com/klarna/phobos) and is meant as a drop in replacement to `Phobos::Handler`, extending it with the following features:
6
+ * Persists your Kafka events to an active record compatible database
7
+ * Ensures that your [handler](https://github.com/klarna/phobos#usage-consuming-messages-from-kafka) will consume messages only once
8
+ * Allows your system to quickly reprocess events in case of failures
9
+
10
+ ## Table of Contents
11
+
12
+ 1. [Installation](#installation)
13
+ 1. [Usage](#usage)
14
+ 1. [Setup](#setup)
15
+ 1. [Handler](#handler)
16
+ 1. [Accessing the events](#accessing-the-events)
17
+ 1. [Instrumentation](#instrumentation)
18
+ 1. [Development](#development)
19
+
20
+ ## <a name="installation"></a> Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'phobos_db_checkpoint'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install phobos_db_checkpoint
35
+
36
+ ## <a name="usage"></a> Usage
37
+
38
+ The main idea of Phobos DB Checkpoint is to replace the default handler `Phobos::Handler` with `PhobosDBCheckpoint::Handler`.
39
+
40
+ In order to use it, you have to [setup the database](#setup) and [switch your handler](#handler) to use the `PhobosDBCheckpoint::Handler` instead.
41
+
42
+ ### <a name="setup"></a> Setup
43
+
44
+ Phobos DB Checkpoint exposes a CLI to help you setup your project, assuming an [already configured phobos project](https://github.com/klarna/phobos#usage-standalone-apps). Example:
45
+
46
+ ```sh
47
+ # run this command inside your app directory
48
+ $ phobos_db_checkpoint init
49
+ create Rakefile
50
+ prepend Rakefile
51
+ create config/database.yml
52
+ create db/migrate/20160825183351479299_phobos_01_create_events.rb
53
+ append phobos_boot.rb
54
+ ```
55
+
56
+ The __init__ command will generate the base migrations, an example of `database.yml`, and add the default configuration into `phobos_boot.rb` and `Rakefile` for your convenience.
57
+
58
+ After this, your app should have access to all db tasks.
59
+
60
+ ```sh
61
+ $ rake -T
62
+ rake db:create # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all ...
63
+ rake db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databa...
64
+ rake db:environment:set # Set the environment value for the database
65
+ rake db:fixtures:load # Loads fixtures into the current environment's database
66
+ rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
67
+ rake db:migrate:status # Display status of migrations
68
+ rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
69
+ rake db:schema:cache:clear # Clears a db/schema_cache.dump file
70
+ rake db:schema:cache:dump # Creates a db/schema_cache.dump file
71
+ rake db:schema:dump # Creates a db/schema.rb file that is portable against any DB supported by Active Record
72
+ rake db:schema:load # Loads a schema.rb file into the database
73
+ rake db:seed # Loads the seed data from db/seeds.rb
74
+ rake db:setup # Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)
75
+ rake db:structure:dump # Dumps the database structure to db/structure.sql
76
+ rake db:structure:load # Recreates the databases from the structure.sql file
77
+ rake db:version # Retrieves the current schema version number
78
+ ```
79
+
80
+ ##### Note
81
+
82
+ You can always re-generate the base migrations using the command __copy-migrations__, example:
83
+
84
+ ```sh
85
+ $ phobos_db_checkpoint copy-migrations
86
+ exists phobos_01_create_events.rb
87
+ ```
88
+
89
+ This command has no side effects, if the migration is already present it will ignore it.
90
+
91
+ ### <a name="handler"></a> Handler
92
+
93
+ In order to use the database checkpointing, your handler should be changed to include `PhobosDBCheckpoint::Handler` instead of `Phobos::Handler`. Phobos DB Checkpoint handler uses the Phobos `around_consume` functionality, which means you need to implement a `#consume` method to handle the event.
94
+
95
+ Since Phobos DB Checkpoint will only save acknowledged events, you need to return from `#consume` with an invocation to `#ack` with the __entity_id__ and __event_time__ of your event. Example:
96
+
97
+ ```ruby
98
+ class MyHandler
99
+ include PhobosDBCheckpoint::Handler
100
+
101
+ def consume(payload, metadata)
102
+ my_event = JSON.parse(payload)
103
+ # <-- your logic (which possibly skips messages) here
104
+ ack(my_event['id'], Time.now)
105
+ end
106
+ end
107
+ ```
108
+
109
+ If your handler returns anything different than an __ack__ it won't be saved to the database.
110
+
111
+ Note that the `PhobosDBCheckpoint::Handler` will automatically skip already handled events (i.e. duplicate Kafka messages).
112
+
113
+ ### <a name="accessing-the-events">Accessing the events</a>
114
+
115
+ `PhobosDBCheckpoint::Event` is a plain `ActiveRecord::Base` model, feel free to play with it.
116
+
117
+ ### <a name="instrumentation"></a> Instrumentation
118
+
119
+ Some operations are instrumented using [Phobos::Instrumentation](https://github.com/klarna/phobos#usage-instrumentation)
120
+
121
+ #### Handler notifications
122
+
123
+ Overview of the built in notifications:
124
+ * `db_checkpoint.event_acknowledged` is sent when the event is acknowledged (saved)
125
+ * `db_checkpoint.event_skipped` is sent when the event is skipped (not saved)
126
+ * `db_checkpoint.event_already_consumed` is sent when the handler receives an existing message (not saved)
127
+
128
+ The following payload is included for all notifications:
129
+ * listener_id
130
+ * group_id
131
+ * topic
132
+ * key
133
+ * partition
134
+ * offset
135
+ * retry_count
136
+ * checksum
137
+
138
+ ## <a name="development"></a> Development
139
+
140
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec spec` to run the tests.
141
+
142
+ To install this gem in your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
143
+
144
+ ## Contributing
145
+
146
+ Bug reports and pull requests are welcome on GitHub at https://github.com/klarna/phobos_db_checkpoint.
147
+
148
+ ## License
149
+
150
+ Copyright 2016 Klarna
151
+
152
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
153
+
154
+ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
155
+
156
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'phobos_db_checkpoint'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'phobos_db_checkpoint'
6
+ require 'phobos_db_checkpoint/cli'
7
+
8
+ STDOUT.sync = true
9
+ PhobosDBCheckpoint::CLI::Commands.start(ARGV)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/circle.yml ADDED
@@ -0,0 +1,29 @@
1
+ machine:
2
+ pre:
3
+ - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
4
+ services:
5
+ - docker
6
+ environment:
7
+ LOG_LEVEL: DEBUG
8
+ CI: true
9
+ ruby:
10
+ version: 2.3.1
11
+
12
+ # Ignores circle ci default database setup
13
+ database:
14
+ override:
15
+ - echo "overrides circle CI commands"
16
+
17
+ dependencies:
18
+ pre:
19
+ - docker -v
20
+ - docker pull postgres:9.5.4
21
+ - gem install bundler -v 1.9.5
22
+ - bundle install
23
+
24
+ test:
25
+ override:
26
+ - docker run -d -p 5432:5432 --name db postgres; sleep 5
27
+ - bundle exec rspec -r rspec_junit_formatter --format RspecJunitFormatter -o $CIRCLE_TEST_REPORTS/rspec/unit.xml
28
+ post:
29
+ - cp log/*.log $CIRCLE_ARTIFACTS/ || true
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+ require 'digest'
3
+ require 'active_record'
4
+ require 'phobos'
5
+
6
+ require 'phobos_db_checkpoint/version'
7
+ require 'phobos_db_checkpoint/event_actions'
8
+ require 'phobos_db_checkpoint/event'
9
+ require 'phobos_db_checkpoint/handler'
10
+
11
+ module PhobosDBCheckpoint
12
+ DEFAULT_DB_DIR = 'db'.freeze
13
+ DEFAULT_MIGRATION_PATH = File.join(DEFAULT_DB_DIR, 'migrate').freeze
14
+ DEFAULT_DB_CONFIG_PATH = 'config/database.yml'.freeze
15
+
16
+ class << self
17
+ attr_reader :db_config
18
+ attr_accessor :db_config_path, :db_dir, :migration_path
19
+
20
+ def configure
21
+ load_db_config
22
+ at_exit { PhobosDBCheckpoint.close_db_connection }
23
+ ActiveRecord::Base.establish_connection(db_config)
24
+ end
25
+
26
+ def env
27
+ ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] ||= 'development'
28
+ end
29
+
30
+ def load_db_config
31
+ @db_config_path ||= DEFAULT_DB_CONFIG_PATH
32
+ configs = YAML.load_file(File.expand_path(@db_config_path))
33
+ @db_config = configs[env]
34
+ end
35
+
36
+ def close_db_connection
37
+ connection = ActiveRecord::Base.connection
38
+ connection.disconnect! if connection
39
+ rescue ActiveRecord::ConnectionNotEstablished
40
+ end
41
+
42
+ def load_tasks
43
+ @db_dir ||= DEFAULT_DB_DIR
44
+ @migration_path ||= DEFAULT_MIGRATION_PATH
45
+ ActiveRecord::Tasks::DatabaseTasks.send(:define_method, :db_dir) { PhobosDBCheckpoint.db_dir }
46
+ ActiveRecord::Tasks::DatabaseTasks.send(:define_method, :migrations_paths) { [PhobosDBCheckpoint.migration_path] }
47
+ ActiveRecord::Tasks::DatabaseTasks.send(:define_method, :env) { PhobosDBCheckpoint.env }
48
+ require 'phobos_db_checkpoint/tasks'
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,95 @@
1
+ require 'thor'
2
+ require 'fileutils'
3
+
4
+ module PhobosDBCheckpoint
5
+ module CLI
6
+
7
+ class Commands < Thor
8
+ include Thor::Actions
9
+
10
+ map '-v' => :version
11
+ map '--version' => :version
12
+
13
+ desc 'version', 'Outputs the version number. Can be used with: phobos_db_checkpoint -v or phobos_db_checkpoint --version'
14
+ def version
15
+ puts PhobosDBCheckpoint::VERSION
16
+ end
17
+
18
+ desc 'init', 'Initialize your project with PhobosDBCheckpoint'
19
+ def init
20
+ create_file('Rakefile') unless File.exist?(File.join(destination_root, 'Rakefile'))
21
+ prepend_to_file 'Rakefile', "require 'phobos_db_checkpoint'\nPhobosDBCheckpoint.load_tasks\n"
22
+ copy_file 'templates/database.yml.example', 'config/database.yml'
23
+
24
+ cmd = self.class.new
25
+ cmd.destination_root = destination_root
26
+ cmd.invoke(:copy_migrations)
27
+
28
+ create_file('phobos_boot.rb') unless File.exist?(File.join(destination_root, 'phobos_boot.rb'))
29
+ append_to_file 'phobos_boot.rb', File.read(phobos_boot_template)
30
+ end
31
+
32
+ desc 'copy-migrations', 'Copy required migrations to the project'
33
+ option :destination,
34
+ aliases: ['-d'],
35
+ default: 'db/migrate',
36
+ banner: 'Destination folder relative to your project'
37
+ def copy_migrations
38
+ destination_fullpath = File.join(destination_root, options[:destination])
39
+ generated_migrations = list_migrations(destination_fullpath)
40
+ FileUtils.mkdir_p(destination_fullpath)
41
+
42
+ template_migrations_metadata.each do |metadata|
43
+
44
+ if migration_exists?(generated_migrations, metadata[:name])
45
+ say_status('exists', metadata[:name])
46
+
47
+ else
48
+ file_path = File.join(options[:destination], "#{metadata[:number]}_#{metadata[:name]}")
49
+ template_path = File.join('templates/migrate', metadata[:path])
50
+ template(template_path, file_path)
51
+ end
52
+
53
+ end
54
+ end
55
+
56
+ def self.source_root
57
+ File.expand_path(File.join(File.dirname(__FILE__), '../..'))
58
+ end
59
+
60
+ private
61
+ def migration_exists?(list, name)
62
+ list.find { |filename| filename =~ /#{name}/ }
63
+ end
64
+
65
+ def template_migrations_metadata
66
+ @template_migrations_metadata ||= begin
67
+ index = 0
68
+ template_migrations.map do |path|
69
+ number = [Time.now.utc.strftime('%Y%m%d%H%M%S%6N'), '%.21d' % index].max
70
+ name = path.split('/').last
71
+ index += 1
72
+ {path: path, name: path.gsub(/\.erb$/, ''), number: number}
73
+ end
74
+ end
75
+ end
76
+
77
+ def template_migrations
78
+ @template_migrations ||= list_migrations(migrations_template_dir)
79
+ end
80
+
81
+ def list_migrations(dir)
82
+ return [] unless Dir.exist?(dir)
83
+ Dir.entries(dir).select {|f| f =~ /\.rb(\.erb)?$/}
84
+ end
85
+
86
+ def migrations_template_dir
87
+ File.join(self.class.source_root, 'templates/migrate')
88
+ end
89
+
90
+ def phobos_boot_template
91
+ File.join(self.class.source_root, 'templates/phobos_boot.rb')
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,23 @@
1
+ module PhobosDBCheckpoint
2
+ class Event < ActiveRecord::Base
3
+ after_initialize :assign_checksum
4
+
5
+ def exists?
6
+ Event.where(topic: topic, group_id: group_id, checksum: checksum).exists?
7
+ end
8
+
9
+ def acknowledge!(ack)
10
+ self.entity_id = ack.entity_id
11
+ self.event_time = ack.event_time
12
+ self.event_type = ack.event_type
13
+ self.event_version = ack.event_version
14
+ save!
15
+ end
16
+
17
+ private
18
+
19
+ def assign_checksum
20
+ self.checksum ||= Digest::MD5.hexdigest(payload.to_json) if payload
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module PhobosDBCheckpoint
2
+ Ack = Struct.new(:entity_id, :event_time, :event_type, :event_version)
3
+ end
@@ -0,0 +1,41 @@
1
+ module PhobosDBCheckpoint
2
+ module Handler
3
+ include Phobos::Handler
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def ack(entity_id, event_time, event_type = nil, event_version = nil)
10
+ PhobosDBCheckpoint::Ack.new(entity_id, event_time, event_type, event_version)
11
+ end
12
+
13
+ module ClassMethods
14
+ include Phobos::Instrumentation
15
+
16
+ def around_consume(payload, metadata)
17
+ event = PhobosDBCheckpoint::Event.new(
18
+ topic: metadata[:topic],
19
+ group_id: metadata[:group_id],
20
+ payload: payload
21
+ )
22
+
23
+ event_metadata = {checksum: event.checksum}.merge(metadata)
24
+ if event.exists?
25
+ instrument('db_checkpoint.event_already_consumed', event_metadata)
26
+ return
27
+ end
28
+
29
+ event_action = yield
30
+ case event_action
31
+ when PhobosDBCheckpoint::Ack
32
+ instrument('db_checkpoint.event_acknowledged', event_metadata) do
33
+ event.acknowledge!(event_action)
34
+ end
35
+ else
36
+ instrument('db_checkpoint.event_skipped', event_metadata)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+
3
+ module PhobosDBCheckpoint
4
+ module Tasks
5
+ extend Rake::DSL if defined? Rake::DSL
6
+
7
+ namespace :db do
8
+ task :environment do
9
+ PhobosDBCheckpoint.configure
10
+ end
11
+
12
+ task :load_config do
13
+ PhobosDBCheckpoint.load_db_config
14
+ task_db_config = Hash[PhobosDBCheckpoint.env, PhobosDBCheckpoint.db_config]
15
+ ActiveRecord::Tasks::DatabaseTasks.database_configuration = task_db_config
16
+ end
17
+ end
18
+
19
+ load 'active_record/railties/databases.rake'
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module PhobosDBCheckpoint
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,57 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'phobos_db_checkpoint/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'phobos_db_checkpoint'
8
+ spec.version = PhobosDBCheckpoint::VERSION
9
+ spec.authors = [
10
+ 'Túlio Ornelas',
11
+ 'Mathias Klippinge',
12
+ 'Sergey Evstifeev',
13
+ 'Thiago R. Colucci',
14
+ 'Martin Svalin',
15
+ 'Francisco Juan'
16
+ ]
17
+ spec.email = [
18
+ 'ornelas.tulio@gmail.com',
19
+ 'mathias.klippinge@gmail.com',
20
+ 'sergey.evstifeev@gmail.com',
21
+ 'ticolucci@gmail.com',
22
+ 'martin@lite.nu',
23
+ 'francisco.juan@gmail.com'
24
+ ]
25
+
26
+ spec.summary = %q{Phobos DB Checkpoint is a plugin to Phobos and is meant as a drop in replacement to Phobos::Handler}
27
+ spec.description = %q{Phobos DB Checkpoint is a plugin to Phobos and is meant as a drop in replacement to Phobos::Handler}
28
+ spec.homepage = "https://github.com/klarna/phobos_db_checkpoint"
29
+ spec.license = 'Apache License Version 2.0'
30
+
31
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
32
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
33
+ if spec.respond_to?(:metadata)
34
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
35
+ else
36
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
37
+ end
38
+
39
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
40
+ spec.bindir = 'bin'
41
+ spec.executables = spec.files.grep(%r{^bin/phobos_db_checkpoint}) { |f| File.basename(f) }
42
+ spec.require_paths = ['lib']
43
+ spec.required_ruby_version = '>= 2.3'
44
+
45
+ spec.add_development_dependency 'bundler', '~> 1.12'
46
+ spec.add_development_dependency 'rake', '~> 10.0'
47
+ spec.add_development_dependency 'rspec', '~> 3.0'
48
+ spec.add_development_dependency 'pry-byebug'
49
+ spec.add_development_dependency 'pg'
50
+ spec.add_development_dependency 'database_cleaner'
51
+ spec.add_development_dependency 'rspec_junit_formatter', '0.2.2'
52
+
53
+ spec.add_dependency 'thor'
54
+ spec.add_dependency 'rake'
55
+ spec.add_dependency 'activerecord', '>= 4.0.0'
56
+ spec.add_dependency 'phobos', '>= 1.0.0'
57
+ end
@@ -0,0 +1,11 @@
1
+ development: &default
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: phobos-db-checkpoint
5
+ user: postgres
6
+ host: localhost
7
+ port: 5432
8
+
9
+ test: &test
10
+ <<: *default
11
+ database: phobos-db-checkpoint-test
@@ -0,0 +1,20 @@
1
+ class Phobos01CreateEvents < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def up
3
+ create_table :events do |t|
4
+ t.string :topic, index: true
5
+ t.string :group_id, index: true
6
+ t.string :entity_id, index: true
7
+ t.timestamp :event_time, index: true
8
+ t.string :event_type, index: true
9
+ t.string :event_version, index: true
10
+ t.string :checksum, index: true
11
+ t.json :payload
12
+ end
13
+
14
+ add_index :events, [:topic, :group_id, :checksum]
15
+ end
16
+
17
+ def down
18
+ drop_table :events
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ require 'phobos_db_checkpoint'
3
+ PhobosDBCheckpoint.configure
4
+ PhobosDBCheckpoint.load_tasks
5
+ Rake.application['db:create'].invoke
6
+ Rake.application['db:migrate'].invoke
metadata ADDED
@@ -0,0 +1,233 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: phobos_db_checkpoint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Túlio Ornelas
8
+ - Mathias Klippinge
9
+ - Sergey Evstifeev
10
+ - Thiago R. Colucci
11
+ - Martin Svalin
12
+ - Francisco Juan
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2016-08-30 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: bundler
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - "~>"
23
+ - !ruby/object:Gem::Version
24
+ version: '1.12'
25
+ type: :development
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - "~>"
30
+ - !ruby/object:Gem::Version
31
+ version: '1.12'
32
+ - !ruby/object:Gem::Dependency
33
+ name: rake
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '10.0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.0'
60
+ - !ruby/object:Gem::Dependency
61
+ name: pry-byebug
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: pg
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ - !ruby/object:Gem::Dependency
89
+ name: database_cleaner
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ - !ruby/object:Gem::Dependency
103
+ name: rspec_junit_formatter
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '='
107
+ - !ruby/object:Gem::Version
108
+ version: 0.2.2
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '='
114
+ - !ruby/object:Gem::Version
115
+ version: 0.2.2
116
+ - !ruby/object:Gem::Dependency
117
+ name: thor
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ - !ruby/object:Gem::Dependency
131
+ name: rake
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ type: :runtime
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ - !ruby/object:Gem::Dependency
145
+ name: activerecord
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: 4.0.0
151
+ type: :runtime
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: 4.0.0
158
+ - !ruby/object:Gem::Dependency
159
+ name: phobos
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: 1.0.0
165
+ type: :runtime
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 1.0.0
172
+ description: Phobos DB Checkpoint is a plugin to Phobos and is meant as a drop in
173
+ replacement to Phobos::Handler
174
+ email:
175
+ - ornelas.tulio@gmail.com
176
+ - mathias.klippinge@gmail.com
177
+ - sergey.evstifeev@gmail.com
178
+ - ticolucci@gmail.com
179
+ - martin@lite.nu
180
+ - francisco.juan@gmail.com
181
+ executables:
182
+ - phobos_db_checkpoint
183
+ extensions: []
184
+ extra_rdoc_files: []
185
+ files:
186
+ - ".gitignore"
187
+ - ".rspec"
188
+ - Gemfile
189
+ - LICENSE.txt
190
+ - README.md
191
+ - Rakefile
192
+ - bin/console
193
+ - bin/phobos_db_checkpoint
194
+ - bin/setup
195
+ - circle.yml
196
+ - lib/phobos_db_checkpoint.rb
197
+ - lib/phobos_db_checkpoint/cli.rb
198
+ - lib/phobos_db_checkpoint/event.rb
199
+ - lib/phobos_db_checkpoint/event_actions.rb
200
+ - lib/phobos_db_checkpoint/handler.rb
201
+ - lib/phobos_db_checkpoint/tasks.rb
202
+ - lib/phobos_db_checkpoint/version.rb
203
+ - phobos_db_checkpoint.gemspec
204
+ - templates/database.yml.example
205
+ - templates/migrate/phobos_01_create_events.rb.erb
206
+ - templates/phobos_boot.rb
207
+ homepage: https://github.com/klarna/phobos_db_checkpoint
208
+ licenses:
209
+ - Apache License Version 2.0
210
+ metadata:
211
+ allowed_push_host: https://rubygems.org
212
+ post_install_message:
213
+ rdoc_options: []
214
+ require_paths:
215
+ - lib
216
+ required_ruby_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - ">="
219
+ - !ruby/object:Gem::Version
220
+ version: '2.3'
221
+ required_rubygems_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - ">="
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ requirements: []
227
+ rubyforge_project:
228
+ rubygems_version: 2.5.1
229
+ signing_key:
230
+ specification_version: 4
231
+ summary: Phobos DB Checkpoint is a plugin to Phobos and is meant as a drop in replacement
232
+ to Phobos::Handler
233
+ test_files: []