Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit eafa07e

Browse files
committedJan 14, 2025·
Keep Usage Event records corresponding to running and unprocessed Apps & Services
1 parent 91604fc commit eafa07e

21 files changed

+406
-17
lines changed
 
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module VCAP::CloudController
2+
class AppUsageConsumerAccess < BaseAccess
3+
def delete?(_app_usage_consumer)
4+
true
5+
end
6+
end
7+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module VCAP::CloudController
2+
class ServiceUsageConsumerAccess < BaseAccess
3+
def delete?(_service_usage_consumer)
4+
true
5+
end
6+
end
7+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class AppUsageConsumersController < ApplicationController
2+
def destroy
3+
app_usage_consumer = AppUsageConsumer.find(consumer_guid: hashed_params[:guid])
4+
5+
resource_not_found!(:app_usage_consumer) unless app_usage_consumer
6+
7+
app_usage_consumer_access = VCAP::CloudController::AppUsageConsumerAccess.new(context: SecurityContext)
8+
unauthorized! unless app_usage_consumer_access.delete?(app_usage_consumer)
9+
10+
app_usage_consumer.destroy
11+
12+
head :no_content
13+
end
14+
end

‎app/controllers/v3/app_usage_events_controller.rb

+14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ def index
1111

1212
app_usage_events = AppUsageEventListFetcher.fetch_all(message, AppUsageEvent.dataset) if permission_queryer.can_read_globally?
1313

14+
if message.consumer_guid && message.after_guid&.first && permission_queryer.can_read_globally?
15+
begin
16+
consumer = AppUsageConsumer.find_or_create(consumer_guid: message.consumer_guid) do |c|
17+
c.last_processed_guid = message.after_guid.first
18+
end
19+
20+
consumer.update(last_processed_guid: message.after_guid.first) if !consumer.new? && consumer.last_processed_guid != message.after_guid.first
21+
rescue Sequel::ValidationFailed => e
22+
unprocessable!(e.message)
23+
rescue Sequel::Error
24+
error!('Failed to update consumer tracking', 500)
25+
end
26+
end
27+
1428
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
1529
presenter: Presenters::V3::AppUsageEventPresenter,
1630
paginated_result: SequelPaginator.new.get_page(app_usage_events, message.try(:pagination_options)),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class ServiceUsageConsumersController < ApplicationController
2+
def destroy
3+
service_usage_consumer = ServiceUsageConsumer.find(consumer_guid: hashed_params[:guid])
4+
5+
resource_not_found!(:service_usage_consumer) unless service_usage_consumer
6+
7+
service_usage_consumer_access = VCAP::CloudController::ServiceUsageConsumerAccess.new(context: SecurityContext)
8+
unauthorized! unless service_usage_consumer_access.delete?(service_usage_consumer)
9+
10+
service_usage_consumer.destroy
11+
12+
head :no_content
13+
end
14+
end

‎app/controllers/v3/service_usage_events_controller.rb

+14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ def index
1111

1212
service_usage_events = ServiceUsageEventListFetcher.fetch_all(message, ServiceUsageEvent.dataset) if permission_queryer.can_read_globally?
1313

14+
if message.consumer_guid && message.after_guid&.first && permission_queryer.can_read_globally?
15+
begin
16+
consumer = ServiceUsageConsumer.find_or_create(consumer_guid: message.consumer_guid) do |c|
17+
c.last_processed_guid = message.after_guid.first
18+
end
19+
20+
consumer.update(last_processed_guid: message.after_guid.first) if !consumer.new? && consumer.last_processed_guid != message.after_guid.first
21+
rescue Sequel::ValidationFailed => e
22+
unprocessable!(e.message)
23+
rescue Sequel::Error
24+
error!('Failed to update consumer tracking', 500)
25+
end
26+
end
27+
1428
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
1529
presenter: Presenters::V3::ServiceUsageEventPresenter,
1630
paginated_result: SequelPaginator.new.get_page(service_usage_events, message.try(:pagination_options)),

