magic_pipe 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +10 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +92 -0
  8. data/LICENSE.txt +19 -0
  9. data/README.md +241 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +16 -0
  12. data/bin/setup +8 -0
  13. data/lib/magic_pipe.rb +45 -0
  14. data/lib/magic_pipe/client.rb +41 -0
  15. data/lib/magic_pipe/codecs.rb +24 -0
  16. data/lib/magic_pipe/codecs/base.rb +26 -0
  17. data/lib/magic_pipe/codecs/json.rb +38 -0
  18. data/lib/magic_pipe/codecs/message_pack.rb +81 -0
  19. data/lib/magic_pipe/codecs/thrift.rb +17 -0
  20. data/lib/magic_pipe/codecs/yaml.rb +18 -0
  21. data/lib/magic_pipe/config.rb +103 -0
  22. data/lib/magic_pipe/envelope.rb +29 -0
  23. data/lib/magic_pipe/errors.rb +14 -0
  24. data/lib/magic_pipe/loaders.rb +22 -0
  25. data/lib/magic_pipe/loaders/simple_active_record.rb +54 -0
  26. data/lib/magic_pipe/metrics.rb +54 -0
  27. data/lib/magic_pipe/senders.rb +23 -0
  28. data/lib/magic_pipe/senders/async.rb +76 -0
  29. data/lib/magic_pipe/senders/base.rb +24 -0
  30. data/lib/magic_pipe/senders/metrics_mixin.rb +20 -0
  31. data/lib/magic_pipe/senders/sync.rb +38 -0
  32. data/lib/magic_pipe/transports.rb +26 -0
  33. data/lib/magic_pipe/transports/base.rb +17 -0
  34. data/lib/magic_pipe/transports/debug.rb +17 -0
  35. data/lib/magic_pipe/transports/https.rb +80 -0
  36. data/lib/magic_pipe/transports/kafka.rb +8 -0
  37. data/lib/magic_pipe/transports/log.rb +15 -0
  38. data/lib/magic_pipe/transports/multi.rb +44 -0
  39. data/lib/magic_pipe/transports/sqs.rb +68 -0
  40. data/lib/magic_pipe/version.rb +3 -0
  41. data/magic_pipe.gemspec +44 -0
  42. metadata +257 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a16af40ae46abcba5bafeca16db82382e91bd4ee
