deimos-ruby 1.4.0.pre.beta7 → 1.5.0.pre.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile.lock +140 -58
  5. data/README.md +38 -11
  6. data/Rakefile +2 -2
  7. data/deimos-ruby.gemspec +3 -2
  8. data/docs/CONFIGURATION.md +1 -0
  9. data/docs/DATABASE_BACKEND.md +1 -1
  10. data/lib/deimos/active_record_consumer.rb +11 -12
  11. data/lib/deimos/active_record_producer.rb +2 -2
  12. data/lib/deimos/backends/base.rb +32 -0
  13. data/lib/deimos/backends/db.rb +6 -1
  14. data/lib/deimos/backends/kafka.rb +1 -1
  15. data/lib/deimos/backends/kafka_async.rb +1 -1
  16. data/lib/deimos/backends/test.rb +20 -0
  17. data/lib/deimos/base_consumer.rb +7 -7
  18. data/lib/deimos/batch_consumer.rb +0 -1
  19. data/lib/deimos/config/configuration.rb +4 -0
  20. data/lib/deimos/consumer.rb +0 -2
  21. data/lib/deimos/kafka_source.rb +1 -1
  22. data/lib/deimos/kafka_topic_info.rb +1 -1
  23. data/lib/deimos/message.rb +7 -7
  24. data/lib/deimos/producer.rb +10 -12
  25. data/lib/deimos/schema_backends/avro_base.rb +108 -0
  26. data/lib/deimos/schema_backends/avro_local.rb +30 -0
  27. data/lib/deimos/{schema_coercer.rb → schema_backends/avro_schema_coercer.rb} +39 -51
  28. data/lib/deimos/schema_backends/avro_schema_registry.rb +34 -0
  29. data/lib/deimos/schema_backends/avro_validation.rb +21 -0
  30. data/lib/deimos/schema_backends/base.rb +130 -0
  31. data/lib/deimos/schema_backends/mock.rb +42 -0
  32. data/lib/deimos/test_helpers.rb +42 -168
  33. data/lib/deimos/utils/db_producer.rb +5 -0
  34. data/lib/deimos/version.rb +1 -1
  35. data/lib/deimos.rb +22 -6
  36. data/lib/tasks/deimos.rake +1 -1
  37. data/spec/active_record_consumer_spec.rb +7 -0
  38. data/spec/{publish_backend_spec.rb → backends/base_spec.rb} +1 -1
  39. data/spec/backends/db_spec.rb +5 -0
  40. data/spec/batch_consumer_spec.rb +0 -8
  41. data/spec/config/configuration_spec.rb +20 -20
  42. data/spec/consumer_spec.rb +0 -1
  43. data/spec/deimos_spec.rb +0 -4
  44. data/spec/kafka_source_spec.rb +8 -0
  45. data/spec/producer_spec.rb +23 -37
  46. data/spec/rake_spec.rb +19 -0
  47. data/spec/schema_backends/avro_base_shared.rb +174 -0
  48. data/spec/schema_backends/avro_local_spec.rb +32 -0
  49. data/spec/schema_backends/avro_schema_registry_spec.rb +32 -0
  50. data/spec/schema_backends/avro_validation_spec.rb +24 -0
  51. data/spec/schema_backends/base_spec.rb +29 -0
  52. data/spec/spec_helper.rb +6 -0
  53. data/spec/utils/db_producer_spec.rb +10 -0
  54. metadata +56 -33
  55. data/lib/deimos/avro_data_coder.rb +0 -89
  56. data/lib/deimos/avro_data_decoder.rb +0 -36
  57. data/lib/deimos/avro_data_encoder.rb +0 -51
  58. data/lib/deimos/monkey_patches/schema_store.rb +0 -19
  59. data/lib/deimos/publish_backend.rb +0 -30
  60. data/spec/avro_data_decoder_spec.rb +0 -18
  61. data/spec/avro_data_encoder_spec.rb +0 -37
  62. data/spec/updateable_schema_store_spec.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c3894cd6181a4213507473bee8a572421f0b3d645a3b9c9fea042b699634376
4
- data.tar.gz: 980ec0edfda96e7b51822b2209c4f91c8c36286c78910636c6a904d168e9fa66
3
+ metadata.gz: c2f20063d68540f1d48e8a87b210f42b146156120b0cc183bd9ce9b3aacee848
4
+ data.tar.gz: 23a7dac7a19d8defae93a5e675b614541d771b4c46746f12177dbd184ff37d67
5
5
  SHA512:
