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:
@@ -2,6 +2,7 @@ class Budget < ApplicationRecord
|
||||
|
||||
include Measurable
|
||||
include Sluggable
|
||||
include StatsVersionable
|
||||
|
||||
translates :name, touch: true
|
||||
include Globalizable
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
11
app/models/concerns/stats_versionable.rb
Normal file
11
app/models/concerns/stats_versionable.rb
Normal 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
|
||||
@@ -6,6 +6,7 @@ class Poll < ApplicationRecord
|
||||
include ActsAsParanoidAliases
|
||||
include Notifiable
|
||||
include Sluggable
|
||||
include StatsVersionable
|
||||
|
||||
translates :name, touch: true
|
||||
translates :summary, touch: true
|
||||
|
||||
@@ -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
|
||||
|
||||
5
app/models/stats_version.rb
Normal file
5
app/models/stats_version.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class StatsVersion < ApplicationRecord
|
||||
validates :process, presence: true
|
||||
|
||||
belongs_to :process, polymorphic: true
|
||||
end
|
||||
8
db/migrate/20190408133956_create_stats_versions.rb
Normal file
8
db/migrate/20190408133956_create_stats_versions.rb
Normal 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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
13
spec/models/stats_version_spec.rb
Normal file
13
spec/models/stats_version_spec.rb
Normal 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
|
||||
Reference in New Issue
Block a user