‎app/messages/app_usage_events_list_message.rb

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
module VCAP::CloudController
44
class AppUsageEventsListMessage < ListMessage
5-
register_allowed_keys [
6-
:after_guid
5+
register_allowed_keys %i[
6+
after_guid
7+
consumer_guid
78
]
89

910
validates_with NoAdditionalParamsValidator
@@ -19,7 +20,7 @@ def valid_order_by_values
1920
end
2021

2122
def self.from_params(params)
22-
super(params, %w[after_guid])
23+
super(params, %w[after_guid consumer_guid])
2324
end
2425
end
2526
end

‎app/messages/service_usage_events_list_message.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class ServiceUsageEventsListMessage < ListMessage
66
after_guid
77
service_instance_types
88
service_offering_guids
9+
consumer_guid
910
]
1011

1112
validates_with NoAdditionalParamsValidator
@@ -24,7 +25,7 @@ def valid_order_by_values
2425
end
2526

2627
def self.from_params(params)
27-
super(params, %w[after_guid service_instance_types service_offering_guids])
28+
super(params, %w[after_guid service_instance_types service_offering_guids consumer_guid])
2829
end
2930
end
3031
end

‎app/models.rb

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
require 'models/runtime/security_group'
1919
require 'models/runtime/security_groups_space'
2020
require 'models/runtime/staging_security_groups_space'
21+
require 'models/runtime/app_usage_consumer'
2122
require 'models/runtime/app_usage_event'
2223
require 'models/runtime/auto_detection_buildpack'
2324
require 'models/runtime/app_event'
@@ -140,6 +141,7 @@
140141
require 'models/services/service_plan_visibility'
141142
require 'models/services/service_plan_annotation_model'
142143
require 'models/services/service_plan_label_model'
144+
require 'models/services/service_usage_consumer'
143145
require 'models/services/service_usage_event'
144146
require 'models/services/service_key'
145147
require 'models/services/service_key_label_model'
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module VCAP::CloudController
2+
class AppUsageConsumer < Sequel::Model
3+
plugin :validation_helpers
4+
5+
many_to_one :last_processed_event,
6+
class: 'VCAP::CloudController::AppUsageEvent',
7+
key: :last_processed_guid,
8+
primary_key: :guid
9+
10+
def validate
11+
validates_presence %i[consumer_guid last_processed_guid]
12+
validates_unique :consumer_guid
13+
validates_max_length 255, :consumer_guid, message: 'must be less than 255 characters'
14+
end
15+
16+
def last_processed_guid=(guid)
17+
self[:last_processed_guid] = guid
18+
end
19+
20+
def last_processed_guid
21+
self[:last_processed_guid]
22+
end
23+
24+
export_attributes :consumer_guid, :last_processed_guid
25+
end
26+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module VCAP::CloudController
2+
class ServiceUsageConsumer < Sequel::Model
3+
plugin :validation_helpers
4+
5+
many_to_one :last_processed_event,
6+
class: 'VCAP::CloudController::ServiceUsageEvent',
7+
key: :last_processed_guid,
8+
primary_key: :guid
9+
10+
def validate
11+
validates_presence %i[consumer_guid last_processed_guid]
12+
validates_unique :consumer_guid
13+
validates_max_length 255, :consumer_guid, message: 'must be less than 255 characters'
14+
end
15+
16+
def last_processed_guid=(guid)
17+
self[:last_processed_guid] = guid
18+
end
19+
20+
def last_processed_guid
21+
self[:last_processed_guid]
22+
end
23+
24+
export_attributes :consumer_guid, :last_processed_guid
25+
end
26+
end

‎app/repositories/app_usage_event_repository.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def purge_and_reseed_started_apps!
152152
end
153153

154154
def delete_events_older_than(cutoff_age_in_days)
155-
Database::OldRecordCleanup.new(AppUsageEvent, cutoff_age_in_days, keep_at_least_one_record: true).delete
155+
Database::OldRecordCleanup.new(AppUsageEvent, cutoff_age_in_days, keep_at_least_one_record: true, keep_running_records: true, keep_unprocessed_records: true).delete
156156
end
157157

158158
private