4
+ data.tar.gz: 31eb6c6e23ce061727e467879869a749adf7af08
5
+ SHA512:
6
+ metadata.gz: 4cfebdd5448a995c0a93b0f10b1ac5f950e9ed9c2d94c1eeed99020da205d1943d520d2c24fa91279e48eb779f27cf5f2de7fb5ad469277fb2f083675abc66c2
7
+ data.tar.gz: 82cfe543de0788e2e3cf758450396de4707fa7255b0bc90564465203b3d6f439e23120cc85a38c20ac1c72ca23edee4fa3d738399c3f314f778a71dd406ecff1
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.3
5
+ - 2.3.6
6
+ - 2.2.9
7
+ script:
8
+ - bundle exec rspec
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # MagicPipe Changelog
2
+
3
+ ## v0.1.0
4
+
5
+ Initial release.
6
+
7
+ Codecs: json, message_pack, jaml.
8
+ Loaders: simple_active_record.
9
+ Senders: sync and async (Sidekiq).
10
+ Transports: debug, log, https, sqs, multi.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in magic_pipe.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,92 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ magic_pipe (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.5.2)
10
+ public_suffix (>= 2.0.2, < 4.0)
11
+ aws-partitions (1.56.0)
12
+ aws-sdk-core (3.14.0)
13
+ aws-partitions (~> 1.0)
14
+ aws-sigv4 (~> 1.0)
15
+ jmespath (~> 1.0)
16
+ aws-sdk-sqs (1.3.0)
17
+ aws-sdk-core (~> 3)
18
+ aws-sigv4 (~> 1.0)
19
+ aws-sigv4 (1.0.2)
20
+ coderay (1.1.2)
21
+ concurrent-ruby (1.0.5)
22
+ connection_pool (2.2.1)
23
+ crack (0.4.3)
24
+ safe_yaml (~> 1.0.0)
25
+ diff-lcs (1.3)
26
+ ethon (0.11.0)
27
+ ffi (>= 1.3.0)
28
+ faraday (0.14.0)
29
+ multipart-post (>= 1.2, < 3)
30
+ ffi (1.9.18)
31
+ hashdiff (0.3.7)
32
+ jmespath (1.3.1)
33
+ method_source (0.9.0)
34
+ msgpack (1.2.2)
35
+ multipart-post (2.0.0)
36
+ oj (3.3.10)
37
+ pry (0.11.3)
38
+ coderay (~> 1.1.0)
39
+ method_source (~> 0.9.0)
40
+ public_suffix (3.0.1)
41
+ rack (2.0.3)
42
+ rack-protection (2.0.0)
43
+ rack
44
+ rake (10.5.0)
45
+ redis (4.0.1)
46
+ rspec (3.7.0)
47
+ rspec-core (~> 3.7.0)
48
+ rspec-expectations (~> 3.7.0)
49
+ rspec-mocks (~> 3.7.0)
50
+ rspec-core (3.7.0)
51
+ rspec-support (~> 3.7.0)
52
+ rspec-expectations (3.7.0)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.7.0)
55
+ rspec-mocks (3.7.0)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.7.0)
58
+ rspec-support (3.7.0)
59
+ safe_yaml (1.0.4)
60
+ sidekiq (5.0.5)
61
+ concurrent-ruby (~> 1.0)
62
+ connection_pool (~> 2.2, >= 2.2.0)
63
+ rack-protection (>= 1.5.0)
64
+ redis (>= 3.3.4, < 5)
65
+ timecop (0.9.1)
66
+ typhoeus (1.3.0)
67
+ ethon (>= 0.9.0)
68
+ webmock (3.3.0)
69
+ addressable (>= 2.3.6)
70
+ crack (>= 0.3.2)
71
+ hashdiff
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ aws-sdk-sqs (~> 1.3)
78
+ bundler (~> 1.16)
79
+ faraday
80
+ magic_pipe!
81
+ msgpack
82
+ oj
83
+ pry (~> 0.11)
84
+ rake (~> 10.0)
85
+ rspec (~> 3.0)
86
+ sidekiq
87
+ timecop (~> 0.9)
88
+ typhoeus
89
+ webmock (~> 3.3)
90
+
91
+ BUNDLED WITH
92
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 Tommaso Pavese
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,241 @@
1
+ # MagicPipe
2
+
3
+ [![Build Status](https://travis-ci.org/tompave/magic_pipe.svg?branch=master)](https://travis-ci.org/tompave/magic_pipe) [![Gem Version](https://badge.fury.io/rb/magic_pipe.svg)](https://badge.fury.io/rb/magic_pipe)
4
+
5
+ MagicPipe is a Ruby library to push data to remote destinations on multiple topics.
6
+
7
+ It provides client adapters for several popular message busses, and it's meant to facilitate publishing messages and streaming data, in different formats and to different backends.
8
+
9
+ ## Content
10
+
11
+ * [Design, concepts and internals](#design-concepts-and-internals)
12
+ - [The moving parts](#the-moving-parts)
13
+ + [Codecs](#codecs)
14
+ + [Transports](#transports)
15
+ + [Senders](#senders)
16
+ + [Loaders](#loaders)
17
+ - [Gluing everything together](#gluing-everything-together)
18
+ - [Multiple pipes](#multiple-pipes)
19
+ - [The message payloads](#the-message-payloads)
20
+ * [Usage](#usage)
21
+ - [Configuration](#configuration)
22
+ * [Dependencies](#dependencies)
23
+ * [Use cases](#use-cases)
24
+ * [Installation](#installation)
25
+
26
+ ## Design, concepts and internals
27
+
28
+ Its design principles are:
29
+
30
+ * It should be plug and play, with minimal configuration -- it's an opinionated library.
31
+ * It should support different message formats and backends with a consistent interface.
32
+ * It should allow for multiple backends to be targeted at the same time.
33
+ * It should be extendable and customizable.
34
+
35
+ To achieve these goals, MagicPipe adopts a modular design with interchangeable parts.
36
+
37
+ ### The moving parts
38
+
39
+ The four main units of work are codecs, transports, senders and loaders. MagicPipe provides a set of classes out of the box, but users of the library can configure their own custom classes that implement the correct interface.
40
+
41
+ #### Codecs
42
+
43
+ Codecs accept a Ruby object and produce an encoded output. The input must respond to `as_json` and return a Hash. The provided codecs are:
44
+
45
+ * Yaml
46
+ * JSON
47
+ * MessagePack
48
+ * Thrift (work in progress)
49
+
50
+ #### Transports
51
+
52
+ Transports are adapters for the different backends, and take care of establishing connections and submitting the payloads. The provided transports are:
53
+
54
+ * Debug
55
+ * Log
56
+ * HTTPS
57
+ * SQS
58
+ * Kafka (work in progress)
59
+ * DynamoDB (work in progress)
60
+ * Multi (allows to target different transports at the same time)
61
+
62
+ #### Senders
63
+
64
+ Senders glue things together and implement a processing strategy. The provided senders are:
65
+
66
+ * Sync
67
+ * Async (Sidekiq)
68
+
69
+ #### Loaders
70
+
71
+ Loaders are used with the Async sender to serialize Ruby objects into something that can be passed to Sidekiq, and then to rebuild the original Objects inside the Sidekiq workers. The provided loaders are:
72
+
73
+ * SimpleActiveRecord: it takes `ActiveRecord::Base` instances and an optional wrapper (e.g. an `ActiveModel::Serializer`) and turns them into class references (Strings) and a record ID. Then, when the Sidekiq jobs execute, it loads the record from the DB and wraps it in the serializer, if present.
74
+
75
+ ### Gluing everything together
76
+
77
+ A MagicPipe client encapsulates the configured parts. For example, clients with a single and multiple transports can be represented like this:
78
+
79
+ ```
80
+ Client
81
+ ├ Configuration
82
+ └ Sender
83
+ ├ Codec
84
+ ├ (Loader)
85
+ └ Transport
86
+
87
+ Client
88
+ ├ Configuration
89
+ └ Sender
90
+ ├ Codec
91
+ ├ (Loader)
92
+ └ Transports
93
+ ├ Transport A
94
+ ├ Transport B
95
+ └ Transport C
96
+ ```
97
+
98
+ A client can only have a single codec and sender, but can have multiple transports to submit data to multiple backends. This is particularly efficient because it allows to reduce the number of DB queries to re-load the data, when using the async sender.
99
+
100
+ ### Multiple pipes
101
+
102
+ Multiple clients with different configurations can be used together in the same process.
103
+
104
+ The main use case is to support different codecs (message formats). Some applications may in fact need to emit messages with different formats to different backends, for example JSON to both SQS and a remote HTTPS endpoint, and Thrift to Kafka.
105
+
106
+ Another use case is to use different Sidekiq queues (and worker pools) for different topics, which can be accomplished by using different MagicPipe clients for different types of objects.
107
+
108
+ ### The message payloads
109
+
110
+ MagicPipe wraps the payloads in message envelopes with extra metadata. These extra attributes are:
111
+
112
+ * the message topic (string)
113
+ * the producer name (string)
114
+ * the submission timestamp, captured when `client#send_data` is invoked (integer)
115
+ * the payload mime type, e.g. `application/json` (string)
116
+
117
+ Some transports will additionally provide this metadata as message meta attributes. For example, the HTTPS transport will set them as custom HTTP request headers, and the SQS transport will set them as the SQS message custom attributes.
118
+
119
+ ## Usage
120
+
121
+ Create and configure a MagicPipe client:
122
+ (Temporary API! Still a work in progress)
123
+
124
+ ```ruby
125
+ require "magic_pipe/senders/async"
126
+ require "magic_pipe/transports/https"
127
+ require "magic_pipe/transports/sqs"
128
+
129
+ $magic_pipe = MagicPipe.build do |mp|
130
+ mp.sender = :async
131
+ mp.loader = :simple_active_record
132
+
133
+ mp.codec = :json
134
+ mp.transports = [:https, :sqs]
135
+
136
+ mp.sidekiq_options = {
137
+ queue: "magic_pipe"
138
+ }
139
+ mp.https_transport_options = {
140
+ url: "https://my.receiver.service/foo",
141
+ auth_token: "bar",
142
+ }
143
+ mp.sqs_transport_options = {
144
+ queue: "my_data_stream"
145
+ }
146
+
147
+ mp.logger = Rails.logger
148
+ mp.metrics_client = $statsd_client
149
+ end
150
+ ```
151
+
152
+ Then, to submit a message:
153
+
154
+ ```ruby
155
+ $magic_pipe.send_data(
156
+ object: object,
157
+ topic: "my_topic",
158
+ wrapper: nil, # default
159
+ time: Time.now.utc # default
160
+ )
161
+ ```
162
+
163
+ A more concrete example, with an active record object:
164
+
165
+ ```ruby
166
+ class Article < ActiveRecord::Base
167
+ after_commit :send_on_magic_pipe
168
+
169
+ private
170
+
171
+ def send_on_magic_pipe
172
+ $magic_pipe.send_data(
173
+ object: self,
174
+ topic: "articles",
175
+ wrapper: Serializers::InventoryArticleSerializer,
176
+ time: updated_at
177
+ )
178
+ end
179
+ end
180
+ ```
181
+
182
+ ### Configuration
183
+
184
+ The [`MagicPipe::Config`](https://github.com/tompave/magic_pipe/blob/master/lib/magic_pipe/config.rb) class lists all supported configuration options and their default values.
185
+
186
+ Users of the library are strongly encouraged to set explicit values for these settings:
187
+
188
+ * `client_name`: used internally to identify a specific magic pipe client instance, for example when handing over the message publication to Sidekiq.
189
+ * `producer_name`: the name of your application, as it should appear in the metadata of the published messages.
190
+ * `logger`: when used in Rails, this should be set to `Rails.logger`.
191
+ * `metrics_client`: to collect operational metrics. Wrapped by [`MagicPipe::Metrics`](https://github.com/tompave/magic_pipe/blob/master/lib/magic_pipe/metrics.rb). It's mainly designed to work with [DataDog's StatsD](https://rubygems.org/gems/dogstatsd-ruby) out of the box, but any stats collector library would work, if you provide a thin adapter wrapper. If not configured, the default implementation will simply send the metrics to the logger.
192
+
193
+ More specific configuration is then required for the different modules of the library, when used.
194
+
195
+ #### Transport: SQS
196
+
197
+ This transport requires credentials for the AWS API. The credentials need to be associated to an IAM user with full access to SQS, and need to be present in the system env:
198
+
199
+ ```
200
+ export AWS_ACCESS_KEY_ID='foo'
201
+ export AWS_SECRET_ACCESS_KEY='bar'
202
+ export AWS_REGION='us-east-1'
203
+ ```
204
+
205
+ ## Dependencies
206
+
207
+ Becasuse of MagicPipe's modular design, and in order to keep a small installation footprint, all of its dependencies are optional. Users of the library need to manually install the required dependencies in their projects.
208
+
209
+ The Ruby gems MagicPipe's modules depend on are:
210
+
211
+ * Senders:
212
+ - Async: `sidekiq`
213
+ * Transports:
214
+ - SQS: `aws-sdk-sqs`
215
+ - HTTPS: `faraday`, `typhoeus`
216
+ * Codecs:
217
+ - JSON: `oj` (optional, will fallback to `json` from the stdlib if `oj` is missing)
218
+ - MessagePack: `msgpack`
219
+
220
+ ## Use cases
221
+
222
+ TODO
223
+
224
+ * event driven architectures
225
+ * streaming domain data to replicate it somewhere else
226
+
227
+ ## Installation
228
+
229
+ Add this line to your application's Gemfile:
230
+
231
+ ```ruby
232
+ gem 'magic_pipe'
233
+ ```
234
+
235
+ And then execute:
236
+
237
+ $ bundle
238
+
239
+ Or install it yourself as:
240
+
241
+ $ gem install magic_pipe
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,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENV['AWS_ACCESS_KEY_ID'] = ""
4
+ ENV['AWS_SECRET_ACCESS_KEY'] = ""
5
+ ENV['AWS_SDK_CONFIG_OPT_OUT'] = "true"
6
+ ENV['AWS_REGION'] = "eu-west-1"
7
+
8
+ require "bundler/setup"
9
+ require "magic_pipe"
10
+
11
+ # You can add fixtures and/or initialization code here to make experimenting
12
+ # with your gem easier. You can also use a different console, if you like.
13
+
14
+ # (If you use this, don't forget to add pry to your Gemfile!)
15
+ require "pry"
16
+ Pry.start
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