6
- metadata.gz: 0e5abba9957ae55c5c2894da01852f20e24682d7ff0a863c8a825e629fcd70937b56a6238d0d8a73df087c1f4be46ea2a9d4ed1568ee116ada6c0bdc4f74c75e
7
- data.tar.gz: affd7b4a83f851f4b34e170787aa4b9e7a483cd58e9572df5b280d5b50021b3fac696ac6eea6cde36ef0263a10e82eded22eb59b2cbcb3c6dd881bd0c75f6e67
6
+ metadata.gz: d3e9e905a67b22e953c9b8066d13adcff0f7c219676791f30e05ba34e007913c7615996278cf1aa52655a3f272d007baefd08f9d2612cbcc7d4453d656872197
7
+ data.tar.gz: 7978987a6ba5e56c2d9f5fd46582b17ed4ae85cac8b94fd67a65bac13dd60a0d56491eb91f060b030324e29f94b00158fdd0b87125ba4acd2b0ccfe887a009cf
data/.rubocop.yml CHANGED
@@ -291,6 +291,9 @@ RSpec/HookArgument:
291
291
  RSpec/ItBehavesLike:
292
292
  EnforcedStyle: it_should_behave_like
293
293
 
294
+ RSpec/LeakyConstantDeclaration:
295
+ Enabled: false
296
+
294
297
  RSpec/MessageChain:
295
298
  Severity: refactor
296
299
 
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## UNRELEASED
9
9
 
10
+ # [1.5.0-beta2] - 2020-01-17
11
+ - Added schema backends, which should simplify Avro encoding and make it
12
+ more flexible for unit tests and local development.
13
+ - Add `:test` producer backend which replaces the existing TestHelpers
14
+ functionality of writing messages to an in-memory hash.
15
+
10
16
  # [1.4.0-beta7] - 2019-12-16
11
17
  - Clone loggers when assigning to multiple levels.
12
18
 
@@ -23,6 +29,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
29
  # [1.4.0-beta1] - 2019-11-22
24
30
  - Complete revamp of configuration method.
25
31
 
32
+ # [1.3.0-beta5] - 2020-01-14
33
+ - Added `db_producer.insert` and `db_producer.process` metrics.
34
+
35
+ # [1.3.0-beta4] - 2019-12-02
36
+ - Fixed bug where by running `rake deimos:start` without
37
+ specifying a producer backend would crash.
38
+
26
39
  # [1.3.0-beta3] - 2019-11-26
27
40
  - Fixed bug in TestHelpers where key_decoder was not stubbed out.
28
41
 
data/Gemfile.lock CHANGED
@@ -1,51 +1,83 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- deimos-ruby (1.4.0.pre.beta7)
5
- avro-patches (~> 0.3)
6
- avro_turf (~> 0.8)
4
+ deimos-ruby (1.5.0.pre.beta2)
5
+ avro_turf (~> 0.11)
7
6
  phobos (~> 1.8.2.pre.beta2)
8
7
  ruby-kafka (~> 0.7)
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
12
11
  specs:
13
- activemodel (5.2.0)
14
- activesupport (= 5.2.0)
15
- activerecord (5.2.0)
16
- activemodel (= 5.2.0)
17
- activesupport (= 5.2.0)
12
+ actioncable (5.2.4)
13
+ actionpack (= 5.2.4)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ actionmailer (5.2.4)
17
+ actionpack (= 5.2.4)
18
+ actionview (= 5.2.4)
19
+ activejob (= 5.2.4)
20
+ mail (~> 2.5, >= 2.5.4)
21
+ rails-dom-testing (~> 2.0)
22
+ actionpack (5.2.4)
23
+ actionview (= 5.2.4)
24
+ activesupport (= 5.2.4)
25
+ rack (~> 2.0)
26
+ rack-test (>= 0.6.3)
27
+ rails-dom-testing (~> 2.0)
28
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
+ actionview (5.2.4)
30
+ activesupport (= 5.2.4)
31
+ builder (~> 3.1)
32
+ erubi (~> 1.4)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
35
+ activejob (5.2.4)
36
+ activesupport (= 5.2.4)
37
+ globalid (>= 0.3.6)
38
+ activemodel (5.2.4)
39
+ activesupport (= 5.2.4)
40
+ activerecord (5.2.4)
41
+ activemodel (= 5.2.4)
42
+ activesupport (= 5.2.4)
18
43
  arel (>= 9.0)
