nagare-redis 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e9e2f9eff170361ad7bd5cf8177e204901661a1c2ead971a7c691cc56074a61
4
- data.tar.gz: b7da8fbdfc9ef0f4df7d9d69edfc75fe00af96e2fb90e60639c76e473388e124
3
+ metadata.gz: a74be4520fe261310cc782a2232ad5270ed572c4afbd98072241ef6dc1bc118c
4
+ data.tar.gz: f3d9391aae29591f9652e342034ce79959498a020596b01a4d4c3d40bf35d33a
5
5
  SHA512:
6
- metadata.gz: 348df3c8aca37b15300f8e9bd167a5b70462467c220f9ac6b6e25bf074c886eeb9425e9aeb00dba1d4fbad3fb345691424e325fed1677f6f94a1bbc106bf2601
7
- data.tar.gz: a42b40b705890d5b485c73af9c63afd786723552af98a7d728e9b224a4601e52e4ea6073e924b0d007d9ec771040404bb04d2395d1439568159727773774bb52
6
+ metadata.gz: b9c5c5bfa26746f5a0d5c84446991aad2d3d3faf802e8c9222d61fb79b6f2aaecdf55b81654ec71571c5d978d1350bb8306c054db2e816106c9a730383c2b9e6
7
+ data.tar.gz: a973dc7583907a0b75d0c8e09dda57e799721eab302a3d1786e0c83293c1475baa37cbea8ccc3dbb36fd39a45e4c906bac33dea2b4980a36c7a733b7a919f96f
data/Gemfile.lock CHANGED
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: .
9
9
  specs:
10
- nagare-redis (0.4.0)
10
+ nagare-redis (0.3.0)
11
11
  redis (~> 6.2, >= 6.2.0)
12
12
 
13
13
  GEM
data/README.md CHANGED
@@ -67,6 +67,19 @@ Nagare.configure do |config|
67
67
  # and in the background
68
68
  # Default: 3 threads
69
69
  config.threads = 3
70
+
71
+ # Nagare can execute a proc for error handling. This enables you to
72
+ # use APM tools like New Relic or Appsignal with it.
73
+ # The proc takes 2 parameters, message and error.
74
+ # By default, nagare logs the error to stderr.
75
+ config.error_handler = proc do |message, error|
76
+ Appsignal.set_error(error);
77
+ end
78
+
79
+ # After exceeding the maximum number of retries, nagare moves the
80
+ # failing messages to a Dead Letter Queue stream.
81
+ # By default this stream is named 'dlq', but you can customize it.
82
+ config.dlq_stream = 'its_dead_jim'
70
83
  end
