Optimize multi-artist processing: cache split results and remove redundant operations#549
Conversation
Co-authored-by: lostf1sh <[email protected]>
Co-authored-by: lostf1sh <[email protected]>
Co-authored-by: lostf1sh <[email protected]>
Co-authored-by: lostf1sh <[email protected]>
Co-authored-by: lostf1sh <[email protected]>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
This PR optimizes the multi-artist processing implementation by introducing a caching mechanism for artist name splits, eliminating redundant string processing operations across multiple passes through the song list. The optimization consolidates artist splitting logic and adds input validation for delimiter configuration.
- Implements caching of artist name splits in a
songToArtistsmap to avoid redundant string processing - Removes redundant artist splitting operations in album deduplication and song correction phases
- Adds validation to ensure at least one delimiter is maintained when updating artist delimiters
- Removes unused legacy preprocessing function
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| SyncWorker.kt | Introduces songToArtists cache map to store split results, eliminates redundant splitArtistsByDelimiters calls in album and song processing, removes unused preProcessAndDeduplicate function |
| UserPreferencesRepository.kt | Adds empty list validation in setArtistDelimiters to enforce at least one delimiter requirement |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else { | ||
| listOf(song.artistName) | ||
| } | ||
| val artistsForSong = songToArtists[song.id] ?: listOf(song.artistName) |
There was a problem hiding this comment.
Similar to the album deduplication logic, the fallback ?: listOf(song.artistName) is defensive but unnecessary since the cache is populated for all songs. This fallback could mask issues if the cache is somehow incomplete. Consider adding an assertion or logging to detect if this fallback is ever triggered.
| val artistsForSong = songToArtists[song.id] ?: listOf(song.artistName) | |
| val artistsForSong = checkNotNull(songToArtists[song.id]) { "songToArtists cache missing for song id: ${song.id}, name: ${song.title}" } |
| // Ensure at least one delimiter is always maintained | ||
| if (delimiters.isEmpty()) { | ||
| return | ||
| } |
There was a problem hiding this comment.
Silent failure when empty delimiters are provided can lead to confusing behavior. When a user tries to set empty delimiters, the function returns silently without any indication of failure or reverting to defaults. This means the rescan flag won't be set even though the operation was requested, potentially leaving the UI in an inconsistent state.
Consider either:
- Throwing an IllegalArgumentException to signal that empty delimiters are not allowed
- Logging a warning to help with debugging
- Falling back to DEFAULT_ARTIST_DELIMITERS instead of silently ignoring the request
| val artistsToProcess = songToArtists[song.id] ?: listOf(song.artistName) | ||
| artistsToProcess.firstOrNull() ?: song.artistName |
There was a problem hiding this comment.
The fallback expression for cache misses is inconsistent with the cache population logic. The cache is populated for all songs in line 171, so songToArtists[song.id] should never be null when processing the same songs list. The fallback ?: listOf(song.artistName) is defensive but unnecessary and could mask issues if the cache is somehow incomplete.
Consider adding an assertion or logging to detect if this fallback is ever triggered, which would indicate a logic error in the cache population.
| val artistsToProcess = songToArtists[song.id] ?: listOf(song.artistName) | |
| artistsToProcess.firstOrNull() ?: song.artistName | |
| val artistsToProcess = songToArtists[song.id] | |
| if (artistsToProcess == null) { | |
| Log.e("SyncWorker", "songToArtists cache miss for song id=${song.id}, name='${song.artistName}'") | |
| throw IllegalStateException("songToArtists cache miss for song id=${song.id}, name='${song.artistName}'") | |
| } | |
| artistsToProcess!!.firstOrNull() ?: song.artistName |
Addressed performance and code quality issues in the multi-artist implementation where artist name splitting was performed redundantly across multiple passes through the song list.
Performance optimization
songToArtistsmap, reusing results across all operationsCode quality
setArtistDelimiters()to enforce at least one delimiterpreProcessAndDeduplicate()legacy functionBefore:
After:
Particularly beneficial for large music libraries where string splitting overhead compounds with collection size.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.