19
- activerecord-import (0.28.1)
44
+ activerecord-import (1.0.3)
20
45
  activerecord (>= 3.2)
21
- activesupport (5.2.0)
46
+ activestorage (5.2.4)
47
+ actionpack (= 5.2.4)
48
+ activerecord (= 5.2.4)
49
+ marcel (~> 0.3.1)
50
+ activesupport (5.2.4)
22
51
  concurrent-ruby (~> 1.0, >= 1.0.2)
23
52
  i18n (>= 0.7, < 2)
24
53
  minitest (~> 5.1)
25
54
  tzinfo (~> 1.1)
26
55
  arel (9.0.0)
27
56
  ast (2.4.0)
28
- avro (1.8.2)
57
+ avro (1.9.1)
29
58
  multi_json
30
- avro-patches (0.4.1)
31
- avro (= 1.8.2)
32
59
  avro_turf (0.11.0)
33
60
  avro (>= 1.7.7, < 1.10)
34
61
  excon (~> 0.45)
62
+ builder (3.2.4)
35
63
  coderay (1.1.2)
36
- concurrent-ruby (1.1.3)
37
- concurrent-ruby-ext (1.1.3)
38
- concurrent-ruby (= 1.1.3)
39
- ddtrace (0.22.0)
64
+ concurrent-ruby (1.1.5)
65
+ concurrent-ruby-ext (1.1.5)
66
+ concurrent-ruby (= 1.1.5)
67
+ crass (1.0.5)
68
+ ddtrace (0.30.0)
40
69
  msgpack
41
70
  diff-lcs (1.3)
42
71
  digest-crc (0.4.1)
43
- dogstatsd-ruby (4.2.0)
44
- excon (0.71.0)
72
+ dogstatsd-ruby (4.5.0)
73
+ erubi (1.9.0)
74
+ excon (0.71.1)
45
75
  exponential-backoff (0.0.4)
46
- ffi (1.11.1)
76
+ ffi (1.11.3)
47
77
  formatador (0.2.5)
48
- guard (2.14.2)
78
+ globalid (0.4.2)
79
+ activesupport (>= 4.2.0)
80
+ guard (2.16.1)
49
81
  formatador (>= 0.2.4)
50
82
  listen (>= 2.7, < 4.0)
51
83
  lumberjack (>= 1.0.12, < 2.0)
@@ -62,31 +94,43 @@ GEM
62
94
  guard-rubocop (1.3.0)
63
95
  guard (~> 2.0)
64
96
  rubocop (~> 0.20)
65
- i18n (1.1.1)
97
+ i18n (1.7.0)
66
98
  concurrent-ruby (~> 1.0)
67
- jaro_winkler (1.5.3)
68
- listen (3.1.5)
69
- rb-fsevent (~> 0.9, >= 0.9.4)
70
- rb-inotify (~> 0.9, >= 0.9.7)
71
- ruby_dep (~> 1.2)
99
+ jaro_winkler (1.5.4)
100
+ listen (3.2.1)
101
+ rb-fsevent (~> 0.10, >= 0.10.3)
102
+ rb-inotify (~> 0.9, >= 0.9.10)
72
103
  little-plugger (1.1.4)
73
104
  logging (2.2.2)
74
105
  little-plugger (~> 1.1)
75
106
  multi_json (~> 1.10)
76
- lumberjack (1.0.12)
77
- method_source (0.9.0)
78
- minitest (5.11.3)
79
- msgpack (1.2.10)
107
+ loofah (2.4.0)
108
+ crass (~> 1.0.2)
109
+ nokogiri (>= 1.5.9)
110
+ lumberjack (1.0.13)
111
+ mail (2.7.1)
112
+ mini_mime (>= 0.1.1)
113
+ marcel (0.3.3)
114
+ mimemagic (~> 0.3.2)
115
+ method_source (0.9.2)
116
+ mimemagic (0.3.3)
117
+ mini_mime (1.0.2)
118
+ mini_portile2 (2.4.0)
119
+ minitest (5.13.0)
120
+ msgpack (1.3.1)
80
121
  multi_json (1.14.1)
