megaphone-client 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3194e82bf4582a1956b969f23902751161badf46
4
- data.tar.gz: 3e604bdf98d9440d551cead5ce2be555f9ced59c
3
+ metadata.gz: 15b75d53f469b5b1eb81174951801eb6b131957d
4
+ data.tar.gz: 72d170f64c4a6eb00f009411e21700ce238eee82
5
5
  SHA512:
6
- metadata.gz: d68dd2886287ff4feeb390bbcffc0b0218769af1e5cbf385a412e72156e75cb3a8ccbe1dd8c287e21f90b9da933ec181a760f9d6c42fc9ca084ccf33eedd7364
7
- data.tar.gz: 4b5261feb237c02f1c894bf8141effadf68d4a0ec1e2d5230ff287552ab19f0a43928c364cfe4e7081be84e0289a7290e0c91a4ed544a8315c2587719801654e
6
+ metadata.gz: 5510da93aa27aaa55aa0bfb6b4ea68cb11678f9c7424d89cd309cad42e87e220dc62b4a0bd2ba7d5925fe233a34f8fe07c8c2cfd427c8e97645b4f9508fdffcd
7
+ data.tar.gz: 2e6f71ac8caa1f13ab0bbf81351d6baf4056322d4e5cd4ffb85e8e3ca86a56d5ab8ba9a901eecc27a22a38d525d2b94bc3d7d219f9308285ae370d81059d7eba
data/CHANGELOG.md CHANGED
@@ -5,11 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and
6
6
  this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0] - 2018-01-30
