mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 03:00:46 +03:00
Speedup startup by caching last message timestamp
The cache is only approximate, i.e. it doesn't skip edits and similar, but this might be good enough? Also no migration right now. Speeds up startup by about 5x on my system. Half the startup time is now loading the powerlevels for each room. We can probably lazily load those too in the future.
This commit is contained in:
parent
6e1fec1e63
commit
ef9ebe3fd3
5 changed files with 95 additions and 23 deletions
|
@ -1570,6 +1570,47 @@ Cache::updateState(const std::string &room, const mtx::responses::StateEvents &s
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename T>
|
||||||
|
auto
|
||||||
|
isMessage(const mtx::events::RoomEvent<T> &e)
|
||||||
|
-> std::enable_if_t<std::is_same<decltype(e.content.msgtype), std::string>::value, bool>
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
auto
|
||||||
|
isMessage(const mtx::events::Event<T> &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
auto
|
||||||
|
isMessage(const mtx::events::EncryptedEvent<T> &)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto
|
||||||
|
isMessage(const mtx::events::RoomEvent<mtx::events::msg::CallInvite> &)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto
|
||||||
|
isMessage(const mtx::events::RoomEvent<mtx::events::msg::CallAnswer> &)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto
|
||||||
|
isMessage(const mtx::events::RoomEvent<mtx::events::msg::CallHangUp> &)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Cache::saveState(const mtx::responses::Sync &res)
|
Cache::saveState(const mtx::responses::Sync &res)
|
||||||
{
|
{
|
||||||
|
@ -1623,6 +1664,25 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
saveTimelineMessages(txn, eventsDb, room.first, room.second.timeline);
|
saveTimelineMessages(txn, eventsDb, room.first, room.second.timeline);
|
||||||
|
|
||||||
RoomInfo updatedInfo;
|
RoomInfo updatedInfo;
|
||||||
|
{
|
||||||
|
// retrieve the old tags and modification ts
|
||||||
|
std::string_view data;
|
||||||
|
if (roomsDb_.get(txn, room.first, data)) {
|
||||||
|
try {
|
||||||
|
RoomInfo tmp = json::parse(std::string_view(data.data(), data.size()));
|
||||||
|
updatedInfo.tags = std::move(tmp.tags);
|
||||||
|
|
||||||
|
updatedInfo.approximate_last_modification_ts =
|
||||||
|
tmp.approximate_last_modification_ts;
|
||||||
|
} catch (const json::exception &e) {
|
||||||
|
nhlog::db()->warn("failed to parse room info: room_id ({}), {}: {}",
|
||||||
|
room.first,
|
||||||
|
std::string(data.data(), data.size()),
|
||||||
|
e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updatedInfo.name = getRoomName(txn, statesdb, membersdb).toStdString();
|
updatedInfo.name = getRoomName(txn, statesdb, membersdb).toStdString();
|
||||||
updatedInfo.topic = getRoomTopic(txn, statesdb).toStdString();
|
updatedInfo.topic = getRoomTopic(txn, statesdb).toStdString();
|
||||||
updatedInfo.avatar_url = getRoomAvatarUrl(txn, statesdb, membersdb).toStdString();
|
updatedInfo.avatar_url = getRoomAvatarUrl(txn, statesdb, membersdb).toStdString();
|
||||||
|
@ -1666,7 +1726,6 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
rooms_with_space_updates.insert(room.first);
|
rooms_with_space_updates.insert(room.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_new_tags = false;
|
|
||||||
// Process the account_data associated with this room
|
// Process the account_data associated with this room
|
||||||
if (!room.second.account_data.events.empty()) {
|
if (!room.second.account_data.events.empty()) {
|
||||||
auto accountDataDb = getAccountDataDb(txn, room.first);
|
auto accountDataDb = getAccountDataDb(txn, room.first);
|
||||||
|
@ -1691,7 +1750,8 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
// for tag events
|
// for tag events
|
||||||
if (std::holds_alternative<AccountDataEvent<account_data::Tags>>(evt)) {
|
if (std::holds_alternative<AccountDataEvent<account_data::Tags>>(evt)) {
|
||||||
auto tags_evt = std::get<AccountDataEvent<account_data::Tags>>(evt);
|
auto tags_evt = std::get<AccountDataEvent<account_data::Tags>>(evt);
|
||||||
has_new_tags = true;
|
|
||||||
|
updatedInfo.tags.clear();
|
||||||
for (const auto &tag : tags_evt.content.tags) {
|
for (const auto &tag : tags_evt.content.tags) {
|
||||||
updatedInfo.tags.push_back(tag.first);
|
updatedInfo.tags.push_back(tag.first);
|
||||||
}
|
}
|
||||||
|
@ -1704,20 +1764,12 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_new_tags) {
|
|
||||||
// retrieve the old tags, they haven't changed
|
for (const auto &e : room.second.timeline.events) {
|
||||||
std::string_view data;
|
if (!std::visit([](const auto &e) -> bool { return isMessage(e); }, e))
|
||||||
if (roomsDb_.get(txn, room.first, data)) {
|
continue;
|
||||||
try {
|
updatedInfo.approximate_last_modification_ts =
|
||||||
RoomInfo tmp = json::parse(std::string_view(data.data(), data.size()));
|
mtx::accessors::origin_server_ts(e).toMSecsSinceEpoch();
|
||||||
updatedInfo.tags = tmp.tags;
|
|
||||||
} catch (const json::exception &e) {
|
|
||||||
nhlog::db()->warn("failed to parse room info: room_id ({}), {}: {}",
|
|
||||||
room.first,
|
|
||||||
std::string(data.data(), data.size()),
|
|
||||||
e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
roomsDb_.put(txn, room.first, json(updatedInfo).dump());
|
roomsDb_.put(txn, room.first, json(updatedInfo).dump());
|
||||||
|
@ -4709,6 +4761,8 @@ to_json(json &j, const RoomInfo &info)
|
||||||
j["join_rule"] = info.join_rule;
|
j["join_rule"] = info.join_rule;
|
||||||
j["guest_access"] = info.guest_access;
|
j["guest_access"] = info.guest_access;
|
||||||
|
|
||||||
|
j["app_l_ts"] = info.approximate_last_modification_ts;
|
||||||
|
|
||||||
j["notification_count"] = info.notification_count;
|
j["notification_count"] = info.notification_count;
|
||||||
j["highlight_count"] = info.highlight_count;
|
j["highlight_count"] = info.highlight_count;
|
||||||
|
|
||||||
|
@ -4732,6 +4786,8 @@ from_json(const json &j, RoomInfo &info)
|
||||||
info.join_rule = j.at("join_rule");
|
info.join_rule = j.at("join_rule");
|
||||||
info.guest_access = j.at("guest_access");
|
info.guest_access = j.at("guest_access");
|
||||||
|
|
||||||
|
info.approximate_last_modification_ts = j.value("app_l_ts", 0);
|
||||||
|
|
||||||
info.notification_count = j.value("notification_count", 0);
|
info.notification_count = j.value("notification_count", 0);
|
||||||
info.highlight_count = j.value("highlight_count", 0);
|
info.highlight_count = j.value("highlight_count", 0);
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ struct DescInfo
|
||||||
QString userid;
|
QString userid;
|
||||||
QString body;
|
QString body;
|
||||||
QString descriptiveTime;
|
QString descriptiveTime;
|
||||||
uint64_t timestamp;
|
uint64_t timestamp = 0;
|
||||||
QDateTime datetime;
|
QDateTime datetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,6 +89,10 @@ struct RoomInfo
|
||||||
//! The list of tags associated with this room
|
//! The list of tags associated with this room
|
||||||
std::vector<std::string> tags;
|
std::vector<std::string> tags;
|
||||||
|
|
||||||
|
//! An approximate timestamp of when the last message was sent in the room.
|
||||||
|
//! Use the TimelineModel::lastMessage for an accurate timestamp.
|
||||||
|
uint64_t approximate_last_modification_ts = 0;
|
||||||
|
|
||||||
uint16_t highlight_count = 0;
|
uint16_t highlight_count = 0;
|
||||||
uint16_t notification_count = 0;
|
uint16_t notification_count = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,7 +121,7 @@ RoomlistModel::data(const QModelIndex &index, int role) const
|
||||||
case Roles::Time:
|
case Roles::Time:
|
||||||
return room->lastMessage().descriptiveTime;
|
return room->lastMessage().descriptiveTime;
|
||||||
case Roles::Timestamp:
|
case Roles::Timestamp:
|
||||||
return QVariant{static_cast<quint64>(room->lastMessage().timestamp)};
|
return QVariant{static_cast<quint64>(room->lastMessageTimestamp())};
|
||||||
case Roles::HasUnreadMessages:
|
case Roles::HasUnreadMessages:
|
||||||
return this->roomReadStatus.count(roomid) && this->roomReadStatus.at(roomid);
|
return this->roomReadStatus.count(roomid) && this->roomReadStatus.at(roomid);
|
||||||
case Roles::HasLoudNotification:
|
case Roles::HasLoudNotification:
|
||||||
|
@ -333,7 +333,7 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
|
||||||
emit totalUnreadMessageCountUpdated(total_unread_msgs);
|
emit totalUnreadMessageCountUpdated(total_unread_msgs);
|
||||||
});
|
});
|
||||||
|
|
||||||
newRoom->updateLastMessage();
|
// newRoom->updateLastMessage();
|
||||||
|
|
||||||
std::vector<QString> previewsToAdd;
|
std::vector<QString> previewsToAdd;
|
||||||
if (newRoom->isSpace()) {
|
if (newRoom->isSpace()) {
|
||||||
|
|
|
@ -353,14 +353,13 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, permissions_{room_id_}
|
, permissions_{room_id_}
|
||||||
{
|
{
|
||||||
lastMessage_.timestamp = 0;
|
|
||||||
|
|
||||||
this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
|
this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
|
||||||
|
|
||||||
auto roomInfo = cache::singleRoomInfo(room_id_.toStdString());
|
auto roomInfo = cache::singleRoomInfo(room_id_.toStdString());
|
||||||
this->isSpace_ = roomInfo.is_space;
|
this->isSpace_ = roomInfo.is_space;
|
||||||
this->notification_count = roomInfo.notification_count;
|
this->notification_count = roomInfo.notification_count;
|
||||||
this->highlight_count = roomInfo.highlight_count;
|
this->highlight_count = roomInfo.highlight_count;
|
||||||
|
lastMessage_.timestamp = roomInfo.approximate_last_modification_ts;
|
||||||
|
|
||||||
// this connection will simplify adding the plainRoomNameChanged() signal everywhere that it
|
// this connection will simplify adding the plainRoomNameChanged() signal everywhere that it
|
||||||
// needs to be
|
// needs to be
|
||||||
|
@ -1025,10 +1024,21 @@ isYourJoin(const mtx::events::Event<T> &)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DescInfo
|
||||||
|
TimelineModel::lastMessage() const
|
||||||
|
{
|
||||||
|
if (lastMessage_.event_id.isEmpty())
|
||||||
|
QTimer::singleShot(0, this, &TimelineModel::updateLastMessage);
|
||||||
|
|
||||||
|
return lastMessage_;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineModel::updateLastMessage()
|
TimelineModel::updateLastMessage()
|
||||||
{
|
{
|
||||||
for (auto it = events.size() - 1; it >= 0; --it) {
|
// only try to generate a preview for the last 1000 messages
|
||||||
|
auto end = std::max(events.size() - 1001, 0);
|
||||||
|
for (auto it = events.size() - 1; it >= end; --it) {
|
||||||
auto event = events.get(it, decryptDescription);
|
auto event = events.get(it, decryptDescription);
|
||||||
if (!event)
|
if (!event)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -311,7 +311,9 @@ public:
|
||||||
void sendMessageEvent(const T &content, mtx::events::EventType eventType);
|
void sendMessageEvent(const T &content, mtx::events::EventType eventType);
|
||||||
RelatedInfo relatedInfo(const QString &id);
|
RelatedInfo relatedInfo(const QString &id);
|
||||||
|
|
||||||
DescInfo lastMessage() const { return lastMessage_; }
|
DescInfo lastMessage() const;
|
||||||
|
uint64_t lastMessageTimestamp() const { return lastMessage_.timestamp; }
|
||||||
|
|
||||||
bool isSpace() const { return isSpace_; }
|
bool isSpace() const { return isSpace_; }
|
||||||
bool isEncrypted() const { return isEncrypted_; }
|
bool isEncrypted() const { return isEncrypted_; }
|
||||||
crypto::Trust trustlevel() const;
|
crypto::Trust trustlevel() const;
|
||||||
|
|
Loading…
Reference in a new issue