81
- mysql2 (0.5.2)
122
+ mysql2 (0.5.3)
82
123
  nenv (0.3.0)
83
- notiffany (0.1.1)
124
+ nio4r (2.5.2)
125
+ nokogiri (1.10.7)
126
+ mini_portile2 (~> 2.4.0)
127
+ notiffany (0.1.3)
84
128
  nenv (~> 0.1)
85
129
  shellany (~> 0.0)
86
- parallel (1.17.0)
87
- parser (2.6.3.0)
130
+ parallel (1.19.1)
131
+ parser (2.6.5.0)
88
132
  ast (~> 2.4.0)
89
- pg (1.1.3)
133
+ pg (1.1.4)
90
134
  phobos (1.8.2)
91
135
  activesupport (>= 3.0.0)
92
136
  concurrent-ruby (>= 1.0.2)
@@ -95,49 +139,85 @@ GEM
95
139
  logging
96
140
  ruby-kafka
97
141
  thor
98
- pry (0.11.3)
142
+ pry (0.12.2)
99
143
  coderay (~> 1.1.0)
100
144
  method_source (~> 0.9.0)
145
+ rack (2.0.8)
146
+ rack-test (1.1.0)
147
+ rack (>= 1.0, < 3)
148
+ rails (5.2.4)
149
+ actioncable (= 5.2.4)
150
+ actionmailer (= 5.2.4)
151
+ actionpack (= 5.2.4)
152
+ actionview (= 5.2.4)
153
+ activejob (= 5.2.4)
154
+ activemodel (= 5.2.4)
155
+ activerecord (= 5.2.4)
156
+ activestorage (= 5.2.4)
157
+ activesupport (= 5.2.4)
158
+ bundler (>= 1.3.0)
159
+ railties (= 5.2.4)
160
+ sprockets-rails (>= 2.0.0)
161
+ rails-dom-testing (2.0.3)
162
+ activesupport (>= 4.2.0)
163
+ nokogiri (>= 1.6)
164
+ rails-html-sanitizer (1.3.0)
165
+ loofah (~> 2.3)
166
+ railties (5.2.4)
167
+ actionpack (= 5.2.4)
168
+ activesupport (= 5.2.4)
169
+ method_source
170
+ rake (>= 0.8.7)
171
+ thor (>= 0.19.0, < 2.0)
101
172
  rainbow (3.0.0)
102
173
  rake (10.5.0)
103
- rb-fsevent (0.10.2)
104
- rb-inotify (0.9.10)
105
- ffi (>= 0.5.0, < 2)
106
- rspec (3.7.0)
107
- rspec-core (~> 3.7.0)
108
- rspec-expectations (~> 3.7.0)
109
- rspec-mocks (~> 3.7.0)
110
- rspec-core (3.7.1)
111
- rspec-support (~> 3.7.0)
112
- rspec-expectations (3.7.0)
174
+ rb-fsevent (0.10.3)
175
+ rb-inotify (0.10.0)
176
+ ffi (~> 1.0)
177
+ rspec (3.9.0)
178
+ rspec-core (~> 3.9.0)
179
+ rspec-expectations (~> 3.9.0)
180
+ rspec-mocks (~> 3.9.0)
181
+ rspec-core (3.9.0)
182
+ rspec-support (~> 3.9.0)
183
+ rspec-expectations (3.9.0)
113
184
  diff-lcs (>= 1.2.0, < 2.0)
114
- rspec-support (~> 3.7.0)
115
- rspec-mocks (3.7.0)
185
+ rspec-support (~> 3.9.0)
186
+ rspec-mocks (3.9.0)
116
187
  diff-lcs (>= 1.2.0, < 2.0)
117
- rspec-support (~> 3.7.0)
118
- rspec-support (3.7.1)
119
- rspec_junit_formatter (0.3.0)
188
+ rspec-support (~> 3.9.0)
189
+ rspec-support (3.9.0)
190
+ rspec_junit_formatter (0.4.1)
120
191
  rspec-core (>= 2, < 4, != 2.12.0)
121
- rubocop (0.72.0)
192
+ rubocop (0.77.0)
122
193
  jaro_winkler (~> 1.5.1)
123
194
  parallel (~> 1.10)
