Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions db/migrations/20260324120000_add_composite_indexes_to_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# rubocop:disable Metrics/BlockLength
Sequel.migration do
no_transaction # required for concurrently option on postgres

up do
if database_type == :postgres
VCAP::Migration.with_concurrent_timeout(self) do
add_index :events, %i[actee created_at guid],
name: :events_actee_created_at_guid_index,
if_not_exists: true,
concurrently: true

add_index :events, %i[space_guid created_at guid],
name: :events_space_guid_created_at_guid_index,
if_not_exists: true,
concurrently: true

add_index :events, %i[organization_guid created_at guid],
name: :events_organization_guid_created_at_guid_index,
if_not_exists: true,
concurrently: true
end
else
alter_table(:events) do
# rubocop:disable Sequel/ConcurrentIndex
unless @db.indexes(:events).key?(:events_actee_created_at_guid_index)
add_index %i[actee created_at guid],
name: :events_actee_created_at_guid_index
end
unless @db.indexes(:events).key?(:events_space_guid_created_at_guid_index)
add_index %i[space_guid created_at guid],
name: :events_space_guid_created_at_guid_index
end
unless @db.indexes(:events).key?(:events_organization_guid_created_at_guid_index)
add_index %i[organization_guid created_at guid],
name: :events_organization_guid_created_at_guid_index
end
# rubocop:enable Sequel/ConcurrentIndex
end
end
end

down do
if database_type == :postgres
VCAP::Migration.with_concurrent_timeout(self) do
drop_index :events, nil,
name: :events_actee_created_at_guid_index,
if_exists: true,
concurrently: true

drop_index :events, nil,
name: :events_space_guid_created_at_guid_index,
if_exists: true,
concurrently: true

drop_index :events, nil,
name: :events_organization_guid_created_at_guid_index,
if_exists: true,
concurrently: true
end
else
alter_table(:events) do
# rubocop:disable Sequel/ConcurrentIndex
drop_index nil, name: :events_actee_created_at_guid_index if @db.indexes(:events).key?(:events_actee_created_at_guid_index)
drop_index nil, name: :events_space_guid_created_at_guid_index if @db.indexes(:events).key?(:events_space_guid_created_at_guid_index)
drop_index nil, name: :events_organization_guid_created_at_guid_index if @db.indexes(:events).key?(:events_organization_guid_created_at_guid_index)
# rubocop:enable Sequel/ConcurrentIndex
end
end
end
end
# rubocop:enable Metrics/BlockLength
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'spec_helper'
require 'migrations/helpers/migration_shared_context'

RSpec.describe 'migration to add composite indexes to events table', isolation: :truncation, type: :migration do
include_context 'migration' do
let(:migration_filename) { '20260324120000_add_composite_indexes_to_events.rb' }
end

describe 'events table' do
it 'adds composite indexes and handles idempotency gracefully' do
# Before migration: composite indexes should not exist
expect(db.indexes(:events)).not_to include(:events_actee_created_at_guid_index)
expect(db.indexes(:events)).not_to include(:events_space_guid_created_at_guid_index)
expect(db.indexes(:events)).not_to include(:events_organization_guid_created_at_guid_index)

# Test up migration
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error

expect(db.indexes(:events)).to include(:events_actee_created_at_guid_index)
expect(db.indexes(:events)).to include(:events_space_guid_created_at_guid_index)
expect(db.indexes(:events)).to include(:events_organization_guid_created_at_guid_index)

# Verify index column order
expect(db.indexes(:events)[:events_actee_created_at_guid_index][:columns]).to eq(%i[actee created_at guid])
expect(db.indexes(:events)[:events_space_guid_created_at_guid_index][:columns]).to eq(%i[space_guid created_at guid])
expect(db.indexes(:events)[:events_organization_guid_created_at_guid_index][:columns]).to eq(%i[organization_guid created_at guid])

# Test up migration idempotency: running again should not fail
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
expect(db.indexes(:events)).to include(:events_actee_created_at_guid_index)
expect(db.indexes(:events)).to include(:events_space_guid_created_at_guid_index)
expect(db.indexes(:events)).to include(:events_organization_guid_created_at_guid_index)

# Test down migration
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error
expect(db.indexes(:events)).not_to include(:events_actee_created_at_guid_index)
expect(db.indexes(:events)).not_to include(:events_space_guid_created_at_guid_index)
expect(db.indexes(:events)).not_to include(:events_organization_guid_created_at_guid_index)

# Test down migration idempotency: running again should not fail
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error
expect(db.indexes(:events)).not_to include(:events_actee_created_at_guid_index)
expect(db.indexes(:events)).not_to include(:events_space_guid_created_at_guid_index)
expect(db.indexes(:events)).not_to include(:events_organization_guid_created_at_guid_index)
end
end
end
Loading