waterdrop 2.0.7 → 2.6.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/workflows/ci.yml +22 -11
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +200 -0
  7. data/Gemfile +0 -2
  8. data/Gemfile.lock +32 -75
  9. data/README.md +22 -275
  10. data/certs/cert_chain.pem +26 -0
  11. data/config/locales/errors.yml +33 -0
  12. data/docker-compose.yml +19 -12
  13. data/lib/waterdrop/clients/buffered.rb +90 -0
  14. data/lib/waterdrop/clients/dummy.rb +69 -0
  15. data/lib/waterdrop/clients/rdkafka.rb +34 -0
  16. data/lib/{water_drop → waterdrop}/config.rb +39 -16
  17. data/lib/waterdrop/contracts/config.rb +43 -0
  18. data/lib/waterdrop/contracts/message.rb +64 -0
  19. data/lib/{water_drop → waterdrop}/errors.rb +14 -7
  20. data/lib/waterdrop/instrumentation/callbacks/delivery.rb +102 -0
  21. data/lib/{water_drop → waterdrop}/instrumentation/callbacks/error.rb +6 -2
  22. data/lib/{water_drop → waterdrop}/instrumentation/callbacks/statistics.rb +1 -1
  23. data/lib/{water_drop/instrumentation/stdout_listener.rb → waterdrop/instrumentation/logger_listener.rb} +66 -21
  24. data/lib/waterdrop/instrumentation/monitor.rb +20 -0
  25. data/lib/{water_drop/instrumentation/monitor.rb → waterdrop/instrumentation/notifications.rb} +12 -14
  26. data/lib/waterdrop/instrumentation/vendors/datadog/dashboard.json +1 -0
  27. data/lib/waterdrop/instrumentation/vendors/datadog/metrics_listener.rb +210 -0
  28. data/lib/waterdrop/middleware.rb +50 -0
  29. data/lib/{water_drop → waterdrop}/producer/async.rb +40 -4
  30. data/lib/{water_drop → waterdrop}/producer/buffer.rb +12 -30
  31. data/lib/{water_drop → waterdrop}/producer/builder.rb +6 -11
  32. data/lib/{water_drop → waterdrop}/producer/sync.rb +44 -15
  33. data/lib/waterdrop/producer/transactions.rb +170 -0
  34. data/lib/waterdrop/producer.rb +308 -0
  35. data/lib/{water_drop → waterdrop}/version.rb +1 -1
  36. data/lib/waterdrop.rb +28 -2
  37. data/renovate.json +6 -0
  38. data/waterdrop.gemspec +14 -11
  39. data.tar.gz.sig +0 -0
  40. metadata +71 -111
  41. metadata.gz.sig +0 -0
  42. data/certs/mensfeld.pem +0 -25
  43. data/config/errors.yml +0 -6
  44. data/lib/water_drop/contracts/config.rb +0 -26
  45. data/lib/water_drop/contracts/message.rb +0 -42
  46. data/lib/water_drop/instrumentation/callbacks/delivery.rb +0 -30
  47. data/lib/water_drop/instrumentation/callbacks/statistics_decorator.rb +0 -77
  48. data/lib/water_drop/instrumentation/callbacks_manager.rb +0 -39
  49. data/lib/water_drop/instrumentation.rb +0 -20
  50. data/lib/water_drop/patches/rdkafka/bindings.rb +0 -42
  51. data/lib/water_drop/patches/rdkafka/producer.rb +0 -20
  52. data/lib/water_drop/producer/dummy_client.rb +0 -32
  53. data/lib/water_drop/producer.rb +0 -162
  54. data/lib/water_drop.rb +0 -36
  55. /data/lib/{water_drop → waterdrop}/contracts.rb +0 -0
  56. /data/lib/{water_drop → waterdrop}/producer/status.rb +0 -0