124
195
  parser (>= 2.6)
125
196
  rainbow (>= 2.2.2, < 4.0)
126
197
  ruby-progressbar (~> 1.7)
127
198
  unicode-display_width (>= 1.4.0, < 1.7)
128
- rubocop-rspec (1.32.0)
129
- rubocop (>= 0.60.0)
199
+ rubocop-rspec (1.37.1)
200
+ rubocop (>= 0.68.1)
130
201
  ruby-kafka (0.7.10)
131
202
  digest-crc
132
203
  ruby-progressbar (1.10.1)
133
- ruby_dep (1.5.0)
134
204
  shellany (0.0.1)
135
- sqlite3 (1.3.13)
136
- thor (0.20.3)
205
+ sprockets (4.0.0)
206
+ concurrent-ruby (~> 1.0)
207
+ rack (> 1, < 3)
208
+ sprockets-rails (3.2.1)
209
+ actionpack (>= 4.0)
210
+ activesupport (>= 4.0)
211
+ sprockets (>= 3.0.0)
212
+ sqlite3 (1.4.1)
213
+ thor (1.0.1)
137
214
  thread_safe (0.3.6)
138
215
  tzinfo (1.2.5)
139
216
  thread_safe (~> 0.1)
140
217
  unicode-display_width (1.6.0)
218
+ websocket-driver (0.7.1)
219
+ websocket-extensions (>= 0.1.0)
220
+ websocket-extensions (0.1.4)
141
221
 
142
222
  PLATFORMS
143
223
  ruby
@@ -145,6 +225,7 @@ PLATFORMS
145
225
  DEPENDENCIES
146
226
  activerecord (~> 5.2)
147
227
  activerecord-import
228
+ avro (~> 1.9)
148
229
  bundler (~> 1)
149
230
  ddtrace (~> 0.11)
150
231
  deimos-ruby!
@@ -154,6 +235,7 @@ DEPENDENCIES
154
235
  guard-rubocop (~> 1)
155
236
  mysql2 (~> 0.5)
156
237
  pg (~> 1.1)
238
+ rails (~> 5.2)
157
239
  rake (~> 10)
158
240
  rspec (~> 3)
159
241
  rspec_junit_formatter (~> 0.3)
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  <img src="https://img.shields.io/codeclimate/maintainability/flipp-oss/deimos.svg"/>
7
7
  </p>
8
8
 
9
- A Ruby framework for marrying Kafka, Avro, and/or ActiveRecord and provide
9
+ A Ruby framework for marrying Kafka, a schema definition like Avro, and/or ActiveRecord and provide
10
10
  a useful toolbox of goodies for Ruby-based Kafka development.
11
11
  Built on Phobos and hence Ruby-Kafka.
12
12
 