9
+
10
+ ### Added
11
+
12
+ * Add new `MegaphoneMessageDelayWarning` exception for transient errors
13
+ * Add `close` method to allow connection to Fluentd to be closed
14
+ * Support `buffer_overflow` callback to notify when messages have been lost ([documentation](../README.md#buffer-overflow-callback-handler))
15
+
16
+ ### Fixed
17
+
18
+ * Update Fluentd Gem from 0.7.1 to 0.7.2
19
+ * Do not dump entire payload into exceptions; just stream/topic IDs
20
+
8
21
  ## [1.0.1] - 2017-09-18
9
22
 
10
23
  ### Fixed
11
24
 
12
- - Output format to match the [stream-schema v2][stream-schema-v2].
25
+ * Output format to match the [stream-schema v2][stream-schema-v2].
13
26
 
14
27
  [stream-schema-v2]: https://github.com/redbubble/megaphone-event-type-registry/blob/master/stream-schema-2.0.0.json
15
28
 
@@ -17,39 +30,40 @@ this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
17
30
 
18
31
  ### Changed
19
32
 
20
- - The `Megaphone::Client` API is now stable!
33
+ * The `Megaphone::Client` API is now stable!
21
34
 
22
35
  ## [0.3.0] - 2017-08-17
23
36
 
24
37
  ### Changed
25
38
 
26
- - Signature of the `Megaphone::Client` constructor : now takes a host and a port as optional arguments and doesn't take the logger as an argument anymore.
39
+ * Signature of the `Megaphone::Client` constructor : now takes a host and a port as optional arguments and doesn't take the logger as an argument anymore.
27
40
 
28
41
  ## [0.2.0] - 2017-08-14
29
42
 
30
43
  ### Changed
31
44
 
32
- - Signature of the `publish!` method: now takes a schema and a partition key as arguments
45
+ * Signature of the `publish!` method: now takes a schema and a partition key as arguments
33
46
 
34
47
  ## [0.1.2] - 2017-08-04
35
48
 
36
49
  ### Fixed
37
50
 
38
- - Removing unnecessary files from gemspec
51
+ * Removing unnecessary files from gemspec
39
52
 
40
53
  ## [0.1.1] - 2017-08-03
41
54
 
42
55
  ### Fixed
43
56
 
44
- - Including files in gemspec
57
+ * Including files in gemspec
45
58
 
46
59
  ## 0.1.0 - 2017-08-03 [YANKED]
47
60
 
48
61
  ### Added
49
62
 
50
- - Initial implementation of the `Megaphone::Client.publish!` method
63
+ * Initial implementation of the `Megaphone::Client.publish!` method
51
64
 
52
- [1.0.1]: https://github.com/redbubble/megaphone-client-ruby/compare/v1.0.1...v1.0.1
65
+ [1.1.0]: https://github.com/redbubble/megaphone-client-ruby/compare/v1.0.1...v1.1.0
66
+ [1.0.1]: https://github.com/redbubble/megaphone-client-ruby/compare/v1.0.0...v1.0.1
53
67
  [1.0.0]: https://github.com/redbubble/megaphone-client-ruby/compare/v0.3.0...v1.0.0
54
68
  [0.3.0]: https://github.com/redbubble/megaphone-client-ruby/compare/v0.2.0...v0.3.0
55
69
  [0.2.0]: https://github.com/redbubble/megaphone-client-ruby/compare/v0.1.2...v0.2.0
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- Megaphone::Client
2
- =================
1
+ # Megaphone::Client
3
2
 
4
3
  [![Build Status](https://badge.buildkite.com/9f4fdb370f5f295ee6bf3d68937b1be2d7cf9bf65b2c7b4213.svg?branch=master)](https://buildkite.com/redbubble/megaphone-client-ruby)
5
4
  [![Gem Version](https://badge.fury.io/rb/megaphone-client.svg)](https://badge.fury.io/rb/megaphone-client)
@@ -7,8 +6,7 @@ Megaphone::Client
7
6
 
8
7
  Send events to [Megaphone](https://github.com/redbubble/Megaphone).
9
8
 
10
- Getting Started
11
- ---------------
9
+ ## Getting Started
12
10
 
13
11
  Add the gem to your `Gemfile`:
14
12
 
@@ -18,18 +16,17 @@ Add the gem to your `Gemfile`:
18
16
  gem 'megaphone-client', '~> 1.0' # see semver.org
19
17
  ```
20
18
 
21
- Usage
22
- -----
19
+ ## Usage
23
20
 
24
21
  In order to be as unobstrusive as possible, this client will append events to local files (e.g. `./work-updates.stream`) unless:
25
22
 
26
- - the `MEGAPHONE_FLUENT_HOST` and `MEGAPHONE_FLUENT_PORT` environment variables are set.
27
- - **or** the Fluentd host and port values are passed as arguments to the client's constructor
23
+ * the `MEGAPHONE_FLUENT_HOST` and `MEGAPHONE_FLUENT_PORT` environment variables are set.
24
+ * **or** the Fluentd host and port values are passed as arguments to the client's constructor
28
25
 
29
26
  That behaviour ensures that unless you want to send events to the Megaphone [streams][stream], you do not need to [start Fluentd][megaphone-fluentd] at all.
30
27
 
31
- [stream]: https://github.com/redbubble/megaphone#stream
32
- [megaphone-fluentd]: https://github.com/redbubble/megaphone-fluentd-container
28
+ [stream]: https://github.com/redbubble/megaphone#stream
29
+ [megaphone-fluentd]: https://github.com/redbubble/megaphone-fluentd-container
33
30
 
34
31
  ### Publishing events
35
32
 
@@ -54,19 +51,113 @@ payload = { url: 'https://www.redbubble.com/people/wytrab8/works/26039653-toadal
54
51
 
55
52
  # Publish your event
56
53
  client.publish!(topic, subtopic, schema, partition_key, payload)
54
+
55
+ # Note: the client will close the connection to Fluentd on exit, if you need to do it before that (unlikely), you can use Megaphone::Client#close method.
56
+
57
+ # See below for error handling instructions and examples.
57
58
  ```
58
59
 
59
- Credits
60
- -------
60
+ ## Error Handling
61
+
62
+ ### Exceptions the client will raise
63
+
64
+ `publish!` can raise two exceptions if the underlying Fluentd
65
+ client library throws an error.
66
+
67
+ The most common is `MegaphoneMessageDelayWarning` which indicates
68
+ a transient failure occurred, but the message will probably be resent
69
+ later. See section on _[Internal buffering](#internal-buffering-upon-error)_ below for more details.
70
+
71
+ The second exception is `MegaphoneUnavailableError`, which is thrown
72
+ for all other errors. Note that these _may or may not_ also buffer
73
+ the message for later transmission. Unfortunately the underlying
74
+ client library does not make the distinction.
75
+
76
+ ### Internal buffering upon error
77
+
78
+ Note that, in some cases, the client library will buffer failed messages
79
+ and attempt to resend them later. It will resend buffered messages in
80
+ the following cases:
81
+
82
+ * another message is sent
83
+ * the `close` method is called
84
+ * at exit (via an `at_exit` handler)
85
+
86
+ Applications that require fast handling of events should read the section
87
+ below on handling time-sensitive events and errors.
88
+
89
+ Because of this buffering, users of the library should not treat exceptions
90
+ as a definite reason to resend a message, as this may result in multiple
91
+ messages being eventually dispatched. However without in-depth knowledge
92
+ it can be hard to tell which exceptions are recoverable, and which indicate
93
+ some kind of catastrophic failure.
94
+
95
+ This author has confirmed that both connection failure and `ECONNRESET`
96
+ errors will result in buffering of messages.
97
+
98
+ If the internal buffer fills up, the buffer overflow handler will be called.
99
+
100
+ At exit, or when `client.close` is called, the buffer will be flushed.
101
+ If it cannot be flushed to the daemon, then the buffer overflow
102
+ handler will be called.
103
+
104
+ ### Buffer overflow callback handler
105
+
106
+ Passing a lambda to the `:overflow_handler` will enable your application
107
+ to receive notifications of messages being lost. They can be lost in at
108
+ least two ways:
109
+
110
+ * Fluentd daemon has gone away, and internal client-side buffer exceeded
111
+ * process is shutting down, and fluent client library was unable to flush buffers
112
+ to the daemon.
113
+
114
+ Production applications [MUST][rfc2119] handle this case, and raise an alert, if their
115
+ messages are considered important.
116
+
117
+ [rfc2119]: https://tools.ietf.org/html/rfc2119
118
+
119
+ ```ruby
120
+ # Example usage:
121
+ my_handler = -> (*) {
122
+ Rollbar.error("Megaphone/fluent messages lost due to buffer overflow")
123
+ }
124
+
125
+ logger = Megaphone::Client.new({
126
+ origin: "my-app",
127
+ overflow_handler: my_handler
128
+ })
129
+
130
+ begin
131
+ logger.publish!(... event ...)
132
+ rescue Megaphone::Client::MegaphoneUnavailableError => e
133
+ Rollbar.warning("Megaphone client error", e)
134
+ rescue Megaphone::Client::MegaphoneMessageDelayWarning => e
135
+ Rollbar.info("Megaphone transient message delay", e)
136
+ end
137
+ ```
138
+
139
+ ### Handling time-sensitive events and errors
140
+
141
+ As long as an application is either short lived, or frequently sending messages,
142
+ then it will probably be fine with the usual behaviour, which is to flush
143
+ buffers at next send, or flush at exit.
144
+
145
+ Applications with time-sensitive, infrequent events, will need to find a
146
+ different strategy if errors have been raised during previous message publishing.
147
+
148
+ Unfortunately there is no means to do this with the underlying Ruby client
149
+ for Fluentd. It would require patches to the upstream code to expose a flush
150
+ method.
151
+
152
+ ## Credits
61
153
 
62
154
  [![](doc/redbubble.png)][redbubble]
63
155
 
64
156
  Megaphone::Client is maintained and funded by [Redbubble][redbubble].
65
157
 
66
- [redbubble]: https://www.redbubble.com
158
+ [redbubble]: https://www.redbubble.com
67
159
 
68
- License
69
- -------
160
+ ## License
70
161
 
71
162
  Megaphone::Client
72
163
  Copyright (C) 2017 Redbubble
@@ -8,18 +8,45 @@ module Megaphone
8
8
  attr_reader :logger, :origin
9
9
  private :logger, :origin
10
10
 
11
+ # Main entry point for apps using this library.
12
+ # Will default to environment for host and port settings, if not passed.
13
+ # Note that a missing callback_handler will result in a default handler
14
+ # being assigned if the FluentLogger is used.
11
15
  def initialize(config)
12
16
  @origin = config.fetch(:origin)
13
17
  host = config.fetch(:host, ENV['MEGAPHONE_FLUENT_HOST'])
14
18
  port = config.fetch(:port, ENV['MEGAPHONE_FLUENT_PORT'])
15
- @logger = Megaphone::Client::Logger.create(host, port)
19
+ overflow_handler = config.fetch(:overflow_handler, nil)
20
+ @logger = Megaphone::Client::Logger.create(host, port, overflow_handler)
16
21
  end
17
22
 
18
23
  def publish!(topic, subtopic, schema, partition_key, payload)
19
24
  event = Event.new(topic, subtopic, origin, schema, partition_key, payload)
20
25
  unless logger.post(topic, event.to_hash)
21
- raise MegaphoneUnavailableError.new(logger.last_error.message, event)
26
+ if transient_error?(logger.last_error)
27
+ raise MegaphoneMessageDelayWarning.new(logger.last_error.message, event)
28
+ else
29
+ raise MegaphoneUnavailableError.new(logger.last_error.message, event)
30
+ end
22
31
  end
23
32
  end
33
+
34
+ def close
35
+ logger.close
36
+ end
37
+
38
+ private
39
+
40
+ def transient_error?(err)
41
+ err_msg = err.message
42
+ if err_msg.include?("Connection reset by peer")
43
+ true
44
+ elsif err_msg.include?("Broken pipe")
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
24
51
  end
25
52
  end
@@ -9,7 +9,20 @@ module Megaphone
9
9
  end
10
10
 
11
11
  def to_s
12
- "#{@msg} - The following event was not published: #{@event.to_s}"
12
+ "#{@msg} - An event could not be immediately published, but may be retried: #{@event.stream_id}"
13
+ end
14
+ end
15
+
16
+ class MegaphoneMessageDelayWarning < StandardError
17
+
18
+ def initialize(msg, event)
19
+ super(msg)
20
+ @msg = msg
21
+ @event = event
22
+ end
23
+
24
+ def to_s
25
+ "#{@msg} - Event delayed and will be reattempted: #{@event.stream_id}"
13
26
  end
14
27
  end
15
28
  end
@@ -12,6 +12,10 @@ module Megaphone
12
12
  @payload = payload
13
13
  end
14
14
 
15
+ def stream_id
16
+ "#{@topic}.#{@subtopic}"
17
+ end
18
+
15
19
  def to_hash
16
20
  {
17
21
  schema: @schema,
@@ -6,12 +6,26 @@ module Megaphone
6
6
  class FluentLogger
7
7
  extend Forwardable
8
8
 
9
- def_delegators :@logger, :post, :last_error
9
+ def_delegators :@logger, :post, :last_error, :close
10
10
 
11
- def initialize(host, port)
11
+ def initialize(host, port, overflow_handler = nil)
12
+ overflow_handler ||= default_overflow_handler
12
13
  @logger = Fluent::Logger::FluentLogger.new('megaphone',
13
14
  host: host,
14
- port: port)
15
+ port: port,
16
+ buffer_overflow_handler: overflow_handler
17
+ )
18
+ end
19
+
20
+ private
21
+ # A default overflow handler that just prints a warning message.
22
+ # Production applications should be passing in their own handlers,
23
+ # which should be alerting monitoring systems!
24
+ def default_overflow_handler
25
+ $stderr.puts("Megaphone::Client::FluentLogger - Production apps MUST override buffer overflow handler!")
26
+ -> (*) {
27
+ $stderr.puts("Buffer overflow in Megaphone/fluent logger - messages lost")
28
+ }
15
29
  end
16
30
 
17
31
  end
@@ -4,10 +4,11 @@ require 'megaphone/client/fluent_logger'
4
4
  module Megaphone
5
5
  class Client
6
6
  class Logger
7
- def self.create(host = nil, port = nil)
7
+
8
+ def self.create(host = nil, port = nil, overflow_handler = nil)
8
9
  if !port.nil? && !port.empty? &&
9
10
  !host.nil? && !host.empty?
10
- return FluentLogger.new(host, port)
11
+ return FluentLogger.new(host, port, overflow_handler)
11
12
  end
12
13
  FileLogger.new
13
14
  end
@@ -1,5 +1,5 @@
1
1
  module Megaphone
2
2
  class Client
3
- VERSION = "1.0.1"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: megaphone-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Redbubble
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-09-18 00:00:00.000000000 Z
13
+ date: 2018-01-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: fluent-logger
@@ -94,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  version: '0'
95
95
  requirements: []
96
96
  rubyforge_project:
97
- rubygems_version: 2.6.13
97
+ rubygems_version: 2.4.5.1
98
98
  signing_key:
99
99
  specification_version: 4
100
100
  summary: Send events to Megaphone.