Add table to store stats versions

We need a way to manually expire the cache for a budget or poll without
expiring the cache of every budget or poll.

Using the `updated_at` column would be dangerous because most of the
times we update a budget or a poll, we don't need to regenerate their
stats.

We've considered adding a `stats_updated_at` column to each of these
tables. However, in that case we would also need to add a similar column
in the future to every process type whose stats we want to generate.
This commit is contained in:
Javi Martín
2019-04-08 16:22:23 +02:00
parent 9335c51cfc
commit 7c0e499eee
11 changed files with 79 additions and 2 deletions

View File

@@ -2,6 +2,7 @@ class Budget < ApplicationRecord
include Measurable
include Sluggable
include StatsVersionable
translates :name, touch: true
include Globalizable

View File

@@ -178,6 +178,6 @@ class Budget::Stats
stats_cache :voters, :participants, :authors, :balloters, :poll_ballot_voters
def stats_cache(key, &block)
Rails.cache.fetch("budgets_stats/#{budget.id}/#{phases.join}/#{key}/v10", &block)
Rails.cache.fetch("budgets_stats/#{budget.id}/#{phases.join}/#{key}/#{version}", &block)
end
end

View File

@@ -88,6 +88,10 @@ module Statisticable
PercentageCalculator.calculate(fraction, total)
end
def version
"v#{resource.find_or_create_stats_version.updated_at.to_i}"
end
private
def base_stats_methods

View File

@@ -0,0 +1,11 @@
module StatsVersionable
extend ActiveSupport::Concern
included do
has_one :stats_version, as: :process
end
def find_or_create_stats_version
stats_version || create_stats_version
end
end

View File

@@ -6,6 +6,7 @@ class Poll < ApplicationRecord
include ActsAsParanoidAliases
include Notifiable
include Sluggable
include StatsVersionable
translates :name, touch: true
translates :summary, touch: true

View File

@@ -107,7 +107,7 @@ class Poll::Stats
stats_cache :participants, :voters, :recounts
def stats_cache(key, &block)
Rails.cache.fetch("polls_stats/#{poll.id}/#{key}/v12", &block)
Rails.cache.fetch("polls_stats/#{poll.id}/#{key}/#{version}", &block)
end
end

View File

@@ -0,0 +1,5 @@
class StatsVersion < ApplicationRecord
validates :process, presence: true
belongs_to :process, polymorphic: true
end

View File

@@ -0,0 +1,8 @@
class CreateStatsVersions < ActiveRecord::Migration[4.2]
def change
create_table :stats_versions do |t|
t.references :process, polymorphic: true, index: true
t.timestamps null: false
end
end
end

View File

@@ -1339,6 +1339,14 @@ ActiveRecord::Schema.define(version: 20190411090023) do
t.index ["tsv"], name: "index_spending_proposals_on_tsv", using: :gin
end
create_table "stats_versions", force: :cascade do |t|
t.string "process_type"
t.integer "process_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["process_type", "process_id"], name: "index_stats_versions_on_process_type_and_process_id", using: :btree
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"

View File

@@ -231,4 +231,30 @@ describe Poll::Stats do
end
end
end
describe "#version", :with_frozen_time do
context "record with no stats" do
it "returns a string based on the current time" do
expect(stats.version).to eq "v#{Time.current.to_i}"
end
it "doesn't overwrite the timestamp when called multiple times" do
time = Time.current
expect(stats.version).to eq "v#{time.to_i}"
travel_to 2.seconds.from_now do
expect(stats.version).to eq "v#{time.to_i}"
end
end
end
context "record with stats" do
before { poll.create_stats_version(updated_at: 1.day.ago) }
it "returns the version of the existing stats" do
expect(stats.version).to eq "v#{1.day.ago.to_i}"
end
end
end
end

View File

@@ -0,0 +1,13 @@
require "rails_helper"
describe StatsVersion do
describe "validations" do
it "is valid with a process" do
expect(StatsVersion.new(process: Budget.new)).to be_valid
end
it "is not valid without a process" do
expect(StatsVersion.new(process: nil)).not_to be_valid
end
end
end