@@ -14,6 +14,7 @@ Built on Phobos and hence Ruby-Kafka.
14
14
  * [Installation](#installation)
15
15
  * [Versioning](#versioning)
16
16
  * [Configuration](#configuration)
17
+ * [Schemas](#schemas)
17
18
  * [Producers](#producers)
18
19
  * [Auto-added Fields](#auto-added-fields)
19
20
  * [Coerced Values](#coerced-values)
@@ -60,6 +61,27 @@ gem 'deimos-ruby', '~> 1.1'
60
61
 
61
62
  For a full configuration reference, please see [the configuration docs ](docs/CONFIGURATION.md).
62
63
 
64
+ # Schemas
65
+
66
+ Deimos was originally written only supporting Avro encoding via a schema registry.
67
+ This has since been expanded to a plugin architecture allowing messages to be
68
+ encoded and decoded via any schema specification you wish.
69
+
70
+ Currently we have the following possible schema backends:
71
+ * Avro Local (use pure Avro)
72
+ * Avro Schema Registry (use the Confluent Schema Registry)
73
+ * Avro Validation (validate using an Avro schema but leave decoded - this is useful
74
+ for unit testing and development)
75
+ * Mock (no actual encoding/decoding).
76
+
77
+ Note that to use Avro-encoding, you must include the [avro_turf](https://github.com/dasch/avro_turf) gem in your
78
+ Gemfile.
79
+
80
+ Other possible schemas could include [Protobuf](https://developers.google.com/protocol-buffers), [JSONSchema](https://json-schema.org/), etc. Feel free to
81
+ contribute!
82
+
83
+ To create a new schema backend, please see the existing examples [here](lib/deimos/schema_backends).
84
+
63
85
  # Producers
64
86
 
65
87
  Producers will look like this:
@@ -137,7 +159,7 @@ produced by Phobos and RubyKafka):
137
159
  * topic
138
160
  * exception_object
139
161
  * payloads - the unencoded payloads
140
- * `encode_messages` - sent when messages are being Avro-encoded.
162
+ * `encode_messages` - sent when messages are being schema-encoded.
141
163
  * producer - the class that produced the message
142
164
  * topic
143
165
  * payloads - the unencoded payloads
@@ -165,8 +187,8 @@ Similarly:
165
187
  ### Kafka Message Keys
166
188
 
167
189
  Topics representing events rather than domain data don't need keys. However,
168
- best practice for domain messages is to Avro-encode message keys
169
- with a separate Avro schema.
190
+ best practice for domain messages is to schema-encode message keys
191
+ with a separate schema.
170
192
 
171
193
  This enforced by requiring producers to define a `key_config` directive. If
172
194
  any message comes in with a key, the producer will error out if `key_config` is
@@ -179,7 +201,7 @@ There are three possible configurations to use:
179
201
  all your messages in a topic need to have a key, or they all need to have
180
202
  no key. This is a good choice for events that aren't keyed - you can still
181
203
  set a partition key.
182
- * `key_config plain: true` - this indicates that you are not using an Avro-encoded
204
+ * `key_config plain: true` - this indicates that you are not using an encoded
183
205
  key. Use this for legacy topics - new topics should not use this setting.
184
206
  * `key_config schema: 'MyKeySchema-key'` - this tells the producer to look for
185
207
  an existing key schema named `MyKeySchema-key` in the schema registry and to
@@ -234,8 +256,8 @@ like this:
234
256
  ```
235
257
 
236
258
  If you publish a payload `{ "test_id" => "123", "some_int" => 123 }`, this
237
- will be turned into a key that looks like `{ "test_id" => "123"}` and encoded
238
- via Avro before being sent to Kafka.
259
+ will be turned into a key that looks like `{ "test_id" => "123"}` and schema-encoded
260
+ before being sent to Kafka.
239
261
 
240
262
  If you are using `plain` or `schema` as your config, you will need to have a
241
263
  special `payload_key` key to your payload hash. This will be extracted and
@@ -261,7 +283,7 @@ class MyConsumer < Deimos::Consumer
261
283
 
262
284
  def consume(payload, metadata)
263
285
  # Same method as Phobos consumers.
264
- # payload is an Avro-decoded hash.
286
+ # payload is an schema-decoded hash.
265
287
  # metadata is a hash that contains information like :key and :topic.
266
288
  # In general, your key should be included in the payload itself. However,
267
289
  # if you need to access it separately from the payload, you can use
@@ -311,7 +333,7 @@ this sample:
311
333
  class MyBatchConsumer < Deimos::BatchConsumer
312
334
 
313
335
  def consume_batch(payloads, metadata)
314
- # payloads is an array of Avro-decoded hashes.
336
+ # payloads is an array of schema-decoded hashes.
315
337
  # metadata is a hash that contains information like :keys and :topic.
316
338
  # Keys are automatically decoded and available as an array with
317
339
  # the same cardinality as the payloads. If you need to iterate
@@ -581,6 +603,11 @@ The following metrics are reported:
581
603
  with the database backend. Tagged with the topic that is waiting.
582
604
  Will send a value of 0 with no topics tagged if there are no messages
583
605
  waiting.
606
+ * `db_producer.insert` - the number of messages inserted into the database
607
+ for publishing. Tagged with `topic:{topic_name}`
608
+ * `db_producer.process` - the number of DB messages processed. Note that this
609
+ is *not* the same as the number of messages *published* if those messages
610
+ are compacted. Tagged with `topic:{topic_name}`
584
611
 
585
612
  ### Configuring Metrics Providers
586
613
 
@@ -604,7 +631,7 @@ Also see [deimos.rb](lib/deimos.rb) under `Configure metrics` to see how the met
604
631
  Deimos also includes some tracing for kafka consumers. It ships with
605
632
  DataDog support, but you can add custom tracing providers as well.
606
633
 
607
- Trace spans are used for when incoming messages are avro decoded, and a
634
+ Trace spans are used for when incoming messages are schema-decoded, and a
608
635
  separate span for message consume logic.
609
636
 
610
637
  ### Configuring Tracing Providers
@@ -749,7 +776,7 @@ be
749
776
  }
750
777
  ```
751
778
 
752
- Both payload and key will be Avro-decoded as necessary according to the
779
+ Both payload and key will be schema-decoded as necessary according to the
753
780
  key config.
754
781
 
755
782
  You can also just pass an existing producer or consumer class into the method,
data/Rakefile CHANGED
@@ -6,8 +6,8 @@ begin
6
6
 
7
7
  RSpec::Core::RakeTask.new(:spec)
8
8
  task(default: :spec)
9
- rescue LoadError # rubocop:disable Lint/HandleExceptions
9
+ rescue LoadError # rubocop:disable Lint/SuppressedException
10
10
  # no rspec available
11
11
  end
12
12
 
13
- import('./lib/tasks/phobos.rake')
13
+ import('./lib/tasks/deimos.rake')
data/deimos-ruby.gemspec CHANGED
@@ -18,13 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_runtime_dependency('avro-patches', '~> 0.3')
22
- spec.add_runtime_dependency('avro_turf', '~> 0.8')
21
+ spec.add_runtime_dependency('avro_turf', '~> 0.11')
23
22
  spec.add_runtime_dependency('phobos', '~> 1.8.2.pre.beta2')
24
23
  spec.add_runtime_dependency('ruby-kafka', '~> 0.7')
25
24
 
26
25
  spec.add_development_dependency('activerecord', '~> 5.2')
27
26
  spec.add_development_dependency('activerecord-import')
27
+ spec.add_development_dependency('avro', '~> 1.9')
28
28
  spec.add_development_dependency('bundler', '~> 1')
29
29
  spec.add_development_dependency('ddtrace', '~> 0.11')
30
30
  spec.add_development_dependency('dogstatsd-ruby', '~> 4.2')
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency('guard-rubocop', '~> 1')
34
34
  spec.add_development_dependency('mysql2', '~> 0.5')
35
35
  spec.add_development_dependency('pg', '~> 1.1')
36
+ spec.add_development_dependency('rails', '~> 5.2')
36
37
  spec.add_development_dependency('rake', '~> 10')
37
38
  spec.add_development_dependency('rspec', '~> 3')
38
39
  spec.add_development_dependency('rspec_junit_formatter', '~>0.3')
@@ -144,6 +144,7 @@ producers.backend|`:kafka_async`|Currently can be set to `:db`, `:kafka`, or `:k
144
144
 
145
145
  Config name|Default|Description
146
146
  -----------|-------|-----------
147
+ schema.backend|`:mock`|Backend representing the schema encoder/decoder. You can see a full list [here](../lib/deimos/schema_backends).
147
148
  schema.registry_url|`http://localhost:8081`|URL of the Confluent schema registry.
148
149
  schema.path|nil|Local path to find your schemas.
149
150
 
@@ -77,7 +77,7 @@ The database backend consists of three tables:
77
77
 
78
78
  * `kafka_messages` - this keeps track of the messages that were "published",
79
79
  including the payload, topic, key and partition key. These messages
80
- are *raw data* - all processing, including Avro encoding, must happen
80
+ are *raw data* - all processing, including schema-encoding, must happen
81
81
  upstream before they are inserted.
82
82
  * `kafka_topic_info` - this table is essentially a lock table used to ensure
83
83
  that only one producer thread is ever "working" on a topic at a time.
@@ -68,32 +68,31 @@ module Deimos
68
68
  def record_attributes(payload)
69
69
  klass = self.class.config[:record_class]
70
70
  attributes = {}
71
- schema = self.class.decoder.avro_schema
72
- schema.fields.each do |field|
71
+ self.class.decoder.schema_fields.each do |field|
73
72
  column = klass.columns.find { |c| c.name == field.name }
74
73
  next if column.nil?
75
74
  next if %w(updated_at created_at).include?(field.name)
76
75
 
77
- attributes[field.name] = _coerce_field(field, column, payload[field.name])
76
+ attributes[field.name] = _coerce_field(column, payload[field.name])
78
77
  end
79
78
  attributes
80
79
  end
81
80
 
82
81
  private
83
82
 
84
- # @param field [Avro::Schema]
85
83
  # @param column [ActiveRecord::ConnectionAdapters::Column]
86
84
  # @param val [Object]
87
- def _coerce_field(field, column, val)
85
+ def _coerce_field(column, val)
88
86
  return nil if val.nil?
89
87
 
90
- field_type = field.type.type.to_sym
91
- if field_type == :union
92
- union_types = field.type.schemas.map { |s| s.type.to_sym }
93
- field_type = union_types.find { |t| t != :null }
94
- end
95
- if column.type == :datetime && %i(int long).include?(field_type)
96
- return Time.zone.strptime(val.to_s, '%s')
88
+ if column.type == :datetime
89
+ int_val = begin
90
+ val.is_a?(Integer) ? val : (val.is_a?(String) && Integer(val))
91
+ rescue StandardError
92
+ nil
93
+ end
94
+
95
+ return Time.zone.at(int_val) if int_val
97
96
  end
98
97
 
99
98
  val
@@ -53,10 +53,10 @@ module Deimos
53
53
  # is not set.
54
54
  # @return [Hash]
55
55
  def generate_payload(attributes, _record)
56
- schema = self.encoder.avro_schema
56
+ fields = self.encoder.schema_fields
57
57
  payload = attributes.stringify_keys
58
58
  payload.delete_if do |k, _|
59
- k.to_sym != :payload_key && !schema.fields.find { |f| f.name == k }
59
+ k.to_sym != :payload_key && !fields.map(&:name).include?(k)
60
60
  end
61
61
  end
62
62
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deimos
4
+ module Backends
5
+ # Abstract class for all publish backends.
6
+ class Base
7
+ class << self
8
+ # @param producer_class [Class < Deimos::Producer]
9
+ # @param messages [Array<Deimos::Message>]
10
+ def publish(producer_class:, messages:)
11
+ Deimos.config.logger.info(
12
+ message: 'Publishing messages',
13
+ topic: producer_class.topic,
14
+ payloads: messages.map do |message|
15
+ {
16
+ payload: message.payload,
17
+ key: message.key
18
+ }
19
+ end
20
+ )
21
+ execute(producer_class: producer_class, messages: messages)
22
+ end
23
+
24
+ # @param producer_class [Class < Deimos::Producer]
25
+ # @param messages [Array<Deimos::Message>]
26
+ def execute(producer_class:, messages:)
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -6,7 +6,7 @@ module Deimos
6
6
  module Backends
7
7
  # Backend which saves messages to the database instead of immediately
8
8
  # sending them.
9
- class Db < Deimos::PublishBackend
9
+ class Db < Base
10
10
  class << self
11
11
  # :nodoc:
12
12
  def execute(producer_class:, messages:)
@@ -20,6 +20,11 @@ module Deimos
20
20
  message
21
21
  end
22
22
  Deimos::KafkaMessage.import(records)
23
+ Deimos.config.metrics&.increment(
24
+ 'db_producer.insert',
25
+ tags: %W(topic:#{producer_class.topic}),
26
+ by: records.size
27
+ )
23
28
  end
24
29
  end
25
30
  end
@@ -3,7 +3,7 @@
3
3
  module Deimos
4
4
  module Backends
5
5
  # Default backend to produce to Kafka.
6
- class Kafka < Deimos::PublishBackend
6
+ class Kafka < Base
7
7
  include Phobos::Producer
8
8
 
9
9
  # Shut down the producer if necessary.
@@ -3,7 +3,7 @@
3
3
  module Deimos
4
4
  module Backends
5
5
  # Backend which produces to Kafka via an async producer.
6
- class KafkaAsync < Deimos::PublishBackend
6
+ class KafkaAsync < Base
7
7
  include Phobos::Producer
8
8
 
9
9
  # Shut down the producer cleanly.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deimos
4
+ module Backends
5
+ # Backend which saves messages to an in-memory hash.
6
+ class Test < Deimos::Backends::Base
7
+ class << self
8
+ # @return [Array<Hash>]
9
+ def sent_messages
10
+ @sent_messages ||= []
11
+ end
12
+ end
13
+
14
+ # @override
15
+ def self.execute(producer_class:, messages:)
16
+ self.sent_messages.concat(messages.map(&:to_h))
17
+ end
18
+ end
19
+ end
20
+ end