‎app/repositories/service_usage_event_repository.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def purge_and_reseed_service_instances!
9292
end
9393

9494
def delete_events_older_than(cutoff_age_in_days)
95-
Database::OldRecordCleanup.new(ServiceUsageEvent, cutoff_age_in_days, keep_at_least_one_record: true).delete
95+
Database::OldRecordCleanup.new(ServiceUsageEvent, cutoff_age_in_days, keep_at_least_one_record: true, keep_running_records: true, keep_unprocessed_records: true).delete
9696
end
9797
end
9898
end

‎config/routes.rb

+6
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,12 @@
332332
get '/service_usage_events', to: 'service_usage_events#index'
333333
post '/service_usage_events/actions/destructively_purge_all_and_reseed', to: 'service_usage_events#destructively_purge_all_and_reseed'
334334

335+
# app usage consumers
336+
delete '/app_usage_consumers/:guid', to: 'app_usage_consumers#destroy'
337+
338+
# app usage consumers
339+
delete '/service_usage_consumers/:guid', to: 'service_usage_consumers#destroy'
340+
335341
# environment variable groups
336342
get '/environment_variable_groups/:name', to: 'environment_variable_groups#show'
337343
patch '/environment_variable_groups/:name', to: 'environment_variable_groups#update'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Sequel.migration do
2+
change do
3+
create_table :app_usage_consumers do |_t|
4+
VCAP::Migration.common(self)
5+
String :consumer_guid, null: false, size: 255
6+
String :last_processed_guid, null: false, size: 255
7+
end
8+
end
9+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Sequel.migration do
2+
change do
3+
create_table :service_usage_consumers do |_t|
4+
VCAP::Migration.common(self)
5+
String :consumer_guid, null: false, size: 255
6+
String :last_processed_guid, null: false, size: 255
7+
end
8+
end
9+
end

‎lib/database/old_record_cleanup.rb

+83-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
module Database
44
class OldRecordCleanup
55
class NoCurrentTimestampError < StandardError; end
6-
attr_reader :model, :days_ago, :keep_at_least_one_record
6+
attr_reader :model, :days_ago, :keep_at_least_one_record, :keep_running_records, :keep_unprocessed_records
77

8-
def initialize(model, days_ago, keep_at_least_one_record: false)
8+
def initialize(model, days_ago, keep_at_least_one_record: false, keep_running_records: false, keep_unprocessed_records: false)
99
@model = model
1010
@days_ago = days_ago
1111
@keep_at_least_one_record = keep_at_least_one_record
12+
@keep_running_records = keep_running_records
13+
@keep_unprocessed_records = keep_unprocessed_records
1214
end
1315

1416
def delete
@@ -19,6 +21,10 @@ def delete
1921
last_record = model.order(:id).last
2022
old_records = old_records.where(Sequel.lit('id < ?', last_record.id)) if last_record
2123
end
24+
25+
old_records = exclude_running_records(old_records) if keep_running_records
26+
old_records = exclude_unprocessed_records(old_records) if keep_unprocessed_records
27+
2228
logger.info("Cleaning up #{old_records.count} #{model.table_name} table rows")
2329

2430
Database::BatchDelete.new(old_records, 1000).delete
@@ -35,5 +41,80 @@ def current_timestamp_from_database
3541
def logger
3642
@logger ||= Steno.logger('cc.old_record_cleanup')
3743
end
44+
45+
def exclude_running_records(old_records)
46+
return old_records unless has_duration?(model)
47+
48+
beginning_string = beginning_string(model)
49+
ending_string = ending_string(model)
50+
guid_symbol = guid_symbol(model)
51+
52+
raise "Invalid duration model: #{model}" if beginning_string.nil? || ending_string.nil? || guid_symbol.nil?
53+
54+
initial_records = old_records.where(state: beginning_string).from_self(alias: :initial_records)
55+
final_records = old_records.where(state: ending_string).from_self(alias: :final_records)
56+
57+
exists_condition = final_records.where(Sequel[:final_records][guid_symbol] => Sequel[:initial_records][guid_symbol]).where do
58+
Sequel[:final_records][:id] > Sequel[:initial_records][:id]
59+
end.select(1).exists
60+
61+
prunable_initial_records = initial_records.where(exists_condition)
62+
other_records = old_records.exclude(state: [beginning_string, ending_string])
63+
64+
prunable_initial_records.union(final_records, all: true).union(other_records, all: true)
65+
end
66+
67+
def has_duration?(model)
68+
return true if model == VCAP::CloudController::AppUsageEvent
69+
return true if model == VCAP::CloudController::ServiceUsageEvent
70+
71+
false
72+
end
73+
74+
def beginning_string(model)
75+
return VCAP::CloudController::ProcessModel::STARTED if model == VCAP::CloudController::AppUsageEvent
76+
return VCAP::CloudController::Repositories::ServiceUsageEventRepository::CREATED_EVENT_STATE if model == VCAP::CloudController::ServiceUsageEvent
77+
78+
nil
79+
end
80+
81+
def ending_string(model)
82+
return VCAP::CloudController::ProcessModel::STOPPED if model == VCAP::CloudController::AppUsageEvent
83+
return VCAP::CloudController::Repositories::ServiceUsageEventRepository::DELETED_EVENT_STATE if model == VCAP::CloudController::ServiceUsageEvent
84+
85+
nil
86+
end
87+
88+
def guid_symbol(model)
89+
return :app_guid if model == VCAP::CloudController::AppUsageEvent
90+
return :service_instance_guid if model == VCAP::CloudController::ServiceUsageEvent
91+
92+
nil
93+
end
94+
95+
def exclude_unprocessed_records(old_records)
96+
consumer_model = registered_consumer_model(model)
97+
98+
return old_records unless consumer_model
99+
100+
# Find the usage event record with the lowest ID
101+
# of any that are referenced by a consumer
102+
referenced_event = model.
103+
join(consumer_model.table_name.to_sym, last_processed_guid: :guid).
104+
select(Sequel.function(:min, Sequel.qualify(model.table_name, :id)).as(:min_id)).
105+
first
106+
107+
return old_records if referenced_event[:min_id].nil?
108+
109+
old_records.where { id < referenced_event[:min_id] }
110+
end
111+
112+
def registered_consumer_model(model)
113+
return VCAP::CloudController::AppUsageConsumer if model == VCAP::CloudController::AppUsageEvent && VCAP::CloudController::AppUsageConsumer.present?
114+
115+
return VCAP::CloudController::ServiceUsageConsumer if model == VCAP::CloudController::ServiceUsageEvent && VCAP::CloudController::ServiceUsageConsumer.present?
116+
117+
false
118+
end
38119
end
39120
end

‎spec/support/fakes/blueprints.rb

+8
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,14 @@ module VCAP::CloudController
732732
process_type { 'web' }
733733
end
734734

735+
AppUsageConsumer.blueprint do
736+
consumer_guid { Sham.guid }
737+
end
738+
739+
ServiceUsageConsumer.blueprint do
740+
consumer_guid { Sham.guid }
741+
end
742+
735743
ServiceUsageEvent.blueprint do
736744
state { 'CREATED' }
737745
org_guid { Sham.guid }

‎spec/unit/jobs/runtime/app_usage_events_cleanup_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Jobs::Runtime
55
RSpec.describe AppUsageEventsCleanup, job_context: :worker do
66
let(:cutoff_age_in_days) { 30 }
77
let(:logger) { double(Steno::Logger, info: nil) }
8-
let!(:event_before_threshold) { AppUsageEvent.make(created_at: (cutoff_age_in_days + 1).days.ago) }
8+
let!(:event_before_threshold) { AppUsageEvent.make(created_at: (cutoff_age_in_days + 1).days.ago, state: 'STOPPED') }
99
let!(:event_after_threshold) { AppUsageEvent.make(created_at: (cutoff_age_in_days - 1).days.ago) }
1010

1111
subject(:job) do

