Skip to content

Commit

Permalink
fix: Cache invalidation on subject creation/deletion
Browse files Browse the repository at this point in the history
Also add unit tests for this
  • Loading branch information
mromulus committed Dec 5, 2023
1 parent 2593b00 commit 77d5c38
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 19 deletions.
12 changes: 9 additions & 3 deletions app/models/concerns/spec_cache_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ module SpecCacheUpdater
extend ActiveSupport::Concern

included do
before_destroy :schedule_spec_cache_update
after_save :schedule_spec_cache_update
before_destroy :update_service_subject_spec_cache
around_save :update_service_subject_spec_cache_with_yield
end

private
def schedule_spec_cache_update(item_hint = nil)
def update_service_subject_spec_cache_with_yield
yield
update_service_subject_spec_cache
end

def update_service_subject_spec_cache(item_hint = nil)
ServiceSubject.transaction do
[self, item_hint].compact.flat_map(&method(:gather_subjects)).uniq.each do |subject|
next if subject.customization_spec_ids == subject.matched_spec_ids
subject.update_columns(
customization_spec_ids: subject.matched_spec_ids,
updated_at: Time.current
Expand Down
38 changes: 25 additions & 13 deletions app/models/concerns/vm_cache_buster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@ module VmCacheBuster
extend ActiveSupport::Concern

included do
before_destroy :update_specs
after_save :update_specs
around_destroy :update_specs_before
around_save :update_specs_before
end

private
def update_specs
l_service = case self
when Service
self
else
service
end
exercise
.customization_specs
.where(id: l_service.cached_spec_ids)
.touch_all
def update_specs_before
spec_ids = Set.new
spec_ids.merge(get_service.cached_spec_ids)
yield
spec_ids.merge(get_service.cached_spec_ids)
update_specs(spec_ids)
end

def update_specs_after
update_specs(get_service.cached_spec_ids)
end

def update_specs(ids)
exercise.customization_specs.where(id: ids).touch_all
end

def get_service
case self
when Service
self
else
service
end.tap { |s| s.reload if s.persisted? }
end
end
1 change: 1 addition & 0 deletions app/models/service_subject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'service_subject_match_condition'

class ServiceSubject < ApplicationRecord
include VmCacheBuster
include SpecCacheUpdater

has_paper_trail
Expand Down
15 changes: 12 additions & 3 deletions spec/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

factory :customization_spec do
virtual_machine
name { 'MyString' }
mode { 'host' }
sequence(:name) { |n| "CoolSpec#{n}" }
role_name { 'MyString' }
dns_name { 'MyString' }
description { 'MyText' }
end

Expand Down Expand Up @@ -44,7 +44,7 @@
end

factory :virtual_machine do
name { 'CoolTarget' }
sequence(:name) { |n| "CoolTarget#{n}" }
actor
operating_system
exercise
Expand Down Expand Up @@ -91,4 +91,13 @@
factory :api_token do
user
end

factory :service do
exercise
name { 'Some service' }
end

factory :service_subject do
service
end
end
82 changes: 82 additions & 0 deletions spec/models/service_subject_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe ServiceSubject do

context 'spec cache' do
subject { create(:service_subject, service:) }
let(:service) { create(:service) }
let(:virtual_machine) { create(:virtual_machine, exercise: service.exercise) }

it 'should set correct cache when matcher condition added' do
service.service_subjects.reload
subject.match_conditions = [ServiceSubjectMatchCondition.new(
matcher_type: 'CustomizationSpec', matcher_id: virtual_machine.host_spec.id
)]
subject.save
subject.match_conditions[0].matcher_id = virtual_machine.host_spec.id
subject.save
subject.reload

expect(subject.customization_spec_ids).to eq ([virtual_machine.host_spec.id])
end

context 'with a pre-existing match condition' do
subject {
create(:service_subject, service:, match_conditions: [ServiceSubjectMatchCondition.new(
matcher_type: 'CustomizationSpec', matcher_id: virtual_machine.host_spec.id
)])
}

let(:virtual_machine2) { create(:virtual_machine, exercise: service.exercise) }

it 'should update cache when changing matcher id' do
subject.match_conditions[0].matcher_id = virtual_machine2.host_spec.id
subject.save

expect(subject.customization_spec_ids).to eq ([virtual_machine2.host_spec.id])
end

it 'should update cache key for previous vms when changing matcher id' do
subject.match_conditions[0].matcher_id = virtual_machine2.host_spec.id

expect {
subject.save
virtual_machine.host_spec.reload
}.to change(virtual_machine.host_spec, :cache_key_with_version)
end

it 'should update cache key for following vms when changing matcher id' do
subject.match_conditions[0].matcher_id = virtual_machine2.host_spec.id

expect {
subject.save
virtual_machine2.host_spec.reload
}.to change(virtual_machine2.host_spec, :cache_key_with_version)
end

it 'should empty cached ids when deleting matcher' do
subject.match_conditions[0].matcher_id = nil
subject.save
expect(subject.customization_spec_ids).to eq ([])
end

it 'should update cache key for previous vms when deleting matcher id' do
subject.match_conditions[0].matcher_id = nil

expect {
subject.save
virtual_machine.host_spec.reload
}.to change(virtual_machine.host_spec, :cache_key_with_version)
end

it 'should update cache key for previous vms when deleting subject' do
expect {
subject.destroy
virtual_machine.host_spec.reload
}.to change(virtual_machine.host_spec, :cache_key_with_version)
end
end
end
end

0 comments on commit 77d5c38

Please sign in to comment.