deimos-ruby 1.11.0 → 1.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +8 -8
- data/README.md +149 -66
- data/deimos-ruby.gemspec +1 -1
- data/docs/CONFIGURATION.md +4 -0
- data/lib/deimos/active_record_consume/batch_consumption.rb +1 -1
- data/lib/deimos/active_record_consume/message_consumption.rb +4 -3
- data/lib/deimos/active_record_consumer.rb +2 -2
- data/lib/deimos/active_record_producer.rb +3 -0
- data/lib/deimos/config/configuration.rb +29 -0
- data/lib/deimos/consume/batch_consumption.rb +2 -2
- data/lib/deimos/consume/message_consumption.rb +2 -2
- data/lib/deimos/consumer.rb +10 -0
- data/lib/deimos/producer.rb +4 -3
- data/lib/deimos/schema_backends/avro_base.rb +64 -1
- data/lib/deimos/schema_backends/base.rb +18 -2
- data/lib/deimos/schema_class/base.rb +62 -0
- data/lib/deimos/schema_class/enum.rb +24 -0
- data/lib/deimos/schema_class/record.rb +66 -0
- data/lib/deimos/shared_config.rb +5 -0
- data/lib/deimos/test_helpers.rb +43 -7
- data/lib/deimos/utils/schema_class.rb +29 -0
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +3 -0
- data/lib/generators/deimos/schema_class/templates/schema_class.rb.tt +15 -0
- data/lib/generators/deimos/schema_class/templates/schema_enum.rb.tt +21 -0
- data/lib/generators/deimos/schema_class/templates/schema_record.rb.tt +65 -0
- data/lib/generators/deimos/schema_class_generator.rb +247 -0
- data/lib/tasks/deimos.rake +8 -0
- data/spec/active_record_batch_consumer_spec.rb +120 -110
- data/spec/active_record_consumer_spec.rb +97 -88
- data/spec/active_record_producer_spec.rb +38 -27
- data/spec/batch_consumer_spec.rb +37 -28
- data/spec/config/configuration_spec.rb +10 -3
- data/spec/consumer_spec.rb +95 -84
- data/spec/generators/active_record_generator_spec.rb +1 -0
- data/spec/generators/schema_class/my_schema_with_complex_types_spec.rb +206 -0
- data/spec/generators/schema_class_generator_spec.rb +186 -0
- data/spec/producer_spec.rb +110 -0
- data/spec/schema_classes/generated.rb +156 -0
- data/spec/schema_classes/my_nested_schema.rb +114 -0
- data/spec/schema_classes/my_schema.rb +53 -0
- data/spec/schema_classes/my_schema_key.rb +35 -0
- data/spec/schema_classes/my_schema_with_complex_types.rb +172 -0
- data/spec/schemas/com/my-namespace/Generated.avsc +6 -0
- data/spec/schemas/com/my-namespace/MySchemaWithComplexTypes.avsc +95 -0
- data/spec/spec_helper.rb +6 -1
- metadata +28 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 456ac47b0f41a41e874a109a005d50650ffc36ae0ca9e924f5e06a0c1b7599ef
|
4
|
+
data.tar.gz: 14f3e9591b4a32ea503bd063416d13cdd3625bcd220bbe6f61675f1fb911afe6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5f0f9e5d2e0e68c42102166490a929602ef0327c84d42f987200c5c7c941135b7120ca18827707d056a57e9e9ee6e923a7bba91df84f5b7873f1b24e96597f6
|
7
|
+
data.tar.gz: ad75c79468a87c1a68658099ce9234730926cc049897febd797b2bedbfea316b5ff5b51a8fe215afbd087cf90ca93f2c292b9733128e5470f09cf3e3120d7cce
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
+
# 1.12.1 - 2021-11-02
|
11
|
+
|
12
|
+
- ### Fixes :wrench:
|
13
|
+
- Fixed issue where Schema Class Consumer/Producer are using `Deimos::` instead of `Schema::` for instances of classes.
|
14
|
+
|
15
|
+
# 1.12.0 - 2021-11-01
|
16
|
+
|
17
|
+
### Features :star:
|
18
|
+
|
19
|
+
- Generate Schema classes from Avro Schemas
|
20
|
+
- Use Schema Classes in your consumer and producer
|
21
|
+
|
10
22
|
## 1.11.0 - 2021-08-27
|
11
23
|
|
12
24
|
- ### Fixes :wrench:
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
deimos-ruby (1.
|
4
|
+
deimos-ruby (1.12.0)
|
5
5
|
avro_turf (~> 0.11)
|
6
6
|
fig_tree (~> 0.0.2)
|
7
7
|
phobos (>= 1.9, < 3.0)
|
@@ -88,11 +88,11 @@ GEM
|
|
88
88
|
ddtrace (0.46.0)
|
89
89
|
msgpack
|
90
90
|
diff-lcs (1.4.4)
|
91
|
-
digest-crc (0.6.
|
91
|
+
digest-crc (0.6.4)
|
92
92
|
rake (>= 12.0.0, < 14.0.0)
|
93
93
|
dogstatsd-ruby (4.8.3)
|
94
94
|
erubi (1.10.0)
|
95
|
-
excon (0.
|
95
|
+
excon (0.85.0)
|
96
96
|
exponential-backoff (0.0.4)
|
97
97
|
ffi (1.15.0)
|
98
98
|
fig_tree (0.0.2)
|
@@ -153,10 +153,10 @@ GEM
|
|
153
153
|
nenv (~> 0.1)
|
154
154
|
shellany (~> 0.0)
|
155
155
|
parallel (1.20.1)
|
156
|
-
parser (3.0.
|
156
|
+
parser (3.0.1.1)
|
157
157
|
ast (~> 2.4.1)
|
158
158
|
pg (1.2.3)
|
159
|
-
phobos (2.0
|
159
|
+
phobos (2.1.0)
|
160
160
|
activesupport (>= 3.0.0)
|
161
161
|
concurrent-ruby (>= 1.0.2)
|
162
162
|
concurrent-ruby-ext (>= 1.0.2)
|
@@ -227,7 +227,7 @@ GEM
|
|
227
227
|
rspec-support (3.10.2)
|
228
228
|
rspec_junit_formatter (0.4.1)
|
229
229
|
rspec-core (>= 2, < 4, != 2.12.0)
|
230
|
-
rubocop (0.
|
230
|
+
rubocop (0.89.0)
|
231
231
|
parallel (~> 1.10)
|
232
232
|
parser (>= 2.7.1.1)
|
233
233
|
rainbow (>= 2.2.2, < 4.0)
|
@@ -240,7 +240,7 @@ GEM
|
|
240
240
|
parser (>= 2.7.1.5)
|
241
241
|
rubocop-rspec (1.42.0)
|
242
242
|
rubocop (>= 0.87.0)
|
243
|
-
ruby-kafka (1.
|
243
|
+
ruby-kafka (1.4.0)
|
244
244
|
digest-crc
|
245
245
|
ruby-progressbar (1.11.0)
|
246
246
|
shellany (0.0.1)
|
@@ -284,7 +284,7 @@ DEPENDENCIES
|
|
284
284
|
rspec (~> 3)
|
285
285
|
rspec-rails (~> 4)
|
286
286
|
rspec_junit_formatter (~> 0.3)
|
287
|
-
rubocop (= 0.
|
287
|
+
rubocop (= 0.89.0)
|
288
288
|
rubocop-rspec (= 1.42.0)
|
289
289
|
sqlite3 (~> 1.3)
|
290
290
|
|
data/README.md
CHANGED
@@ -27,6 +27,7 @@ Built on Phobos and hence Ruby-Kafka.
|
|
27
27
|
* [Database Backend](#database-backend)
|
28
28
|
* [Database Poller](#database-poller)
|
29
29
|
* [Running Consumers](#running-consumers)
|
30
|
+
* [Generated Schema Classes](#generated-schema-classes)
|
30
31
|
* [Metrics](#metrics)
|
31
32
|
* [Testing](#testing)
|
32
33
|
* [Test Helpers](#test-helpers)
|
@@ -795,6 +796,101 @@ which can be useful if you want to figure out if you're inside the task
|
|
795
796
|
as opposed to running your Rails server or console. E.g. you could start your
|
796
797
|
DB backend only when your rake task is running.
|
797
798
|
|
799
|
+
## Generated Schema Classes
|
800
|
+
|
801
|
+
Deimos offers a way to generate classes from Avro schemas. These classes are documented
|
802
|
+
with YARD to aid in IDE auto-complete, and will help to move errors closer to the code.
|
803
|
+
|
804
|
+
Add the following configurations for schema class generation:
|
805
|
+
|
806
|
+
```ruby
|
807
|
+
config.schema.generated_class_path 'path/to/generated/classes' # Defaults to 'app/lib/schema_classes'
|
808
|
+
```
|
809
|
+
|
810
|
+
Run the following command to generate schema classes in your application. It will generate classes for every configured consumer or producer by `Deimos.configure`:
|
811
|
+
|
812
|
+
bundle exec rake deimos:generate_schema_classes
|
813
|
+
|
814
|
+
Add the following configurations to start using generated schema classes in your application's Consumers and Producers:
|
815
|
+
|
816
|
+
config.schema.use_schema_classes true
|
817
|
+
|
818
|
+
Additionally, you can enable or disable the usage of schema classes for a particular consumer or producer with the
|
819
|
+
`use_schema_classes` config. See [Configuration](./docs/CONFIGURATION.md#defining-producers).
|
820
|
+
|
821
|
+
### Consumer
|
822
|
+
|
823
|
+
The consumer interface uses the `decode_message` method to turn JSON hash into the Schemas
|
824
|
+
generated Class and provides it to the `consume`/`consume_batch` methods for their use.
|
825
|
+
|
826
|
+
Examples of consumers would look like this:
|
827
|
+
```ruby
|
828
|
+
class MyConsumer < Deimos::Consumer
|
829
|
+
def consume(payload, metadata)
|
830
|
+
# Same method as Phobos consumers but payload is now an instance of Deimos::SchemaClass::Record
|
831
|
+
# rather than a hash. metadata is still a hash that contains information like :key and :topic.
|
832
|
+
# You can interact with the schema class instance in the following way:
|
833
|
+
do_something(payload.test_id, payload.some_int)
|
834
|
+
# The original behaviour was as follows:
|
835
|
+
do_something(payload[:test_id], payload[:some_int])
|
836
|
+
end
|
837
|
+
end
|
838
|
+
```
|
839
|
+
|
840
|
+
```ruby
|
841
|
+
class MyActiveRecordConsumer < Deimos::ActiveRecordConsumer
|
842
|
+
record_class Widget
|
843
|
+
# Any method that expects a message payload as a hash will instead
|
844
|
+
# receive an instance of Deimos::SchemaClass::Record.
|
845
|
+
def record_attributes(payload, key)
|
846
|
+
# You can interact with the schema class instance in the following way:
|
847
|
+
super.merge(:some_field => "some_value-#{payload.test_id}")
|
848
|
+
# The original behaviour was as follows:
|
849
|
+
super.merge(:some_field => "some_value-#{payload[:test_id]}")
|
850
|
+
end
|
851
|
+
end
|
852
|
+
```
|
853
|
+
|
854
|
+
### Producer
|
855
|
+
Similarly to the consumer interface, the producer interface for using Schema Classes in your app
|
856
|
+
relies on the `publish`/`publish_list` methods to convert a _provided_ instance of a Schema Class
|
857
|
+
into a hash that can be used freely by the Kafka client.
|
858
|
+
|
859
|
+
Examples of producers would look like this:
|
860
|
+
```ruby
|
861
|
+
class MyProducer < Deimos::Producer
|
862
|
+
class << self
|
863
|
+
# @param test_id [String]
|
864
|
+
# @param some_int [Integer]
|
865
|
+
def self.send_a_message(test_id, some_int)
|
866
|
+
# Instead of sending in a Hash object to the publish or publish_list method,
|
867
|
+
# you can initialize an instance of your schema class and send that in.
|
868
|
+
message = Schemas::MySchema.new(
|
869
|
+
test_id: test_id,
|
870
|
+
some_int: some_int
|
871
|
+
)
|
872
|
+
self.publish(message)
|
873
|
+
self.publish_list([message])
|
874
|
+
end
|
875
|
+
end
|
876
|
+
end
|
877
|
+
```
|
878
|
+
|
879
|
+
```ruby
|
880
|
+
class MyActiveRecordProducer < Deimos::ActiveRecordProducer
|
881
|
+
record_class Widget
|
882
|
+
# @param payload [Deimos::SchemaClass::Record]
|
883
|
+
# @param _record [Widget]
|
884
|
+
def self.generate_payload(attributes, _record)
|
885
|
+
# This method converts your ActiveRecord into a Deimos::SchemaClass::Record. You will be able to use super
|
886
|
+
# as an instance of Schemas::MySchema and set values that are not on your ActiveRecord schema.
|
887
|
+
res = super
|
888
|
+
res.some_value = "some_value-#{res.test_id}"
|
889
|
+
res
|
890
|
+
end
|
891
|
+
end
|
892
|
+
```
|
893
|
+
|
798
894
|
# Metrics
|
799
895
|
|
800
896
|
Deimos includes some metrics reporting out the box. It ships with DataDog support, but you can add custom metric providers as well.
|
@@ -880,8 +976,7 @@ Also see [deimos.rb](lib/deimos.rb) under `Configure tracing` to see how the tra
|
|
880
976
|
|
881
977
|
# Testing
|
882
978
|
|
883
|
-
Deimos comes with a test helper class which
|
884
|
-
to mock versions, and provides useful methods for testing consumers.
|
979
|
+
Deimos comes with a test helper class which provides useful methods for testing consumers.
|
885
980
|
|
886
981
|
In `spec_helper.rb`:
|
887
982
|
```ruby
|
@@ -890,7 +985,56 @@ RSpec.configure do |config|
|
|
890
985
|
end
|
891
986
|
```
|
892
987
|
|
893
|
-
|
988
|
+
## Test Configuration
|
989
|
+
|
990
|
+
```ruby
|
991
|
+
# The following can be added to a rpsec file so that each unit
|
992
|
+
# test can have the same settings every time it is run
|
993
|
+
around(:each) do |example|
|
994
|
+
Deimos::TestHelpers.unit_test!
|
995
|
+
example.run
|
996
|
+
Deimos.config.reset!
|
997
|
+
end
|
998
|
+
|
999
|
+
# Similarly you can use the Kafka test helper
|
1000
|
+
around(:each) do |example|
|
1001
|
+
Deimos::TestHelpers.kafka_test!
|
1002
|
+
example.run
|
1003
|
+
Deimos.config.reset!
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# Kakfa test helper using schema registry
|
1007
|
+
around(:each) do |example|
|
1008
|
+
Deimos::TestHelpers.full_integration_test!
|
1009
|
+
example.run
|
1010
|
+
Deimos.config.reset!
|
1011
|
+
end
|
1012
|
+
```
|
1013
|
+
|
1014
|
+
With the help of these helper methods, rspec examples can be written without having to tinker with Deimos settings.
|
1015
|
+
This also prevents Deimos setting changes from leaking in to other examples.
|
1016
|
+
|
1017
|
+
This does not take away the ability to configure Deimos manually in individual examples. Deimos can still be configured like so:
|
1018
|
+
```ruby
|
1019
|
+
it 'should not fail this random test' do
|
1020
|
+
|
1021
|
+
Deimos.configure do |config|
|
1022
|
+
config.consumers.fatal_error = proc { true }
|
1023
|
+
config.consumers.reraise_errors = false
|
1024
|
+
end
|
1025
|
+
...
|
1026
|
+
expect(some_object).to be_truthy
|
1027
|
+
...
|
1028
|
+
Deimos.config.reset!
|
1029
|
+
end
|
1030
|
+
```
|
1031
|
+
If you are using one of the test helpers in an `around(:each)` block and want to override few settings for one example,
|
1032
|
+
you can do it like in the example shown above. These settings would only apply to that specific example and the Deimos config should
|
1033
|
+
reset once the example has finished running.
|
1034
|
+
|
1035
|
+
## Test Usage
|
1036
|
+
|
1037
|
+
In your tests, you now have the following methods available:
|
894
1038
|
```ruby
|
895
1039
|
# Pass a consumer class (not instance) to validate a payload against it.
|
896
1040
|
# This will fail if the payload does not match the schema the consumer
|
@@ -938,6 +1082,8 @@ expect(message).to eq({
|
|
938
1082
|
})
|
939
1083
|
```
|
940
1084
|
|
1085
|
+
### Test Utilities
|
1086
|
+
|
941
1087
|
There is also a helper method that will let you test if an existing schema
|
942
1088
|
would be compatible with a new version of it. You can use this in your
|
943
1089
|
Ruby console but it would likely not be part of your RSpec test:
|
@@ -947,69 +1093,6 @@ require 'deimos/test_helpers'
|
|
947
1093
|
# Can pass a file path, a string or a hash into this:
|
948
1094
|
Deimos::TestHelpers.schemas_compatible?(schema1, schema2)
|
949
1095
|
```
|
950
|
-
### Test Helpers
|
951
|
-
|
952
|
-
There are helper methods available to configure Deimos for different types of testing scenarios.
|
953
|
-
Currently there are helpers defined for unit tests and for testing Kafka related code. You can use it as follows:
|
954
|
-
|
955
|
-
```ruby
|
956
|
-
# The following can be added to a rpsec file so that each unit
|
957
|
-
# test can have the same settings every time it is run
|
958
|
-
around(:each) do |example|
|
959
|
-
Deimos::TestHelpers.unit_test!
|
960
|
-
example.run
|
961
|
-
Deimos.config.reset!
|
962
|
-
end
|
963
|
-
|
964
|
-
# Similarly you can use the Kafka test helper
|
965
|
-
around(:each) do |example|
|
966
|
-
Deimos::TestHelpers.kafka_test!
|
967
|
-
example.run
|
968
|
-
Deimos.config.reset!
|
969
|
-
end
|
970
|
-
|
971
|
-
# Kakfa test helper using schema registry
|
972
|
-
around(:each) do |example|
|
973
|
-
Deimos::TestHelpers.full_integration_test!
|
974
|
-
example.run
|
975
|
-
Deimos.config.reset!
|
976
|
-
end
|
977
|
-
```
|
978
|
-
|
979
|
-
With the help of these helper methods, rspec examples can be written without having to tinker with Deimos settings.
|
980
|
-
This also prevents Deimos setting changes from leaking in to other examples.
|
981
|
-
|
982
|
-
This does not take away the ability to configure Deimos manually in individual examples. Deimos can still be configured like so:
|
983
|
-
```ruby
|
984
|
-
it 'should not fail this random test' do
|
985
|
-
|
986
|
-
Deimos.configure do |config|
|
987
|
-
config.consumers.fatal_error = proc { true }
|
988
|
-
config.consumers.reraise_errors = false
|
989
|
-
end
|
990
|
-
...
|
991
|
-
expect(some_object).to be_truthy
|
992
|
-
...
|
993
|
-
Deimos.config.reset!
|
994
|
-
end
|
995
|
-
```
|
996
|
-
If you are using one of the test helpers in an `around(:each)` block and want to override few settings for one example,
|
997
|
-
you can do it like in the example shown above. These settings would only apply to that specific example and the Deimos conifg should
|
998
|
-
reset once the example has finished running.
|
999
|
-
|
1000
|
-
|
1001
|
-
### Integration Test Helpers
|
1002
|
-
|
1003
|
-
When running integration tests, you'll want to override the default test helper settings:
|
1004
|
-
|
1005
|
-
```ruby
|
1006
|
-
config.before(:each, :my_integration_metadata) do
|
1007
|
-
Deimos.configure do
|
1008
|
-
producers.backend :kafka
|
1009
|
-
schema.backend :avro_schema_registry
|
1010
|
-
end
|
1011
|
-
end
|
1012
|
-
```
|
1013
1096
|
|
1014
1097
|
You can use the `InlineConsumer` class to help with integration testing,
|
1015
1098
|
with a full external Kafka running.
|
data/deimos-ruby.gemspec
CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
|
|
39
39
|
spec.add_development_dependency('rspec', '~> 3')
|
40
40
|
spec.add_development_dependency('rspec_junit_formatter', '~>0.3')
|
41
41
|
spec.add_development_dependency('rspec-rails', '~> 4')
|
42
|
-
spec.add_development_dependency('rubocop', '0.
|
42
|
+
spec.add_development_dependency('rubocop', '0.89.0')
|
43
43
|
spec.add_development_dependency('rubocop-rspec', '1.42.0')
|
44
44
|
spec.add_development_dependency('sqlite3', '~> 1.3')
|
45
45
|
end
|
data/docs/CONFIGURATION.md
CHANGED
@@ -44,6 +44,7 @@ topic|nil|Topic to produce to.
|
|
44
44
|
schema|nil|Name of the schema to use to encode data before producing.
|
45
45
|
namespace|nil|Namespace of the schema to use when finding it locally.
|
46
46
|
key_config|nil|Configuration hash for message keys. See [Kafka Message Keys](../README.md#installation)
|
47
|
+
use_schema_classes|nil|Set to true or false to enable or disable using the producers schema classes. See [Generated Schema Classes](../README.md#generated-schema-classes)
|
47
48
|
|
48
49
|
## Defining Consumers
|
49
50
|
|
@@ -81,6 +82,7 @@ namespace|nil|Namespace of the schema to use when finding it locally.
|
|
81
82
|
key_config|nil|Configuration hash for message keys. See [Kafka Message Keys](../README.md#installation)
|
82
83
|
disabled|false|Set to true to skip starting an actual listener for this consumer on startup.
|
83
84
|
group_id|nil|ID of the consumer group.
|
85
|
+
use_schema_classes|nil|Set to true or false to enable or disable using the consumers schema classes. See [Generated Schema Classes](../README.md#generated-schema-classes)
|
84
86
|
max_concurrency|1|Number of threads created for this listener. Each thread will behave as an independent consumer. They don't share any state.
|
85
87
|
start_from_beginning|true|Once the consumer group has checkpointed its progress in the topic's partitions, the consumers will always start from the checkpointed offsets, regardless of config. As such, this setting only applies when the consumer initially starts consuming from a topic
|
86
88
|
max_bytes_per_partition|512.kilobytes|Maximum amount of data fetched from a single partition at a time.
|
@@ -176,6 +178,8 @@ Config name|Default|Description
|
|
176
178
|
schema.backend|`:mock`|Backend representing the schema encoder/decoder. You can see a full list [here](../lib/deimos/schema_backends).
|
177
179
|
schema.registry_url|`http://localhost:8081`|URL of the Confluent schema registry.
|
178
180
|
schema.path|nil|Local path to find your schemas.
|
181
|
+
schema.use_schema_classes|false|Set this to true to use generated schema classes in your application.
|
182
|
+
schema.generated_schema_path|`app/lib/schema_classes`|Local path to generated schema classes.
|
179
183
|
|
180
184
|
## Database Producer Configuration
|
181
185
|
|
@@ -15,7 +15,7 @@ module Deimos
|
|
15
15
|
# If two messages in a batch have the same key, we cannot process them
|
16
16
|
# in the same operation as they would interfere with each other. Thus
|
17
17
|
# they are split
|
18
|
-
# @param payloads [Array<Hash>] Decoded payloads
|
18
|
+
# @param payloads [Array<Hash|Deimos::SchemaClass::Record>] Decoded payloads
|
19
19
|
# @param metadata [Hash] Information about batch, including keys.
|
20
20
|
def consume_batch(payloads, metadata)
|
21
21
|
messages = payloads.
|
@@ -9,7 +9,7 @@ module Deimos
|
|
9
9
|
# Default is to use the primary key column and the value of the first
|
10
10
|
# field in the key.
|
11
11
|
# @param klass [Class < ActiveRecord::Base]
|
12
|
-
# @param _payload [Hash]
|
12
|
+
# @param _payload [Hash|Deimos::SchemaClass::Record]
|
13
13
|
# @param key [Object]
|
14
14
|
# @return [ActiveRecord::Base]
|
15
15
|
def fetch_record(klass, _payload, key)
|
@@ -18,13 +18,14 @@ module Deimos
|
|
18
18
|
|
19
19
|
# Assign a key to a new record.
|
20
20
|
# @param record [ActiveRecord::Base]
|
21
|
-
# @param _payload [Hash]
|
21
|
+
# @param _payload [Hash|Deimos::SchemaClass::Record]
|
22
22
|
# @param key [Object]
|
23
23
|
def assign_key(record, _payload, key)
|
24
24
|
record[record.class.primary_key] = key
|
25
25
|
end
|
26
26
|
|
27
|
-
#
|
27
|
+
# @param payload [Hash|Deimos::SchemaClass::Record] Decoded payloads
|
28
|
+
# @param metadata [Hash] Information about batch, including keys.
|
28
29
|
def consume(payload, metadata)
|
29
30
|
unless self.process_message?(payload)
|
30
31
|
Deimos.config.logger.debug(
|
@@ -50,14 +50,14 @@ module Deimos
|
|
50
50
|
|
51
51
|
# Override this method (with `super`) if you want to add/change the default
|
52
52
|
# attributes set to the new/existing record.
|
53
|
-
# @param payload [Hash]
|
53
|
+
# @param payload [Hash|Deimos::SchemaClass::Record]
|
54
54
|
# @param _key [String]
|
55
55
|
def record_attributes(payload, _key=nil)
|
56
56
|
@converter.convert(payload)
|
57
57
|
end
|
58
58
|
|
59
59
|
# Override this message to conditionally save records
|
60
|
-
# @param
|
60
|
+
# @param _payload [Hash|Deimos::SchemaClass::Record] The kafka message
|
61
61
|
# @return [Boolean] if true, record is created/update.
|
62
62
|
# If false, record processing is skipped but message offset is still committed.
|
63
63
|
def process_message?(_payload)
|
@@ -58,6 +58,9 @@ module Deimos
|
|
58
58
|
payload.delete_if do |k, _|
|
59
59
|
k.to_sym != :payload_key && !fields.map(&:name).include?(k)
|
60
60
|
end
|
61
|
+
return payload unless Utils::SchemaClass.use?(config.to_h)
|
62
|
+
|
63
|
+
Utils::SchemaClass.instance(payload, config[:schema])
|
61
64
|
end
|
62
65
|
|
63
66
|
# Query to use when polling the database with the DbPoller. Add
|
@@ -18,6 +18,9 @@ module Deimos
|
|
18
18
|
# :nodoc:
|
19
19
|
after_configure do
|
20
20
|
Phobos.configure(self.config.phobos_config)
|
21
|
+
if self.config.schema.use_schema_classes
|
22
|
+
load_generated_schema_classes
|
23
|
+
end
|
21
24
|
self.config.producer_objects.each do |producer|
|
22
25
|
configure_producer_or_consumer(producer)
|
23
26
|
end
|
@@ -28,6 +31,17 @@ module Deimos
|
|
28
31
|
validate_db_backend if self.config.producers.backend == :db
|
29
32
|
end
|
30
33
|
|
34
|
+
# Loads generated classes
|
35
|
+
def self.load_generated_schema_classes
|
36
|
+
if Deimos.config.schema.generated_class_path.nil?
|
37
|
+
raise 'Cannot use schema classes without schema.generated_class_path. Please provide a directory.'
|
38
|
+
end
|
39
|
+
|
40
|
+
Dir["./#{Deimos.config.schema.generated_class_path}/**/*.rb"].sort.each { |f| require f }
|
41
|
+
rescue LoadError
|
42
|
+
raise 'Cannot load schema classes. Please regenerate classes with rake deimos:generate_schema_models.'
|
43
|
+
end
|
44
|
+
|
31
45
|
# Ensure everything is set up correctly for the DB backend.
|
32
46
|
def self.validate_db_backend
|
33
47
|
begin
|
@@ -68,6 +82,7 @@ module Deimos
|
|
68
82
|
schema(kafka_config.schema) if kafka_config.schema.present?
|
69
83
|
namespace(kafka_config.namespace) if kafka_config.namespace.present?
|
70
84
|
key_config(**kafka_config.key_config) if kafka_config.key_config.present?
|
85
|
+
schema_class_config(kafka_config.use_schema_classes) if kafka_config.use_schema_classes.present?
|
71
86
|
end
|
72
87
|
end
|
73
88
|
|
@@ -260,6 +275,14 @@ module Deimos
|
|
260
275
|
# Local path to look for schemas in.
|
261
276
|
# @return [String]
|
262
277
|
setting :path
|
278
|
+
|
279
|
+
# Local path for schema classes to be generated in.
|
280
|
+
# @return [String]
|
281
|
+
setting :generated_class_path, 'app/lib/schema_classes'
|
282
|
+
|
283
|
+
# Set to true to use the generated schema classes in your application
|
284
|
+
# @return [Boolean]
|
285
|
+
setting :use_schema_classes, false
|
263
286
|
end
|
264
287
|
|
265
288
|
# The configured metrics provider.
|
@@ -301,6 +324,9 @@ module Deimos
|
|
301
324
|
# Key configuration (see docs).
|
302
325
|
# @return [Hash]
|
303
326
|
setting :key_config
|
327
|
+
# Configure the usage of generated schema classes for this producer
|
328
|
+
# @return [Boolean]
|
329
|
+
setting :use_schema_classes
|
304
330
|
end
|
305
331
|
|
306
332
|
setting_object :consumer do
|
@@ -323,6 +349,9 @@ module Deimos
|
|
323
349
|
# listener.
|
324
350
|
# @return [Boolean]
|
325
351
|
setting :disabled, false
|
352
|
+
# Configure the usage of generated schema classes for this consumer
|
353
|
+
# @return [Boolean]
|
354
|
+
setting :use_schema_classes
|
326
355
|
|
327
356
|
# These are the phobos "listener" configs. See CONFIGURATION.md for more
|
328
357
|
# info.
|
@@ -21,11 +21,11 @@ module Deimos
|
|
21
21
|
metadata[:first_offset] = batch.first&.offset
|
22
22
|
|
23
23
|
payloads = batch.map do |message|
|
24
|
-
|
24
|
+
decode_message(message.payload)
|
25
25
|
end
|
26
26
|
_received_batch(payloads, metadata)
|
27
27
|
_with_span do
|
28
|
-
yield
|
28
|
+
yield(payloads, metadata)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
_handle_batch_success(benchmark.real, payloads, metadata)
|
@@ -15,9 +15,9 @@ module Deimos
|
|
15
15
|
benchmark = Benchmark.measure do
|
16
16
|
_with_span do
|
17
17
|
new_metadata[:key] = decode_key(metadata[:key]) if self.class.config[:key_configured]
|
18
|
-
decoded_payload =
|
18
|
+
decoded_payload = decode_message(payload)
|
19
19
|
_received_message(decoded_payload, new_metadata)
|
20
|
-
yield
|
20
|
+
yield(decoded_payload, new_metadata)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
_handle_success(benchmark.real, decoded_payload, new_metadata)
|
data/lib/deimos/consumer.rb
CHANGED
@@ -51,6 +51,16 @@ module Deimos
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
# Helper method to decode an encoded message.
|
55
|
+
# @param payload [Object]
|
56
|
+
# @return [Object] the decoded message.
|
57
|
+
def decode_message(payload)
|
58
|
+
decoded_payload = payload.nil? ? nil : self.class.decoder.decode(payload)
|
59
|
+
return decoded_payload unless Utils::SchemaClass.use?(self.class.config.to_h)
|
60
|
+
|
61
|
+
Utils::SchemaClass.instance(decoded_payload, self.class.config[:schema])
|
62
|
+
end
|
63
|
+
|
54
64
|
private
|
55
65
|
|
56
66
|
def _with_span
|
data/lib/deimos/producer.rb
CHANGED
@@ -57,6 +57,7 @@ module Deimos
|
|
57
57
|
MAX_BATCH_SIZE = 500
|
58
58
|
|
59
59
|
class << self
|
60
|
+
|
60
61
|
# @return [Hash]
|
61
62
|
def config
|
62
63
|
@config ||= {
|
@@ -86,14 +87,14 @@ module Deimos
|
|
86
87
|
end
|
87
88
|
|
88
89
|
# Publish the payload to the topic.
|
89
|
-
# @param payload [Hash] with an optional payload_key hash key.
|
90
|
+
# @param payload [Hash|SchemaClass::Record] with an optional payload_key hash key.
|
90
91
|
# @param topic [String] if specifying the topic
|
91
92
|
def publish(payload, topic: self.topic)
|
92
93
|
publish_list([payload], topic: topic)
|
93
94
|
end
|
94
95
|
|
95
96
|
# Publish a list of messages.
|
96
|
-
# @param payloads [
|
97
|
+
# @param payloads [Array<Hash|SchemaClass::Record>] with optional payload_key hash key.
|
97
98
|
# @param sync [Boolean] if given, override the default setting of
|
98
99
|
# whether to publish synchronously.
|
99
100
|
# @param force_send [Boolean] if true, ignore the configured backend
|
@@ -113,7 +114,7 @@ module Deimos
|
|
113
114
|
topic: topic,
|
114
115
|
payloads: payloads
|
115
116
|
) do
|
116
|
-
messages = Array(payloads).map { |p| Deimos::Message.new(p, self) }
|
117
|
+
messages = Array(payloads).map { |p| Deimos::Message.new(p.to_h, self) }
|
117
118
|
messages.each { |m| _process_message(m, topic) }
|
118
119
|
messages.in_groups_of(MAX_BATCH_SIZE, false) do |batch|
|
119
120
|
self.produce_batch(backend_class, batch)
|