perf(chat): single-pass watermark and move-append on chat load
Loading chat history did more work than it needed to. Seeding the unread watermark walked every conversation and copied each one's full message list out of the store just to read the last timestamp, and the database reload copied every message into the store one at a time with no reservation. - The watermark now comes from one pass over the stored messages that returns the latest timestamp per conversation, with no per- conversation copy or sort. - The store gained a move-append and a reserve, so the reload pre-sizes its vector and dedup set once and moves each row in instead of copying it. The dedup key is computed before the move. Behaviour is unchanged: the watermark values and the blocked-conversation filter on reload are the same as before. Tests cover the per-conversation latest-timestamp pass and move-append. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10899,6 +10899,36 @@ void testTransactionRefreshRevision()
|
||||
// ChatStore::hasChatTxid (History "Message" badge / Chat filter): a lazy txid index keyed on the monotonic
|
||||
// revision(), so every mutation (append / updateDelivery / eraseConversation / clear) is seen on the next lookup;
|
||||
// clear() also drops it. store.empty() must equal conversationIds().empty() (shouldSplitChatBuffer relies on it).
|
||||
void testChatStoreLatestAndMove()
|
||||
{
|
||||
using namespace dragonx::chat;
|
||||
auto mk = [](const std::string& txid, std::size_t pos, const std::string& cid, int64_t ts) {
|
||||
ChatMessage m;
|
||||
m.txid = txid; m.payload_position = pos; m.conversation_id = cid; m.timestamp = ts; m.body = "body-" + txid;
|
||||
return m;
|
||||
};
|
||||
ChatStore a, b;
|
||||
a.reserve(8);
|
||||
std::vector<ChatMessage> in{ mk("t1", 0, "x", 50), mk("t2", 0, "y", 10), mk("t3", 0, "x", 90),
|
||||
mk("t4", 0, "y", 70), mk("t5", 0, "x", 20) };
|
||||
for (const auto& m : in) EXPECT_TRUE(a.append(m));
|
||||
for (auto m : in) EXPECT_TRUE(b.append(std::move(m)));
|
||||
// dedup on txid:position via the move overload; moved-from message not stored twice
|
||||
EXPECT_FALSE(b.append(mk("t1", 0, "x", 999)));
|
||||
EXPECT_TRUE(b.append(mk("t1", 1, "x", 60)));
|
||||
EXPECT_EQ(a.size(), static_cast<size_t>(5));
|
||||
EXPECT_EQ(b.size(), static_cast<size_t>(6));
|
||||
EXPECT_EQ(b.conversation("x")[0].body, std::string("body-t5"));
|
||||
|
||||
auto latest = a.latestTimestampByConversation();
|
||||
EXPECT_EQ(latest.size(), static_cast<size_t>(2));
|
||||
for (const auto& cid : a.conversationIds())
|
||||
EXPECT_EQ(latest[cid], static_cast<int64_t>(a.conversation(cid).back().timestamp));
|
||||
EXPECT_EQ(latest["x"], static_cast<int64_t>(90));
|
||||
EXPECT_EQ(latest["y"], static_cast<int64_t>(70));
|
||||
EXPECT_TRUE(ChatStore().latestTimestampByConversation().empty());
|
||||
}
|
||||
|
||||
void testChatStoreTxidIndex()
|
||||
{
|
||||
using namespace dragonx::chat;
|
||||
@@ -18535,6 +18565,7 @@ int main()
|
||||
testHushChatDatabase();
|
||||
testHushChatDeleteConversation();
|
||||
testChatStoreTxidIndex();
|
||||
testChatStoreLatestAndMove();
|
||||
testHushChatOutgoing();
|
||||
testHushChatTransport();
|
||||
testHushChatShuffledReceive();
|
||||
|
||||
Reference in New Issue
Block a user