assaf / vanity

Experiment Driven Development for Ruby
http://vanity.labnotes.org
MIT License
1.55k stars 269 forks source link

Vanity metrics generating a row for each call to track #344

Open ninduni opened 6 years ago

ninduni commented 6 years ago

I'm not sure if this is an issue or me misinterpreting this section of the docs:

A Vanity::Metric object responds to track! and increments a record in the database (an O(1) operation). It creates one record for each day, accumulating that day’s count. When generating reports, the values method fetches the values of all these keys (also O(1)).

In practice, calling track!(:metric) creates a new row in the vanity_metric_values table for each call, such that we're seeing thousands of rows that are of the form

id vanity_metric_id index value date
123456 1 0 1 2018-04-24
123457 1 0 1 2018-04-24

I noticed this when we started failing to load the vanity dashboard, since the query required to populate the metrics graph was timing out.

phillbaker commented 6 years ago

Hi @ninduni thanks for opening an issue. What version of vanity, ruby framework and datastore are you using?

Can you post a simple experiment/metric config to reproduce this?

ninduni commented 6 years ago

Ruby 2.5.1 Rails 4.2.9 PostgreSQL 9.6.6 Vanity 2.2.2

We have a simple wrapper around Vanity defined in experiments_service.rb:

class ExperimentsService
  def show_special_page(user:)
    set_vanity_context(user: user)
    Vanity.ab_test(:special_page)
  end

  def track_user_visit!(user:)
    track_conversion!(metric: :visit, user: user)
  end

  private

  def set_vanity_context(user:)
    Vanity.context = Struct.new(:vanity_identity).new(user.id)
  end

  def track_conversion!(metric:, user:)
    set_vanity_context(user: user)
    Vanity.track!(metric)
  end
end

And then in experiments/metrics/visit.rb:

metric 'Visits' do
  description 'Measures how many times the user visited a certain page'
end

And then let's say the controller tracks whether a user clicked just by seeing if an endpoint was visited:

class BasicPage
  def index
    ...
    @display_special_page_link = ExperimentsService.new.show_special_page(user: current_user)
    ...
  end
end
class SpecialPageController
  def index
    ...
    ExperimentsService.new.track_user_visit!(user: current_user)
    ...
  end
end

Let's say Visits has a vanity_metric_id of 1, then I think I would expect to see one vanity_metric_values row per day for vanity_metric_id = 1, with a value that indicates how many users have seen the special page on that day.