mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
fix: snapshot live-stream status order before sorting to avoid TimSort contract violation
LiveStreamsFeedFilter.sort and DiscoverLiveFeedFilter.sort computed the primary sort key, convertStatusToOrder(it.event), lazily inside the comparator. That key reads OnlineChecker.isCachedAndOffline(url), which depends on a moving five-minute window and on the checkOnlineCache LruCache. A background online check can mutate that cache while the sort is running, so the same note could compare as LIVE (order 2) in one pairwise comparison and offline (order 0) in another. The resulting unstable ordering makes TimSort throw "Comparison method violates its general contract!". Snapshot the status order once per item before sorting (matching how participantCounts/allParticipants are already precomputed) so the comparator reads stable values. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HwZzCdNMQoRWbgtZrp4SKH
This commit is contained in:
+8
-1
@@ -106,10 +106,17 @@ open class DiscoverLiveFeedFilter(
|
||||
val allParticipants =
|
||||
items.associate { it to counter.countFollowsThatParticipateOn(it, null) }
|
||||
|
||||
// Snapshots the status order once per item. convertStatusToOrder reads the
|
||||
// OnlineChecker cache and the moving five-minute window, both of which can change
|
||||
// mid-sort (a background online check can update the cache). Reading it lazily inside
|
||||
// the comparator makes the ordering unstable and TimSort throws
|
||||
// "Comparison method violates its general contract!".
|
||||
val statusOrders = items.associateWith { convertStatusToOrder(it.event) }
|
||||
|
||||
return items
|
||||
.sortedWith(
|
||||
compareBy(
|
||||
{ convertStatusToOrder(it.event) },
|
||||
{ statusOrders[it] },
|
||||
{ participantCounts[it] },
|
||||
{ allParticipants[it] },
|
||||
{
|
||||
|
||||
+8
-1
@@ -106,10 +106,17 @@ class LiveStreamsFeedFilter(
|
||||
val allParticipants =
|
||||
items.associate { it to counter.countFollowsThatParticipateOn(it, null) }
|
||||
|
||||
// Snapshots the status order once per item. convertStatusToOrder reads the
|
||||
// OnlineChecker cache and the moving five-minute window, both of which can change
|
||||
// mid-sort (a background online check can update the cache). Reading it lazily inside
|
||||
// the comparator makes the ordering unstable and TimSort throws
|
||||
// "Comparison method violates its general contract!".
|
||||
val statusOrders = items.associateWith { convertStatusToOrder(it.event) }
|
||||
|
||||
return items
|
||||
.sortedWith(
|
||||
compareBy(
|
||||
{ convertStatusToOrder(it.event) },
|
||||
{ statusOrders[it] },
|
||||
{ participantCounts[it] },
|
||||
{ allParticipants[it] },
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user