Commit e5cc8675 authored by Sean Arnold's avatar Sean Arnold

Add Pending Alert Escalations table

- Add migrations with partitions
- Add model spec

Changelog: changed
parent 8ee2e9cf
......@@ -6,6 +6,10 @@
Gitlab::Database::Partitioning::PartitionCreator.register(AuditEvent)
Gitlab::Database::Partitioning::PartitionCreator.register(WebHookLog)
if Gitlab.ee?
Gitlab::Database::Partitioning::PartitionCreator.register(IncidentManagement::PendingEscalations::Alert)
end
begin
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions unless ENV['DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP']
rescue ActiveRecord::ActiveRecordError, PG::Error
......
# frozen_string_literal: true
class CreateIncidentManagementPendingAlertEscalations < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
execute(<<~SQL)
CREATE TABLE incident_management_pending_alert_escalations (
id bigserial NOT NULL,
rule_id bigint,
alert_id bigint NOT NULL,
schedule_id bigint NOT NULL,
status smallint NOT NULL,
process_at timestamp with time zone NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
PRIMARY KEY (id, process_at)
) PARTITION BY RANGE (process_at);
CREATE INDEX index_incident_management_pending_alert_escalations_on_alert_id
ON incident_management_pending_alert_escalations USING btree (alert_id);
CREATE INDEX index_incident_management_pending_alert_escalations_on_rule_id
ON incident_management_pending_alert_escalations USING btree (rule_id);
CREATE INDEX index_incident_management_pending_alert_escalations_on_schedule_id
ON incident_management_pending_alert_escalations USING btree (schedule_id);
CREATE INDEX index_incident_management_pending_alert_escalations_on_process_at
ON incident_management_pending_alert_escalations USING btree (process_at);
ALTER TABLE incident_management_pending_alert_escalations ADD CONSTRAINT fk_rails_fcbfd9338b
FOREIGN KEY (schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
ALTER TABLE incident_management_pending_alert_escalations ADD CONSTRAINT fk_rails_057c1e3d87
FOREIGN KEY (rule_id) REFERENCES incident_management_escalation_rules(id) ON DELETE SET NULL;
ALTER TABLE incident_management_pending_alert_escalations ADD CONSTRAINT fk_rails_8d8de95da9
FOREIGN KEY (alert_id) REFERENCES alert_management_alerts(id) ON DELETE CASCADE;
SQL
end
end
def down
with_lock_retries do
drop_table :incident_management_pending_alert_escalations
end
end
end
fa4f1ec80e7039e59d283dc6effd6904ca33c637d27c687d990822eb2f6198e5
\ No newline at end of file
......@@ -190,6 +190,18 @@ CREATE TABLE audit_events (
)
PARTITION BY RANGE (created_at);
CREATE TABLE incident_management_pending_alert_escalations (
id bigint NOT NULL,
rule_id bigint,
alert_id bigint NOT NULL,
schedule_id bigint NOT NULL,
status smallint NOT NULL,
process_at timestamp with time zone NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL
)
PARTITION BY RANGE (process_at);
CREATE TABLE web_hook_logs (
id bigint NOT NULL,
web_hook_id integer NOT NULL,
......@@ -13913,6 +13925,15 @@ CREATE SEQUENCE incident_management_oncall_shifts_id_seq
ALTER SEQUENCE incident_management_oncall_shifts_id_seq OWNED BY incident_management_oncall_shifts.id;
CREATE SEQUENCE incident_management_pending_alert_escalations_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE incident_management_pending_alert_escalations_id_seq OWNED BY incident_management_pending_alert_escalations.id;
CREATE TABLE index_statuses (
id integer NOT NULL,
project_id integer NOT NULL,
......@@ -20017,6 +20038,8 @@ ALTER TABLE ONLY incident_management_oncall_schedules ALTER COLUMN id SET DEFAUL
ALTER TABLE ONLY incident_management_oncall_shifts ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_shifts_id_seq'::regclass);
ALTER TABLE ONLY incident_management_pending_alert_escalations ALTER COLUMN id SET DEFAULT nextval('incident_management_pending_alert_escalations_id_seq'::regclass);
ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statuses_id_seq'::regclass);
ALTER TABLE ONLY insights ALTER COLUMN id SET DEFAULT nextval('insights_id_seq'::regclass);
......@@ -21398,6 +21421,9 @@ ALTER TABLE ONLY incident_management_oncall_schedules
ALTER TABLE ONLY incident_management_oncall_shifts
ADD CONSTRAINT incident_management_oncall_shifts_pkey PRIMARY KEY (id);
ALTER TABLE ONLY incident_management_pending_alert_escalations
ADD CONSTRAINT incident_management_pending_alert_escalations_pkey PRIMARY KEY (id, process_at);
ALTER TABLE ONLY index_statuses
ADD CONSTRAINT index_statuses_pkey PRIMARY KEY (id);
......@@ -23608,6 +23634,14 @@ CREATE INDEX index_incident_management_oncall_schedules_on_project_id ON inciden
CREATE INDEX index_incident_management_oncall_shifts_on_participant_id ON incident_management_oncall_shifts USING btree (participant_id);
CREATE INDEX index_incident_management_pending_alert_escalations_on_alert_id ON ONLY incident_management_pending_alert_escalations USING btree (alert_id);
CREATE INDEX index_incident_management_pending_alert_escalations_on_process_ ON ONLY incident_management_pending_alert_escalations USING btree (process_at);
CREATE INDEX index_incident_management_pending_alert_escalations_on_rule_id ON ONLY incident_management_pending_alert_escalations USING btree (rule_id);
CREATE INDEX index_incident_management_pending_alert_escalations_on_schedule ON ONLY incident_management_pending_alert_escalations USING btree (schedule_id);
CREATE UNIQUE INDEX index_index_statuses_on_project_id ON index_statuses USING btree (project_id);
CREATE INDEX index_insights_on_namespace_id ON insights USING btree (namespace_id);
......@@ -26324,6 +26358,9 @@ ALTER TABLE ONLY terraform_state_versions
ALTER TABLE ONLY ci_build_report_results
ADD CONSTRAINT fk_rails_056d298d48 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE incident_management_pending_alert_escalations
ADD CONSTRAINT fk_rails_057c1e3d87 FOREIGN KEY (rule_id) REFERENCES incident_management_escalation_rules(id) ON DELETE SET NULL;
ALTER TABLE ONLY ci_daily_build_group_report_results
ADD CONSTRAINT fk_rails_0667f7608c FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
......@@ -27164,6 +27201,9 @@ ALTER TABLE ONLY vulnerability_feedback
ALTER TABLE ONLY ci_pipeline_messages
ADD CONSTRAINT fk_rails_8d3b04e3e1 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE incident_management_pending_alert_escalations
ADD CONSTRAINT fk_rails_8d8de95da9 FOREIGN KEY (alert_id) REFERENCES alert_management_alerts(id) ON DELETE CASCADE;
ALTER TABLE ONLY approval_merge_request_rules_approved_approvers
ADD CONSTRAINT fk_rails_8dc94cff4d FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
......@@ -27824,6 +27864,9 @@ ALTER TABLE ONLY ci_job_variables
ALTER TABLE ONLY packages_nuget_metadata
ADD CONSTRAINT fk_rails_fc0c19f5b4 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
ALTER TABLE incident_management_pending_alert_escalations
ADD CONSTRAINT fk_rails_fcbfd9338b FOREIGN KEY (schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
ALTER TABLE ONLY external_approval_rules
ADD CONSTRAINT fk_rails_fd4f9ac573 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
......@@ -8,6 +8,8 @@ module EE
prepended do
include AfterCommitQueue
has_many :pending_escalations, class_name: 'IncidentManagement::PendingEscalations::Alert', foreign_key: :alert_id, inverse_of: :alert
after_create do |alert|
run_after_commit { alert.trigger_auto_rollback }
end
......
# frozen_string_literal: true
module IncidentManagement
module PendingEscalations
class Alert < ApplicationRecord
include PartitionedTable
alias_attribute :target, :alert
self.primary_key = :id
self.table_name = 'incident_management_pending_alert_escalations'
partitioned_by :process_at, strategy: :monthly
belongs_to :oncall_schedule, class_name: 'OncallSchedule', foreign_key: 'schedule_id'
belongs_to :alert, class_name: 'AlertManagement::Alert', foreign_key: 'alert_id', inverse_of: :pending_escalations
belongs_to :rule, class_name: 'EscalationRule', foreign_key: 'rule_id', optional: true
scope :processable, -> { where(process_at: ESCALATION_BUFFER.ago..Time.current) }
enum status: AlertManagement::Alert::STATUSES.slice(:acknowledged, :resolved)
validates :process_at, presence: true
validates :status, presence: true
delegate :project, to: :alert
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :incident_management_pending_alert_escalation, class: 'IncidentManagement::PendingEscalations::Alert' do
transient do
project { create(:project) } # rubocop:disable FactoryBot/InlineAssociation
policy { create(:incident_management_escalation_policy, project: project) } # rubocop:disable FactoryBot/InlineAssociation
end
rule { association :incident_management_escalation_rule, policy: policy }
oncall_schedule { association :incident_management_oncall_schedule, project: project }
alert { association :alert_management_alert, project: project }
status { IncidentManagement::EscalationRule.statuses[:acknowledged] }
process_at { 5.minutes.from_now }
end
end
......@@ -2,10 +2,14 @@
require 'spec_helper'
RSpec.describe EE::AlertManagement::Alert do
RSpec.describe AlertManagement::Alert do
let_it_be(:project, refind: true) { create(:project) }
let_it_be(:environment, refind: true) { create(:environment, project: project) }
describe 'associations' do
it { is_expected.to have_many(:pending_escalations).class_name('IncidentManagement::PendingEscalations::Alert') }
end
describe 'after_create' do
it 'attempts to trigger auto rollback' do
alert = build(:alert_management_alert, :triggered, :critical)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IncidentManagement::PendingEscalations::Alert do
subject { build(:incident_management_pending_alert_escalation) }
it { is_expected.to be_valid }
describe 'validations' do
it { is_expected.to validate_presence_of(:process_at) }
it { is_expected.to validate_presence_of(:status) }
it { is_expected.to delegate_method(:project).to(:alert) }
end
describe 'associations' do
it { is_expected.to belong_to(:oncall_schedule) }
it { is_expected.to belong_to(:alert) }
it { is_expected.to belong_to(:rule) }
end
describe 'scopes' do
describe '.processable' do
subject { described_class.processable }
let_it_be(:policy) { create(:incident_management_escalation_policy) }
let_it_be(:rule) { policy.rules.first }
let_it_be(:two_months_ago_escalation) { create(:incident_management_pending_alert_escalation, rule: rule, process_at: 2.months.ago) }
let_it_be(:three_weeks_ago_escalation) { create(:incident_management_pending_alert_escalation, rule: rule, process_at: 3.weeks.ago) }
let_it_be(:three_days_ago_escalation) { create(:incident_management_pending_alert_escalation, rule: rule, process_at: 3.days.ago) }
let_it_be(:future_escalation) { create(:incident_management_pending_alert_escalation, rule: rule, process_at: 5.minutes.from_now) }
it { is_expected.to eq [three_weeks_ago_escalation, three_days_ago_escalation] }
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment