Fix stuck unread messages by storing state events in the db

This may increase the db size by a factor of 1000 in the worst case and
it will need some fixes, when we decide to not show some events in the
timeline, but it should work for now.
This commit is contained in:
Nicolas Werner 2020-02-24 01:07:25 +01:00
parent 62b962cb44
commit 1eb2869fa8
3 changed files with 40 additions and 14 deletions

View file

@ -43,9 +43,9 @@ static lmdb::val NEXT_BATCH_KEY("next_batch");
static lmdb::val OLM_ACCOUNT_KEY("olm_account"); static lmdb::val OLM_ACCOUNT_KEY("olm_account");
static lmdb::val CACHE_FORMAT_VERSION_KEY("cache_format_version"); static lmdb::val CACHE_FORMAT_VERSION_KEY("cache_format_version");
constexpr size_t MAX_RESTORED_MESSAGES = 30; constexpr size_t MAX_RESTORED_MESSAGES = 30'000;
constexpr auto DB_SIZE = 32UL * 1024UL * 1024UL * 1024ULL; // 32 GB constexpr auto DB_SIZE = 32ULL * 1024ULL * 1024ULL * 1024ULL; // 32 GB
constexpr auto MAX_DBS = 8092UL; constexpr auto MAX_DBS = 8092UL;
//! Cache databases and their format. //! Cache databases and their format.
@ -915,16 +915,17 @@ Cache::calculateRoomReadStatus(const std::string &room_id)
auto txn = lmdb::txn::begin(env_); auto txn = lmdb::txn::begin(env_);
// Get last event id on the room. // Get last event id on the room.
const auto last_event_id = getLastMessageInfo(txn, room_id).event_id; const auto last_event_id = getLastEventId(txn, room_id);
const auto localUser = utils::localUser().toStdString(); const auto localUser = utils::localUser().toStdString();
if (last_event_id.isEmpty())
return false;
txn.commit(); txn.commit();
if (last_event_id.empty())
return false;
// Retrieve all read receipts for that event. // Retrieve all read receipts for that event.
const auto receipts = readReceipts(last_event_id, QString::fromStdString(room_id)); const auto receipts =
readReceipts(QString::fromStdString(last_event_id), QString::fromStdString(room_id));
if (receipts.size() == 0) if (receipts.size() == 0)
return true; return true;
@ -1258,6 +1259,7 @@ Cache::getTimelineMentions()
mtx::responses::Timeline mtx::responses::Timeline
Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id) Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id)
{ {
// TODO(nico): Limit the messages returned by this maybe?
auto db = getMessagesDb(txn, room_id); auto db = getMessagesDb(txn, room_id);
mtx::responses::Timeline timeline; mtx::responses::Timeline timeline;
@ -1325,6 +1327,31 @@ Cache::roomInfo(bool withInvites)
return result; return result;
} }
std::string
Cache::getLastEventId(lmdb::txn &txn, const std::string &room_id)
{
auto db = getMessagesDb(txn, room_id);
if (db.size(txn) == 0)
return {};
std::string timestamp, msg;
auto cursor = lmdb::cursor::open(txn, db);
while (cursor.get(timestamp, msg, MDB_NEXT)) {
auto obj = json::parse(msg);
if (obj.count("event") == 0)
continue;
cursor.close();
return obj["event"]["event_id"];
}
cursor.close();
return {};
}
DescInfo DescInfo
Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id)
{ {
@ -1336,13 +1363,14 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id)
std::string timestamp, msg; std::string timestamp, msg;
QSettings settings; QSettings settings;
auto local_user = settings.value("auth/user_id").toString(); const auto local_user = utils::localUser();
auto cursor = lmdb::cursor::open(txn, db); auto cursor = lmdb::cursor::open(txn, db);
while (cursor.get(timestamp, msg, MDB_NEXT)) { while (cursor.get(timestamp, msg, MDB_NEXT)) {
auto obj = json::parse(msg); auto obj = json::parse(msg);
if (obj.count("event") == 0) if (obj.count("event") == 0 || !(obj["event"]["type"] == "m.room.message" ||
obj["event"]["type"] == "m.sticker"))
continue; continue;
mtx::events::collections::TimelineEvent event; mtx::events::collections::TimelineEvent event;
@ -1937,9 +1965,6 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
using namespace mtx::events::state; using namespace mtx::events::state;
for (const auto &e : res.events) { for (const auto &e : res.events) {
if (isStateEvent(e))
continue;
if (std::holds_alternative<RedactionEvent<msg::Redaction>>(e)) if (std::holds_alternative<RedactionEvent<msg::Redaction>>(e))
continue; continue;

View file

@ -250,6 +250,7 @@ private:
QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
std::string getLastEventId(lmdb::txn &txn, const std::string &room_id);
DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id); DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id);
void saveTimelineMessages(lmdb::txn &txn, void saveTimelineMessages(lmdb::txn &txn,
const std::string &room_id, const std::string &room_id,

View file

@ -17,6 +17,7 @@
#include "Cache.h" #include "Cache.h"
#include "Config.h" #include "Config.h"
#include "MatrixClient.h"
using TimelineEvent = mtx::events::collections::TimelineEvents; using TimelineEvent = mtx::events::collections::TimelineEvents;
@ -44,8 +45,7 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin
QString QString
utils::localUser() utils::localUser()
{ {
QSettings settings; return QString::fromStdString(http::client()->user_id().to_string());
return settings.value("auth/user_id").toString();
} }
QString QString