Problem
AidStation validates uniqueness of split_id scoped to event_id, but the aid_stations table has no unique index backing it — only single-column non-unique indexes on event_id and split_id. Application-level uniqueness validation alone is subject to race conditions (two concurrent saves can both pass validation), so duplicates are technically possible.
This surfaced in #2121, where rubocop's Rails/UniqueValidationWithoutIndex flagged the validation and was silenced with an inline disable:
validates:split_id,uniqueness: {scope: :event_id,message: "only one of any given split permitted within an event"}# rubocop:disable Rails/UniqueValidationWithoutIndex, Layout/LineLengthFix
- Audit production for existing duplicate
(event_id, split_id) pairs (and clean up if any exist — the migration will fail otherwise):
select event_id, split_id, count(*) from aid_stations group by1, 2havingcount(*) >1;
- Migration:
add_index :aid_stations, [:event_id, :split_id], unique: true (consider dropping the now-redundant single-column event_id index, since the composite index covers event_id lookups) - Remove the
Rails/UniqueValidationWithoutIndex portion of the inline disable in app/models/aid_station.rb
Optional hardening while in there: event_id and split_id are also nullable at the DB level despite required belongs_to associations — could add null: false constraints in the same migration after confirming no null rows exist.
Note: event_group.rb, split.rb, and organization.rb carry the same inline disable for their own uniqueness validations; those are case-insensitive (lower(name)) or message-customized cases that may warrant their own follow-up.
Problem
AidStationvalidates uniqueness ofsplit_idscoped toevent_id, but theaid_stationstable has no unique index backing it — only single-column non-unique indexes onevent_idandsplit_id. Application-level uniqueness validation alone is subject to race conditions (two concurrent saves can both pass validation), so duplicates are technically possible.This surfaced in #2121, where rubocop's
Rails/UniqueValidationWithoutIndexflagged the validation and was silenced with an inline disable:Fix
(event_id, split_id)pairs (and clean up if any exist — the migration will fail otherwise):add_index :aid_stations, [:event_id, :split_id], unique: true(consider dropping the now-redundant single-columnevent_idindex, since the composite index coversevent_idlookups)Rails/UniqueValidationWithoutIndexportion of the inline disable inapp/models/aid_station.rbOptional hardening while in there:
event_idandsplit_idare also nullable at the DB level despite requiredbelongs_toassociations — could addnull: falseconstraints in the same migration after confirming no null rows exist.Note:
event_group.rb,split.rb, andorganization.rbcarry the same inline disable for their own uniqueness validations; those are case-insensitive (lower(name)) or message-customized cases that may warrant their own follow-up.