71
84
  ```
72
85
 
data/lib/nagare/config.rb CHANGED
@@ -5,13 +5,14 @@ module Nagare
5
5
  # See the README for possible values and what they do
6
6
  class Config
7
7
  class << self
8
- attr_accessor :group_name, :redis_url, :threads, :suffix, :min_idle_time
8
+ attr_accessor :group_name, :redis_url, :threads, :suffix, :min_idle_time,
9
+ :error_handler, :dlq_stream, :max_retries
9
10
 
10
11
  # Runs code in the block passed in to configure Nagare and sets defaults
11
12
  # when values are not set.
12
13
  #
13
14
  # returns [Nagare::Config] self
14
- # rubocop:disable Metrics/CyclomaticComplexity
15
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
15
16
  def configure
16
17
  yield(self)
17
18
  @dead_consumer_timeout ||= 5000
@@ -20,9 +21,16 @@ module Nagare
20
21
  @threads ||= 1
21
22
  @suffix ||= nil
22
23
  @min_idle_time ||= 600_000
24
+ @error_handler ||= proc do |message, error|
25
+ Nagare.logger.error "Failed to process message #{message}"
26
+ Nagare.logger.error error.message
27
+ Nagare.logger.error error.backtrace.join("\n")
28
+ end
29
+ @dlq_stream ||= 'dlq'
30
+ @max_retries ||= 10
23
31
  self
24
32
  end
25
- # rubocop:enable Metrics/CyclomaticComplexity
33
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
26
34
  end
27
35
  end
28
36
  end
@@ -83,10 +83,8 @@ module Nagare
83
83
  listeners.each do |listener|
84
84
  invoke_listener(stream, message, listener)
85
85
  rescue StandardError => e
86
- logger.error e.message
87
- logger.error e.backtrace.join("\n")
88
86
  listener_failed = true
89
- # TODO: Notify Appsignal
87
+ Nagare::Config.error_handler.call(message, e)
90
88
  end
91
89
 
92
90
  return if listener_failed
@@ -76,27 +76,64 @@ module Nagare
76
76
  # @return [String] message id
77
77
  def publish(stream, event_name, data)
78
78
  stream = stream_name(stream)
79
- connection.xadd(stream, { "#{event_name}": data })
79
+ connection.xadd(stream, { "#{event_name}": data.to_json })
80
80
  end
81
81
 
82
82
  ##
83
83
  # Claums the next message of the consumer group that is stuck
84
84
  # (pending and past min_idle_time since being picked up)
85
85
  #
86
- # @param stream [String] name of the stream
86
+ # @param stream_prefix [String] name of the stream
87
87
  # @param group [String] name of the consumer group
88
88
  #
89
89
  # @return [Array[Hash]] array containing the 1 message or empty
90
- def claim_next_stuck_message(stream, group)
91
- stream = stream_name(stream)
90
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
91
+ def claim_next_stuck_message(stream_prefix, group)
92
+ stream = stream_name(stream_prefix)
92
93
  result = connection.xautoclaim(stream,
93
94
  "#{stream}-#{group}",
94
95
  "#{hostname}-#{thread_id}",
95
96
  Nagare::Config.min_idle_time,
96
97
  '0-0',
97
98
  count: 1)
99
+
100
+ # Move message to DLQ if retried too much and get next one
101
+ if result['entries'].any?
102
+ message_id = result['entries'].first.first
103
+ if retry_count(stream_prefix, group,
104
+ message_id) > Nagare::Config.max_retries
105
+ move_to_dlq(stream_prefix, group, result['entries'].first)
106
+ return claim_next_stuck_message(stream, group)
107
+ end
108
+ end
109
+
98
110
  result['entries'] || []
99
111
  end
112
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
113
+
114
+ ##
115
+ # Uses XPENDING to verify the number of times the message was
116
+ # delivered
117
+ def retry_count(stream, group, message_id)
118
+ stream = stream_name(stream)
119
+ result = connection.xpending(stream,
120
+ "#{stream}-#{group}",
121
+ message_id,
122
+ message_id,
123
+ 1)
124
+ return 0 unless result.any?
125
+
126
+ result.first['count']
127
+ end
128
+
129
+ ##
130
+ # Moves a message to the dead letter queue stream
131
+ def move_to_dlq(stream, group, message)
132
+ Nagare.logger.warn "Moving message to DLQ #{message} \
133
+ from stream #{stream}"
134
+ publish(Nagare::Config.dlq_stream, stream, message)
135
+ mark_processed(stream, group, message.first)
136
+ end
100
137
 
101
138
  ##
102
139
  # Reads the next messages from the consumer group in redis.
@@ -6,6 +6,6 @@ module Nagare
6
6
  # see https://github.com/googleapis/release-please/blob/master/src/updaters/version-rb.ts
7
7
  #
8
8
  # rubocop:disable Style/StringLiterals
9
- VERSION = "0.4.0"
9
+ VERSION = "0.5.0"
10
10
  # rubocop:enable Style/StringLiterals
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nagare-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Reis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-13 00:00:00.000000000 Z
11
+ date: 2021-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  requirements: []
127
- rubygems_version: 3.0.3.1
127
+ rubygems_version: 3.0.3
128
128
  signing_key:
129
129
  specification_version: 4
130
130
  summary: Persistent and resilient pub/sub using Redis Streams