Skip to content

Commit

Permalink
Merge pull request #861 from tf/prune-auto-snapshots
Browse files Browse the repository at this point in the history
Add Rake task and Resque job to delete old auto snapshots
  • Loading branch information
tf authored Sep 7, 2017
2 parents 5a7d7d7 + ad755ff commit d8c55fa
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 d8c55fa

Please sign in to comment.