tpitale/staccato

Default adapter doesn't raise on error?

Closed this issue · 7 comments

thbar commented

While writing a test for an app, I realised that:

stub_request(:post, "https://ssl.google-analytics.com/collect").to_return(status: 500, body: "", headers: {})

would not raise an error.

What would be the best strategy to get an error (to e.g. retry in sidekiq) in case the POST failed? (maybe using a custom adapter?)

thbar commented

Ok I see that writing an adapter is quite easy, so I'll do that.

I wonder if you'd welcome a doc patch to warn that track! won't raise by default?

Yeah, staccato is a "fire-and-forget" style by default. This mirrors tools that interface with statsd, etc. We didn't want to interrupt an application if google is down, or there is a network issue.

Always happy to take a look at PRs that clarify, thank you!

I'd also be open to making something like this configurable.

Strange though, in reviewing the code, I'm not seeing anywhere that we catch errors. I assume they would just bubble up from the adapter.

What was the adapter you were using? The default net/http? I think that may just return a response with a 500, not raise an error. You might be able to check that response and raise yourself.

thbar commented

Fire-and-forget is good usually for that indeed! Here I'm working in a background job, so I'll prefer to retry later.

Indeed I'm using the default net/http adapter, which won't raise for a 500 (but will for plenty of other cases https://stackoverflow.com/a/5370726/20302).

I'll either handle the response, or will write a quick custom adapter.

Thanks for the quick reply!

@thbar In further thinking about this … I would love to add an adapter for like actionjob or something. I think it could be pretty plug-and-play. Are you using Rails and/or ActiveJob?

thbar commented

@tpitale it's a Rails app. In my case though, I prefer to use Sidekiq than ActiveJob!

FWIW, here is what I came up with, because post_form does not return the HTTP response:

require 'httparty'

module Custom
  module Staccato
    class HTTPartyAdapter
      attr_reader :uri

      def initialize(uri)
        @uri = uri
      end

      def post(data)
        HTTParty.post(uri, body: data)
      end
    end
  end
end

Then later in my Sidekiq job, I do:

class GoogleAnalyticsWorker
  include Sidekiq::Worker
  sidekiq_options retry: false

  def perform(options)
    hit = Staccato::Event.new(build_tracker,
      action: options.fetch('action'),
      category: options.fetch('category'),
      document_hostname: options.fetch('document_hostname'),
      data_source: options.fetch('data_source')
    )
    hit.add_custom_dimension(1, default_value(options.fetch('foobar_1')))
    hit.add_custom_dimension(2, default_value(options.fetch('foobar_2')))
    hit.add_custom_dimension(3, default_value(options.fetch('foobar_3')))

    response = hit.track!
    return if response.success?
    raise "Failed to send event to Google Analytics (HTTP #{response.code})"
  end

  # record with a blank dimension value won't be counted into general reports
  def default_value(value)
    value.blank? ? 'not provided' : value
  end

  def build_tracker
    Staccato.tracker(ENV.fetch('GA_PROPERTY_ID'), nil, ssl: true) do |c|
      c.adapter = Custom::Staccato::HTTPartyAdapter.new(Staccato.ga_collection_uri(true))
    end
  end
end

This makes sure that whenever the GA response is not in the 2XX range, I'll get an exception.

Interesting, I have been away from the ruby/rails world too long. It seems ActiveJob isn't as popular as just using the background job of your choice. Unnecessary abstraction.