‎spec/unit/jobs/services/service_usage_events_cleanup_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Jobs::Services
55
RSpec.describe ServiceUsageEventsCleanup, job_context: :worker do
66
let(:cutoff_age_in_days) { 30 }
77
let(:logger) { double(Steno::Logger, info: nil) }
8-
let!(:event_before_threshold) { ServiceUsageEvent.make(created_at: (cutoff_age_in_days + 1).days.ago) }
8+
let!(:event_before_threshold) { ServiceUsageEvent.make(created_at: (cutoff_age_in_days + 1).days.ago, state: 'DELETED') }
99
let!(:event_after_threshold) { ServiceUsageEvent.make(created_at: (cutoff_age_in_days - 1).days.ago) }
1010

1111
subject(:job) do

‎spec/unit/lib/database/old_record_cleanup_spec.rb

+157-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33

44
RSpec.describe Database::OldRecordCleanup do
55
describe '#delete' do
6-
let!(:stale_event1) { VCAP::CloudController::Event.make(created_at: 1.day.ago - 1.minute) }
7-
let!(:stale_event2) { VCAP::CloudController::Event.make(created_at: 2.days.ago) }
6+
it 'deletes records older than specified days' do
7+
stale_event1 = VCAP::CloudController::Event.make(created_at: 1.day.ago - 1.minute)
8+
stale_event2 = VCAP::CloudController::Event.make(created_at: 2.days.ago)
89

9-
let!(:fresh_event) { VCAP::CloudController::Event.make(created_at: 1.day.ago + 1.minute) }
10+
fresh_event = VCAP::CloudController::Event.make(created_at: 1.day.ago + 1.minute)
1011

11-
it 'deletes records older than specified days' do
1212
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::Event, 1)
1313

1414
expect do
@@ -22,10 +22,11 @@
2222

2323
context "when there are no records at all but you're trying to keep at least one" do
2424
it "doesn't keep one because there aren't any to keep" do
25-
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: true)
25+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppEvent, 1, keep_at_least_one_record: true, keep_running_records: true,
26+
keep_unprocessed_records: false)
2627

2728
expect { record_cleanup.delete }.not_to raise_error
28-
expect(VCAP::CloudController::ServiceUsageEvent.count).to eq(0)
29+
expect(VCAP::CloudController::AppEvent.count).to eq(0)
2930
end
3031
end
3132

@@ -36,7 +37,12 @@
3637
end
3738

3839
it 'keeps the last row when :keep_at_least_one_record is true even if it is older than the cutoff date' do
39-
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::Event, 0, keep_at_least_one_record: true)
40+
stale_event1 = VCAP::CloudController::Event.make(created_at: 1.day.ago - 1.minute)
41+
stale_event2 = VCAP::CloudController::Event.make(created_at: 2.days.ago)
42+
43+
fresh_event = VCAP::CloudController::Event.make(created_at: 1.day.ago + 1.minute)
44+
45+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::Event, 0, keep_at_least_one_record: true, keep_running_records: true, keep_unprocessed_records: false)
4046

