fix(autotells): preload tell history fully up to the user-configured limit

PreloadHistory had a hardcoded 500-row SQL scan window that capped the
per-partner history pull regardless of the AutoTellTabsHistoryPreload
setting. For active users with many tell partners, the scan window
filled up with chatter from other partners and pushed less-frequent
partners' history off the back end — pinned tabs reloaded empty even
though the messages were still in the database.

Drops the hardcoded scan cap. The (Receiver, Date) index keeps SQL fast
on the now-unbounded read, and the client-side loop still breaks as
soon as the configured per-tab limit is hit, so decode cost stays
proportional to the depth at which `limit` matches accumulate (typically
shallow even for chatty users).
This commit is contained in:
2026-05-16 12:16:08 +02:00
parent 679b8f0f5e
commit f66316161b
+9 -5
View File
@@ -1001,12 +1001,18 @@ internal class MessageStore : IDisposable
// SQL narrows by Receiver + ChatType (indexed); client does the final // SQL narrows by Receiver + ChatType (indexed); client does the final
// PlayerPayload comparison. sqlScanLimit caps the scan to stay within // PlayerPayload comparison. sqlScanLimit caps the scan to stay within
// the message-processing worker thread budget. // the message-processing worker thread budget.
// Walks the full receiver-filtered tell history newest-first and stops
// as soon as the per-partner match count reaches `limit`. The previous
// hardcoded 500-row scan window cut active users' less-frequent pinned
// partners out of the result whenever other partners' chatter pushed
// them off the back of the window. Index on (Receiver, Date) keeps the
// SQL side cheap; the client-side break bounds the actual decode cost
// to roughly the depth at which `limit` partner matches accumulate.
internal IReadOnlyList<Message> GetTellHistoryWithSender( internal IReadOnlyList<Message> GetTellHistoryWithSender(
ulong receiver, ulong receiver,
string senderName, string senderName,
uint senderWorld, uint senderWorld,
int limit, int limit
int sqlScanLimit = 500
) )
{ {
if (limit <= 0) if (limit <= 0)
@@ -1024,14 +1030,12 @@ internal class MessageStore : IDisposable
WHERE deleted = false WHERE deleted = false
AND Receiver = $Receiver AND Receiver = $Receiver
AND ChatType IN ($TellIncoming, $TellOutgoing) AND ChatType IN ($TellIncoming, $TellOutgoing)
ORDER BY Date DESC ORDER BY Date DESC;
LIMIT $ScanLimit;
"; ";
cmd.CommandTimeout = 60; cmd.CommandTimeout = 60;
cmd.Parameters.AddWithValue("$Receiver", receiver); cmd.Parameters.AddWithValue("$Receiver", receiver);
cmd.Parameters.AddWithValue("$TellIncoming", (int)ChatType.TellIncoming); cmd.Parameters.AddWithValue("$TellIncoming", (int)ChatType.TellIncoming);
cmd.Parameters.AddWithValue("$TellOutgoing", (int)ChatType.TellOutgoing); cmd.Parameters.AddWithValue("$TellOutgoing", (int)ChatType.TellOutgoing);
cmd.Parameters.AddWithValue("$ScanLimit", sqlScanLimit);
var collected = new List<Message>(); var collected = new List<Message>();
using var enumerator = new MessageEnumerator(cmd.ExecuteReader(), _logger); using var enumerator = new MessageEnumerator(cmd.ExecuteReader(), _logger);