@@ -1,162 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WaterDrop
4
- # Main WaterDrop messages producer
5
- class Producer
6
- include Sync
7
- include Async
8
- include Buffer
9
-
10
- # @return [String] uuid of the current producer
11
- attr_reader :id
12
- # @return [Status] producer status object
13
- attr_reader :status
14
- # @return [Concurrent::Array] internal messages buffer
15
- attr_reader :messages
16
- # @return [Object] monitor we want to use
17
- attr_reader :monitor
18
- # @return [Object] dry-configurable config object
19
- attr_reader :config
20
-
21
- # Creates a not-yet-configured instance of the producer
22
- # @param block [Proc] configuration block
23
- # @return [Producer] producer instance
24
- def initialize(&block)
25
- @buffer_mutex = Mutex.new
26
- @connecting_mutex = Mutex.new
27
- @closing_mutex = Mutex.new
28
-
29
- @status = Status.new
30
- @messages = Concurrent::Array.new
31
-
32
- return unless block
33
-
34
- setup(&block)
35
- end
36
-
37
- # Sets up the whole configuration and initializes all that is needed
38
- # @param block [Block] configuration block
39
- def setup(&block)
40
- raise Errors::ProducerAlreadyConfiguredError, id unless @status.initial?
41
-
42
- @config = Config
43
- .new
44
- .setup(&block)
45
- .config
46
-
47
- @id = @config.id
48
- @monitor = @config.monitor
49
- @contract = Contracts::Message.new(max_payload_size: @config.max_payload_size)
50
- @status.configured!
51
- end
52
-
53
- # @return [Rdkafka::Producer] raw rdkafka producer
54
- # @note Client is lazy initialized, keeping in mind also the fact of a potential fork that
55
- # can happen any time.
56
- # @note It is not recommended to fork a producer that is already in use so in case of
57
- # bootstrapping a cluster, it's much better to fork configured but not used producers
58
- def client
59
- return @client if @client && @pid == Process.pid
60
-
61
- # Don't allow to obtain a client reference for a producer that was not configured
62
- raise Errors::ProducerNotConfiguredError, id if @status.initial?
63
-
64
- @connecting_mutex.synchronize do
65
- return @client if @client && @pid == Process.pid
66
-
67
- # We should raise an error when trying to use a producer from a fork, that is already
68
- # connected to Kafka. We allow forking producers only before they are used
69
- raise Errors::ProducerUsedInParentProcess, Process.pid if @status.connected?
70
-
71
- # We undefine all the finalizers, in case it was a fork, so the finalizers from the parent
72
- # process don't leak
73
- ObjectSpace.undefine_finalizer(id)
74
- # Finalizer tracking is needed for handling shutdowns gracefully.
75
- # I don't expect everyone to remember about closing all the producers all the time, thus
76
- # this approach is better. Although it is still worth keeping in mind, that this will
77
- # block GC from removing a no longer used producer unless closed properly but at least
78
- # won't crash the VM upon closing the process
79
- ObjectSpace.define_finalizer(id, proc { close })
80
-
81
- @pid = Process.pid
82
- @client = Builder.new.call(self, @config)
83
-
84
- # Register statistics runner for this particular type of callbacks
85
- ::WaterDrop::Instrumentation.statistics_callbacks.add(
86
- @id,
87
- Instrumentation::Callbacks::Statistics.new(@id, @client.name, @config.monitor)
88
- )
89
-
90
- # Register error tracking callback
91
- ::WaterDrop::Instrumentation.error_callbacks.add(
92
- @id,
93
- Instrumentation::Callbacks::Error.new(@id, @client.name, @config.monitor)
94
- )
95
-
96
- @status.connected!
97
- end
98
-
99
- @client
100
- end
101
-
102
- # Flushes the buffers in a sync way and closes the producer
103
- def close
104
- @closing_mutex.synchronize do
105
- return unless @status.active?
106
-
107
- @monitor.instrument(
108
- 'producer.closed',
109
- producer_id: id
110
- ) do
111
- @status.closing!
112
-
113
- # No need for auto-gc if everything got closed by us
114
- # This should be used only in case a producer was not closed properly and forgotten
115
- ObjectSpace.undefine_finalizer(id)
116
-
117
- # Flush has its own buffer mutex but even if it is blocked, flushing can still happen
118
- # as we close the client after the flushing (even if blocked by the mutex)
119
- flush(true)
120
-
121
- # We should not close the client in several threads the same time
122
- # It is safe to run it several times but not exactly the same moment
123
- # We also mark it as closed only if it was connected, if not, it would trigger a new
124
- # connection that anyhow would be immediately closed
125
- client.close if @client
126
-
127
- # Remove callbacks runners that were registered
128
- ::WaterDrop::Instrumentation.statistics_callbacks.delete(@id)
129
- ::WaterDrop::Instrumentation.error_callbacks.delete(@id)
130
-
131
- @status.closed!
132
- end
133
- end
134
- end
135
-
136
- # Ensures that we don't run any operations when the producer is not configured or when it
137
- # was already closed
138
- def ensure_active!
139
- return if @status.active?
140
-
141
- raise Errors::ProducerNotConfiguredError, id if @status.initial?
142
- raise Errors::ProducerClosedError, id if @status.closing? || @status.closed?
143
-
144
- # This should never happen
145
- raise Errors::StatusInvalidError, [id, @status.to_s]
146
- end
147
-
148
- # Ensures that the message we want to send out to Kafka is actually valid and that it can be
149
- # sent there
150
- # @param message [Hash] message we want to send
151
- # @raise [Karafka::Errors::MessageInvalidError]
152
- def validate_message!(message)
153
- result = @contract.call(message)
154
- return if result.success?
155
-
156
- raise Errors::MessageInvalidError, [
157
- result.errors.to_h,
158
- message
159
- ]
160
- end
161
- end
162
- end
data/lib/water_drop.rb DELETED
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # External components
4
- # delegate should be removed because we don't need it, we just add it because of ruby-kafka
5
- %w[
6
- concurrent/array
7
- dry-configurable
8
- dry/monitor/notifications
9
- dry-validation
10
- rdkafka
11
- json
12
- zeitwerk
13
- securerandom
14
- ].each { |lib| require lib }
15
-
16
- # WaterDrop library
17
- module WaterDrop
18
- class << self
19
- # @return [String] root path of this gem
20
- def gem_root
21
- Pathname.new(File.expand_path('..', __dir__))
22
- end
23
- end
24
- end
25
-
26
- Zeitwerk::Loader
27
- .for_gem
28
- .tap { |loader| loader.ignore("#{__dir__}/waterdrop.rb") }
29
- .tap(&:setup)
30
- .tap(&:eager_load)
31
-
32
- # Rdkafka uses a single global callback for things. We bypass that by injecting a manager for
33
- # each callback type. Callback manager allows us to register more than one callback
34
- # @note Those managers are also used by Karafka for consumer related statistics
35
- Rdkafka::Config.statistics_callback = WaterDrop::Instrumentation.statistics_callbacks
36
- Rdkafka::Config.error_callback = WaterDrop::Instrumentation.error_callbacks
File without changes
File without changes