4147
expect do
4248
record_cleanup.delete
@@ -46,5 +52,149 @@
4652
expect { stale_event1.reload }.to raise_error(Sequel::NoExistingObject)
4753
expect { stale_event2.reload }.to raise_error(Sequel::NoExistingObject)
4854
end
55+
56+
# Testing keep_running_records feature
57+
it 'keeps AppUsageEvent start record when there is no corresponding stop record' do
58+
stale_app_usage_event_start = VCAP::CloudController::AppUsageEvent.make(created_at: 2.days.ago, state: 'STARTED', app_guid: 'guid1')
59+
60+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: true,
61+
keep_unprocessed_records: false)
62+
record_cleanup.delete
63+
expect(stale_app_usage_event_start.reload).to be_present
64+
end
65+
66+
it 'keeps AppUsageEvent start record when stop record is fresh' do
67+
stale_app_usage_event_start = VCAP::CloudController::AppUsageEvent.make(created_at: 2.days.ago, state: 'STARTED', app_guid: 'guid1')
68+
fresh_app_usage_event_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.day.ago + 1.minute, state: 'STOPPED', app_guid: 'guid1')
69+
70+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: true,
71+
keep_unprocessed_records: false)
72+
record_cleanup.delete
73+
expect(stale_app_usage_event_start.reload).to be_present
74+
expect(fresh_app_usage_event_stop.reload).to be_present
75+
end
76+
77+
it 'keeps AppUsageEvent start record when stop record is newer' do
78+
stale_app_usage_event_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 3.days.ago, state: 'STOPPED', app_guid: 'guid1')
79+
stale_app_usage_event_start = VCAP::CloudController::AppUsageEvent.make(created_at: 2.days.ago, state: 'STARTED', app_guid: 'guid1')
80+
81+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: true,
82+
keep_unprocessed_records: false)
83+
record_cleanup.delete
84+
expect(stale_app_usage_event_start.reload).to be_present
85+
expect { stale_app_usage_event_stop.reload }.to raise_error(Sequel::NoExistingObject)
86+
end
87+
88+
it 'keeps ServiceUsageEvent create record when there is no corresponding delete record' do
89+
stale_service_usage_event_create = VCAP::CloudController::ServiceUsageEvent.make(created_at: 2.days.ago, state: 'CREATED', service_instance_guid: 'guid1')
90+
91+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: true,
92+
keep_unprocessed_records: false)
93+
record_cleanup.delete
94+
expect(stale_service_usage_event_create.reload).to be_present
95+
end
96+
97+
it 'keeps ServiceUsageEvent create record when delete record is fresh' do
98+
stale_service_usage_event_create = VCAP::CloudController::ServiceUsageEvent.make(created_at: 2.days.ago, state: 'CREATED', service_instance_guid: 'guid1')
99+
fresh_service_usage_event_delete = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.day.ago + 1.minute, state: 'DELETED', service_instance_guid: 'guid1')
100+
101+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: true,
102+
keep_unprocessed_records: false)
103+
record_cleanup.delete
104+
expect(stale_service_usage_event_create.reload).to be_present
105+
expect(fresh_service_usage_event_delete.reload).to be_present
106+
end
107+
108+
it 'keeps ServiceUsageEvent create record when delete record is newer' do
109+
stale_service_usage_event_delete = VCAP::CloudController::ServiceUsageEvent.make(created_at: 3.days.ago, state: 'DELETED', service_instance_guid: 'guid1')
110+
stale_service_usage_event_create = VCAP::CloudController::ServiceUsageEvent.make(created_at: 2.days.ago, state: 'CREATED', service_instance_guid: 'guid1')
111+
112+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: true,
113+
keep_unprocessed_records: false)
114+
record_cleanup.delete
115+
expect(stale_service_usage_event_create.reload).to be_present
116+
expect { stale_service_usage_event_delete.reload }.to raise_error(Sequel::NoExistingObject)
117+
end
118+
119+
# Testing keep_unprocessed_records feature
120+
it 'keeps unprocessed AppUsageEvent records older than the cutoff date' do
121+
stale_app_usage_event_1_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid1')
122+
stale_app_usage_event_2_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid2')
123+
VCAP::CloudController::AppUsageConsumer.make(consumer_guid: 'guid1', last_processed_guid: stale_app_usage_event_2_stop.guid)
124+
stale_app_usage_event_3_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid3')
125+
126+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: false,
127+
keep_unprocessed_records: true)
128+
record_cleanup.delete
129+
expect { stale_app_usage_event_1_stop.reload }.to raise_error(Sequel::NoExistingObject)
130+
expect(stale_app_usage_event_2_stop.reload).to be_present
131+
expect(stale_app_usage_event_3_stop.reload).to be_present
132+
end
133+
134+
it 'keeps unprocessed ServiceUsageEvent records older than the cutoff date' do
135+
stale_service_usage_event_1_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid1')
136+
stale_service_usage_event_2_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid2')
137+
VCAP::CloudController::ServiceUsageConsumer.make(consumer_guid: 'guid1', last_processed_guid: stale_service_usage_event_2_stop.guid)
138+
stale_service_usage_event_3_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid3')
139+
140+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: false,
141+
keep_unprocessed_records: true)
142+
record_cleanup.delete
143+
expect { stale_service_usage_event_1_stop.reload }.to raise_error(Sequel::NoExistingObject)
144+
expect(stale_service_usage_event_2_stop.reload).to be_present
145+
expect(stale_service_usage_event_3_stop.reload).to be_present
146+
end
147+
148+
it 'deletes all stale AppUsageEvent records when all registered consumers reference non-existant guids' do
149+
stale_app_usage_event_1_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid1')
150+
stale_app_usage_event_2_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid2')
151+
VCAP::CloudController::AppUsageConsumer.make(consumer_guid: 'guid1', last_processed_guid: 'fake-guid-1')
152+
VCAP::CloudController::AppUsageConsumer.make(consumer_guid: 'guid2', last_processed_guid: 'fake-guid-2')
153+
154+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: false,
155+
keep_unprocessed_records: true)
156+
record_cleanup.delete
157+
expect { stale_app_usage_event_1_stop.reload }.to raise_error(Sequel::NoExistingObject)
158+
expect { stale_app_usage_event_2_stop.reload }.to raise_error(Sequel::NoExistingObject)
159+
end
160+
161+
it 'deletes all stale ServiceUsageEvent records when all registered consumers reference non-existant guids' do
162+
stale_service_usage_event_1_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid1')
163+
stale_service_usage_event_2_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid2')
164+
VCAP::CloudController::ServiceUsageConsumer.make(consumer_guid: 'guid1', last_processed_guid: 'fake-guid-1')
165+
VCAP::CloudController::ServiceUsageConsumer.make(consumer_guid: 'guid2', last_processed_guid: 'fake-guid-2')
166+
167+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: false,
168+
keep_unprocessed_records: true)
169+
record_cleanup.delete
170+
expect { stale_service_usage_event_1_stop.reload }.to raise_error(Sequel::NoExistingObject)
171+
expect { stale_service_usage_event_2_stop.reload }.to raise_error(Sequel::NoExistingObject)
172+
end
173+
174+
it 'deletes stale AppUsageEvent records even if 1 consumer references a non-existant guid' do
175+
stale_app_usage_event_1_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid1')
176+
stale_app_usage_event_2_stop = VCAP::CloudController::AppUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', app_guid: 'guid2')
177+
VCAP::CloudController::AppUsageConsumer.make(consumer_guid: 'guid1', last_processed_guid: 'fake-guid-1')
178+
VCAP::CloudController::AppUsageConsumer.make(consumer_guid: 'guid2', last_processed_guid: stale_app_usage_event_2_stop.guid)
179+
180+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::AppUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: false,
181+
keep_unprocessed_records: true)
182+
record_cleanup.delete
183+
expect { stale_app_usage_event_1_stop.reload }.to raise_error(Sequel::NoExistingObject)
184+
expect(stale_app_usage_event_2_stop.reload).to be_present
185+
end
186+
187+
it 'deletes stale ServiceUsageEvent records even if 1 consumer references a non-existant guid' do
188+
stale_service_usage_event_1_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid1')
189+
stale_service_usage_event_2_stop = VCAP::CloudController::ServiceUsageEvent.make(created_at: 1.year.ago, state: 'STOPPED', service_instance_guid: 'guid2')
190+
VCAP::CloudController::ServiceUsageConsumer.make(consumer_guid: 'guid1', last_processed_guid: 'fake-guid-1')
191+
VCAP::CloudController::ServiceUsageConsumer.make(consumer_guid: 'guid2', last_processed_guid: stale_service_usage_event_2_stop.guid)
192+
193+
record_cleanup = Database::OldRecordCleanup.new(VCAP::CloudController::ServiceUsageEvent, 1, keep_at_least_one_record: false, keep_running_records: false,
194+
keep_unprocessed_records: true)
195+
record_cleanup.delete
196+
expect { stale_service_usage_event_1_stop.reload }.to raise_error(Sequel::NoExistingObject)
197+
expect(stale_service_usage_event_2_stop.reload).to be_present
198+
end
49199
end
50200
end

0 commit comments

Comments
 (0)
Please sign in to comment.