Skip to content

Commit

Permalink
Add Rake task and Resque job to delete old auto snapshots
Browse files Browse the repository at this point in the history
Prevent database from growing too much.
  • Loading branch information
tf committed Sep 7, 2017
1 parent 948dc17 commit ad755ff
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 4 deletions.
9 changes: 9 additions & 0 deletions app/jobs/pageflow/prune_auto_snapshots_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Pageflow
class PruneAutoSnapshotsJob
@queue = :prune

def self.perform(entry_id, options)
AutoSnapshotPruning.prune(Entry.find(entry_id), options.symbolize_keys)
end
end
end
26 changes: 26 additions & 0 deletions app/models/pageflow/auto_snapshot_pruning.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Pageflow
# @api private
module AutoSnapshotPruning
extend self

def dirty_entry_ids(options)
Revision
.auto_snapshots
.where('created_at < ?', options.fetch(:created_before))
.select('COUNT(*) as revisions_count, entry_id')
.group('entry_id')
.having('revisions_count > ?', options.fetch(:keep_count))
.map(&:entry_id)
end

def prune(entry, options)
entry
.revisions
.auto_snapshots
.where('created_at < ?', options[:created_before])
.order('created_at DESC')
.offset(options[:keep_count])
.each(&:destroy)
end
end
end
2 changes: 1 addition & 1 deletion app/models/pageflow/chapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class Chapter < ActiveRecord::Base
include SerializedConfiguration

belongs_to :storyline, touch: true
has_many :pages, -> { order('position ASC') }
has_many :pages, -> { order('position ASC') }, dependent: :destroy

delegate :entry, to: :storyline

Expand Down
7 changes: 5 additions & 2 deletions app/models/pageflow/revision.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ class Revision < ActiveRecord::Base
belongs_to :restored_from, :class_name => 'Pageflow::Revision'

has_many :widgets, as: :subject, dependent: :destroy
has_many :storylines, -> { order('pageflow_storylines.position ASC') }

has_many :storylines, -> { order('pageflow_storylines.position ASC') }, dependent: :destroy
has_many :chapters, -> { order('position ASC') }, through: :storylines
has_many :pages, -> { reorder(PAGE_ORDER) }, through: :storylines

has_many :file_usages
has_many :file_usages, dependent: :destroy

has_many :image_files, -> { extending WithFileUsageExtension },
:through => :file_usages, :source => :file, :source_type => 'Pageflow::ImageFile'
Expand All @@ -39,6 +40,8 @@ class Revision < ActiveRecord::Base

scope :publications, -> { where('published_at IS NOT NULL') }
scope :publications_and_user_snapshots, -> { where('published_at IS NOT NULL OR snapshot_type = "user"') }
scope :user_snapshots, -> { where('snapshot_type = "user"') }
scope :auto_snapshots, -> { where('snapshot_type = "auto"') }

validates :entry, :presence => true
validates :creator, :presence => true, :if => :published?
Expand Down
2 changes: 1 addition & 1 deletion app/models/pageflow/storyline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Storyline < ActiveRecord::Base

belongs_to :revision, touch: true

has_many :chapters, -> { order('pageflow_chapters.position ASC') }
has_many :chapters, -> { order('pageflow_chapters.position ASC') }, dependent: :destroy
has_many :pages, through: :chapters

delegate :entry, to: :revision
Expand Down
14 changes: 14 additions & 0 deletions lib/tasks/pageflow_tasks.rake
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,18 @@ namespace :pageflow do
system('bin/npm run build')
end
end

namespace :prune_auto_snapshots_jobs do
desc 'Enqueue jobs to destroy old auto snapshot revisions'
task :enqueue => :environment do
options = {
created_before: ENV['CREATED_BEFORE'] ? Time.parse(ENV['CREATED_BEFORE']) : 1.month.ago,
keep_count: ENV.fetch('KEEP_COUNT', 20)
}

Pageflow::AutoSnapshotPruning.dirty_entry_ids(options).each do |entry_id|
Resque.enqueue(Pageflow::PruneAutoSnapshotsJob, entry_id, options)
end
end
end
end
18 changes: 18 additions & 0 deletions spec/jobs/pageflow/prune_auto_snapshots_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'spec_helper'

