fix: handle duplicate InvitationLogEntry on retry (v2)#2558
Merged
mroderick merged 3 commits intocodebar:masterfrom Apr 10, 2026
Merged
fix: handle duplicate InvitationLogEntry on retry (v2)#2558mroderick merged 3 commits intocodebar:masterfrom
mroderick merged 3 commits intocodebar:masterfrom
Conversation
Second attempt at fixing the "Member has already been taken" validation error when re-sending workshop invitations. Problem: The previous fix (PR codebar#2556) used find_or_initialize_by with the status included in the query. However, the InvitationLogEntry uniqueness constraint only validates (member_id, invitation_type, invitation_id), NOT status. This means: - A member can have ONE success entry AND ONE failure entry for the same invitation - find_or_initialize_by(member, invitation, :success) would return a persisted entry with status=:failed if one existed, causing the wrong status to be used - Or if building a new entry with status=:success when status=:failed already existed, the uniqueness validation would fail Fix: 1. Split the check: first find any existing entry by (member, invitation), ignoring status 2. If found, return it (either success or failure) 3. Only if no entry exists, create a new one with the specified status Additionally, add autosave: false to the has_many :entries association to prevent Rails from attempting to autosave unpersisted entries when fail_batch is called, which was causing "Validation failed: Entries is invalid" errors. Changes: - app/services/invitation_logger.rb: Separate find_or_build_entry logic - app/models/invitation_log.rb: Add autosave: false to entries association Tests: - All 41 existing tests pass - Retry behavior tests verify no duplicate entries are created
Verify that when logging success/failure for a member+invitation that already has an entry with a different status, the existing entry is returned (not a new one created, which would cause uniqueness violation)
Collaborator
|
Perhaps the find_or... with a block (the block is used to set attributes on the non-found record) https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-find_or_create_by |
Refactored per olleolleolle's suggestion: - Use find_or_create_by(member, invitation) with block for status - Check processed_at instead of persisted? for retry detection - Cleaner, more idiomatic Rails pattern
Collaborator
Author
|
Thanks for the suggestion! Your insight led to a cleaner solution. The issue with def find_or_build_entry(member, invitation, status)
@log.entries.find_or_create_by(member: member, invitation: invitation) do |entry|
entry.status = status
end
end
def log_success(member, invitation = nil)
entry = find_or_build_entry(member, invitation, :success)
return entry if entry.processed_at # Already processed → skip
entry.assign_attributes(processed_at: Time.current)
save_entry(entry, :success_count)
endThis is more idiomatic Rails - the block only runs on create, not on find. And using Appreciate the nudge to find the cleaner approach! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second attempt at fixing the "Member has already been taken" validation error when re-sending workshop invitations.
Problem
The previous fix (PR #2556) used
find_or_initialize_bywithstatusincluded in the query:However, the
InvitationLogEntryuniqueness constraint is:This validates
(member_id, invitation_type, invitation_id), NOT status. The bug:status = successfind_or_initialize_by(member, invitation, :failed)→ finds the success entry but with wrong status → tries to save → VALIDATION ERRORThe previous fix worked for most cases but failed when:
Solution
Split the check - query by (member, invitation) only, ignoring status:
This ensures:
Additional Fix
Also add
autosave: falseto thehas_many :entriesassociation inInvitationLog:Why: In Rails 5+,
has_manydefaults toautosave: true. Whenfail_batchcalls@log.update!, Rails attempts to autosave all associated entries. If any unpersisted entries exist in memory with validation issues, this causesValidation failed: Entries is invaliderrors.This happened when I tried to re-invite people for the Berlin chapter this morning:
https://app.rollbar.com/a/codebar-production/fix/item/codebar-production/674#detail
Changes
app/services/invitation_logger.rb- Separate find/build logic to query without statusapp/models/invitation_log.rb- Addautosave: falseto entries associationTesting
41 examples pass, including retry behavior tests.