Optimize domain list updater: Replace N+1 creates with bulk insert_all #2
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
This PR optimizes the
DomainListUpdaterto use Rails'insert_allfor bulk inserts instead of creating records individually. This change reduces database operations from ~5,000+ individual INSERT statements to a single bulk INSERT operation, significantly improving performance for the nightly domain list updates.Why This Change Was Needed
The current implementation uses
delete_allfollowed by a loop that callscreatefor each domain:The Problem:
createcall is a separate database round-tripPerformance Impact:
Constraints & Context
Options Considered
Decision: Chose
insert_allbecause it's a minimal change with massive performance improvement. Delta updates would add significant complexity for marginal benefit in a nightly job scenario.Sources Consulted & Best Practices
Rails Documentation
From Rails guides on Active Record basics:
Rails Best Practices
insert_allfor bulk inserts to avoid N+1 query problemsinsert_allgenerates a single SQL INSERT statement with multiple VALUES, which is orders of magnitude faster than individual insertsrecord_timestamps: trueensurescreated_atandupdated_atare automatically setWhy Not Delta/Diff Updates?
While delta updates (comparing current DB state with new list, only inserting new domains and deleting removed ones) could theoretically be faster if the list rarely changes, the added complexity isn't justified:
Implemented Solution
Before:
After:
Key Changes:
insert_allinsert_allwithrecord_timestamps: trueto automatically set timestampsAffected Files
lib/nondisposable/domain_list_updater.rbcreatecalls with bulkinsert_alltest/nondisposable/domain_list_updater_test.rbtest_update_rolls_back_on_insert_errorto stubinsert_allinstead ofcreatetest_rollback_on_partial_insert_failuretotest_insert_all_is_atomicsinceinsert_allis inherently atomic (no partial failures possible)Testing
✅ All 256 tests pass across Rails 7.2, 8.0, and 8.1
✅ Test coverage remains at 90.6% line coverage, 95.0% branch coverage
✅ All existing functionality preserved
✅ Transaction rollback behavior verified
Performance Characteristics
Backward Compatibility
✅ Fully backward compatible - no API changes
✅ Same transactional guarantees
✅ Same error handling behavior
✅ Same logging output
This change follows Rails best practices for bulk operations and significantly improves performance while maintaining simplicity and maintainability.