module Pageflow
describe PruneAutoSnapshotsJob do
it 'destroys old auto snapshots' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
3.times { entry.snapshot(creator: user) }
end

PruneAutoSnapshotsJob.perform(entry.id, keep_count: 2, created_before: 1.month.ago)

expect(entry.reload.revisions.auto_snapshots.count).to eq(2)
end
end
end
155 changes: 155 additions & 0 deletions spec/models/pageflow/auto_snapshot_pruning_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
require 'spec_helper'

module Pageflow
describe AutoSnapshotPruning do
describe '.dirty_entry_ids' do
it 'includes entries with more than keep_count auto snapshots created before given time' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
5.times { entry.snapshot(creator: user) }
end

result = AutoSnapshotPruning.dirty_entry_ids(keep_count: 4, created_before: 1.month.ago)

expect(result).to include(entry.id)
end

it 'does not includes entries with less than keep_count auto snapshots' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
2.times { entry.snapshot(creator: user) }
end

result = AutoSnapshotPruning.dirty_entry_ids(keep_count: 4, created_before: 1.month.ago)

expect(result).not_to include(entry.id)
end

it 'does not includes entries with auto snapshots created after given date' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(1.day.ago) do
3.times { entry.snapshot(creator: user) }
end

result = AutoSnapshotPruning.dirty_entry_ids(keep_count: 2, created_before: 1.month.ago)

expect(result).not_to include(entry.id)
end

it 'does not includes entries with more than keep_count user snapshots' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
3.times { entry.snapshot(creator: user, type: 'user') }
end

result = AutoSnapshotPruning.dirty_entry_ids(keep_count: 2, created_before: 1.month.ago)

expect(result).not_to include(entry.id)
end

it 'does not includes entries with more than keep_count publications' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
3.times { entry.publish(creator: user) }
end

result = AutoSnapshotPruning.dirty_entry_ids(keep_count: 2, created_before: 1.month.ago)

expect(result).not_to include(entry.id)
end
end

describe '.prune' do
it 'destroy all but keep_count auto snapshots' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
3.times { entry.snapshot(creator: user) }
end

AutoSnapshotPruning.prune(entry, keep_count: 2, created_before: 1.month.ago)

expect(entry.revisions.auto_snapshots.count).to eq(2)
end

it 'triggers dependent destroys down to pages' do
user = create(:user)
entry = create(:entry)
chapter = create(:chapter, storyline: entry.draft.storylines.first)
create(:page, chapter: chapter)

Timecop.freeze(2.month.ago) do
3.times { entry.snapshot(creator: user) }
end

expect {
AutoSnapshotPruning.prune(entry, keep_count: 2, created_before: 1.month.ago)
}.to change { Pageflow::Page.count }
end

it 'destroys oldest auto snapshots first' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(4.month.ago) { entry.snapshot(creator: user) }
Timecop.freeze(3.month.ago) { entry.snapshot(creator: user) }
Timecop.freeze(2.month.ago) { entry.snapshot(creator: user) }

AutoSnapshotPruning.prune(entry, keep_count: 2, created_before: 1.month.ago)
timestamps = entry.revisions.auto_snapshots.map(&:created_at)

expect(timestamps).to match_array([2.month.ago, 3.month.ago])
end

it 'ignores auto snapshots created after given data' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(4.month.ago) { entry.snapshot(creator: user) }
Timecop.freeze(3.days.ago) { entry.snapshot(creator: user) }
Timecop.freeze(2.days.ago) { entry.snapshot(creator: user) }

AutoSnapshotPruning.prune(entry, keep_count: 2, created_before: 1.month.ago)

expect(entry.revisions.auto_snapshots.count).to eq(3)
end

it 'ignores user snapshots' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
3.times { entry.snapshot(creator: user, type: 'user') }
end

AutoSnapshotPruning.prune(entry, keep_count: 2, created_before: 1.month.ago)

expect(entry.revisions.user_snapshots.count).to eq(3)
end

it 'ignores publications' do
user = create(:user)
entry = create(:entry)

Timecop.freeze(2.month.ago) do
3.times { entry.publish(creator: user) }
end

AutoSnapshotPruning.prune(entry, keep_count: 2, created_before: 1.month.ago)

expect(entry.revisions.publications.count).to eq(3)
end
end
end
end

0 comments on commit ad755ff

Please sign in to comment.