mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Don't store pending receipts in cache
We don't get notified for every message. Sometimes we only get a read receipt for the newest message, which means old read receipts accumulate in the database. This least to some considerable CPU overhead, when checking if the timeline should be notified for new read receipts. Instead just always notify, since that has far less overhead in the worst case and doesn't need complicated cache cleanup. The old pending_receipts db is not removed for now. It should still have minimal storage overhead and we don't have a good mechanism for cache format upgrades atm.
This commit is contained in:
parent
2997155f56
commit
e6fcccc8bd
4 changed files with 7 additions and 170 deletions
147
src/Cache.cpp
147
src/Cache.cpp
|
@ -62,6 +62,7 @@ constexpr auto SYNC_STATE_DB("sync_state");
|
||||||
//! Read receipts per room/event.
|
//! Read receipts per room/event.
|
||||||
constexpr auto READ_RECEIPTS_DB("read_receipts");
|
constexpr auto READ_RECEIPTS_DB("read_receipts");
|
||||||
constexpr auto NOTIFICATIONS_DB("sent_notifications");
|
constexpr auto NOTIFICATIONS_DB("sent_notifications");
|
||||||
|
//! TODO: delete pending_receipts database on old cache versions
|
||||||
|
|
||||||
//! Encryption related databases.
|
//! Encryption related databases.
|
||||||
|
|
||||||
|
@ -703,70 +704,6 @@ Cache::setCurrentFormat()
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<QString>
|
|
||||||
Cache::pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id)
|
|
||||||
{
|
|
||||||
auto db = getPendingReceiptsDb(txn);
|
|
||||||
|
|
||||||
std::string key, unused;
|
|
||||||
std::vector<QString> pending;
|
|
||||||
|
|
||||||
auto cursor = lmdb::cursor::open(txn, db);
|
|
||||||
while (cursor.get(key, unused, MDB_NEXT)) {
|
|
||||||
ReadReceiptKey receipt;
|
|
||||||
try {
|
|
||||||
receipt = json::parse(key);
|
|
||||||
} catch (const nlohmann::json::exception &e) {
|
|
||||||
nhlog::db()->warn("pendingReceiptsEvents: {}", e.what());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receipt.room_id == room_id)
|
|
||||||
pending.emplace_back(QString::fromStdString(receipt.event_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor.close();
|
|
||||||
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Cache::removePendingReceipt(lmdb::txn &txn, const std::string &room_id, const std::string &event_id)
|
|
||||||
{
|
|
||||||
auto db = getPendingReceiptsDb(txn);
|
|
||||||
|
|
||||||
ReadReceiptKey receipt_key{event_id, room_id};
|
|
||||||
auto key = json(receipt_key).dump();
|
|
||||||
|
|
||||||
try {
|
|
||||||
lmdb::dbi_del(txn, db, lmdb::val(key.data(), key.size()), nullptr);
|
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
nhlog::db()->critical("removePendingReceipt: {}", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Cache::addPendingReceipt(const QString &room_id, const QString &event_id)
|
|
||||||
{
|
|
||||||
auto txn = lmdb::txn::begin(env_);
|
|
||||||
auto db = getPendingReceiptsDb(txn);
|
|
||||||
|
|
||||||
ReadReceiptKey receipt_key{event_id.toStdString(), room_id.toStdString()};
|
|
||||||
auto key = json(receipt_key).dump();
|
|
||||||
std::string empty;
|
|
||||||
|
|
||||||
try {
|
|
||||||
lmdb::dbi_put(txn,
|
|
||||||
db,
|
|
||||||
lmdb::val(key.data(), key.size()),
|
|
||||||
lmdb::val(empty.data(), empty.size()));
|
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
nhlog::db()->critical("addPendingReceipt: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
txn.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedReceipts
|
CachedReceipts
|
||||||
Cache::readReceipts(const QString &event_id, const QString &room_id)
|
Cache::readReceipts(const QString &event_id, const QString &room_id)
|
||||||
{
|
{
|
||||||
|
@ -802,30 +739,6 @@ Cache::readReceipts(const QString &event_id, const QString &room_id)
|
||||||
return receipts;
|
return receipts;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<QString>
|
|
||||||
Cache::filterReadEvents(const QString &room_id,
|
|
||||||
const std::vector<QString> &event_ids,
|
|
||||||
const std::string &excluded_user)
|
|
||||||
{
|
|
||||||
std::vector<QString> read_events;
|
|
||||||
|
|
||||||
for (const auto &event : event_ids) {
|
|
||||||
auto receipts = readReceipts(event, room_id);
|
|
||||||
|
|
||||||
if (receipts.size() == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (receipts.size() == 1) {
|
|
||||||
if (receipts.begin()->second == excluded_user)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_events.emplace_back(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return read_events;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Receipts &receipts)
|
Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Receipts &receipts)
|
||||||
{
|
{
|
||||||
|
@ -881,27 +794,6 @@ Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Recei
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Cache::notifyForReadReceipts(const std::string &room_id)
|
|
||||||
{
|
|
||||||
auto txn = lmdb::txn::begin(env_);
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
auto local_user = settings.value("auth/user_id").toString();
|
|
||||||
|
|
||||||
auto matches = filterReadEvents(QString::fromStdString(room_id),
|
|
||||||
pendingReceiptsEvents(txn, room_id),
|
|
||||||
local_user.toStdString());
|
|
||||||
|
|
||||||
for (const auto &m : matches)
|
|
||||||
removePendingReceipt(txn, room_id, m.toStdString());
|
|
||||||
|
|
||||||
if (!matches.empty())
|
|
||||||
emit newReadReceipts(QString::fromStdString(room_id), matches);
|
|
||||||
|
|
||||||
txn.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Cache::calculateRoomReadStatus()
|
Cache::calculateRoomReadStatus()
|
||||||
{
|
{
|
||||||
|
@ -1019,7 +911,12 @@ Cache::saveState(const mtx::responses::Sync &res)
|
||||||
std::map<QString, bool> readStatus;
|
std::map<QString, bool> readStatus;
|
||||||
|
|
||||||
for (const auto &room : res.rooms.join) {
|
for (const auto &room : res.rooms.join) {
|
||||||
notifyForReadReceipts(room.first);
|
if (!room.second.ephemeral.receipts.empty()) {
|
||||||
|
std::vector<QString> receipts;
|
||||||
|
for (const auto &receipt : room.second.ephemeral.receipts)
|
||||||
|
receipts.push_back(QString::fromStdString(receipt.first));
|
||||||
|
emit newReadReceipts(QString::fromStdString(room.first), receipts);
|
||||||
|
}
|
||||||
readStatus.emplace(QString::fromStdString(room.first),
|
readStatus.emplace(QString::fromStdString(room.first),
|
||||||
calculateRoomReadStatus(room.first));
|
calculateRoomReadStatus(room.first));
|
||||||
}
|
}
|
||||||
|
@ -2634,36 +2531,6 @@ readReceipts(const QString &event_id, const QString &room_id)
|
||||||
return instance_->readReceipts(event_id, room_id);
|
return instance_->readReceipts(event_id, room_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Filter the events that have at least one read receipt.
|
|
||||||
std::vector<QString>
|
|
||||||
filterReadEvents(const QString &room_id,
|
|
||||||
const std::vector<QString> &event_ids,
|
|
||||||
const std::string &excluded_user)
|
|
||||||
{
|
|
||||||
return instance_->filterReadEvents(room_id, event_ids, excluded_user);
|
|
||||||
}
|
|
||||||
//! Add event for which we are expecting some read receipts.
|
|
||||||
void
|
|
||||||
addPendingReceipt(const QString &room_id, const QString &event_id)
|
|
||||||
{
|
|
||||||
instance_->addPendingReceipt(room_id, event_id);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
removePendingReceipt(lmdb::txn &txn, const std::string &room_id, const std::string &event_id)
|
|
||||||
{
|
|
||||||
instance_->removePendingReceipt(txn, room_id, event_id);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
notifyForReadReceipts(const std::string &room_id)
|
|
||||||
{
|
|
||||||
instance_->notifyForReadReceipts(room_id);
|
|
||||||
}
|
|
||||||
std::vector<QString>
|
|
||||||
pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id)
|
|
||||||
{
|
|
||||||
return instance_->pendingReceiptsEvents(txn, room_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray
|
QByteArray
|
||||||
image(const QString &url)
|
image(const QString &url)
|
||||||
{
|
{
|
||||||
|
|
15
src/Cache.h
15
src/Cache.h
|
@ -154,21 +154,6 @@ using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>
|
||||||
UserReceipts
|
UserReceipts
|
||||||
readReceipts(const QString &event_id, const QString &room_id);
|
readReceipts(const QString &event_id, const QString &room_id);
|
||||||
|
|
||||||
//! Filter the events that have at least one read receipt.
|
|
||||||
std::vector<QString>
|
|
||||||
filterReadEvents(const QString &room_id,
|
|
||||||
const std::vector<QString> &event_ids,
|
|
||||||
const std::string &excluded_user);
|
|
||||||
//! Add event for which we are expecting some read receipts.
|
|
||||||
void
|
|
||||||
addPendingReceipt(const QString &room_id, const QString &event_id);
|
|
||||||
void
|
|
||||||
removePendingReceipt(lmdb::txn &txn, const std::string &room_id, const std::string &event_id);
|
|
||||||
void
|
|
||||||
notifyForReadReceipts(const std::string &room_id);
|
|
||||||
std::vector<QString>
|
|
||||||
pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
|
||||||
|
|
||||||
QByteArray
|
QByteArray
|
||||||
image(const QString &url);
|
image(const QString &url);
|
||||||
QByteArray
|
QByteArray
|
||||||
|
|
|
@ -137,18 +137,6 @@ public:
|
||||||
using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>;
|
using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>;
|
||||||
UserReceipts readReceipts(const QString &event_id, const QString &room_id);
|
UserReceipts readReceipts(const QString &event_id, const QString &room_id);
|
||||||
|
|
||||||
//! Filter the events that have at least one read receipt.
|
|
||||||
std::vector<QString> filterReadEvents(const QString &room_id,
|
|
||||||
const std::vector<QString> &event_ids,
|
|
||||||
const std::string &excluded_user);
|
|
||||||
//! Add event for which we are expecting some read receipts.
|
|
||||||
void addPendingReceipt(const QString &room_id, const QString &event_id);
|
|
||||||
void removePendingReceipt(lmdb::txn &txn,
|
|
||||||
const std::string &room_id,
|
|
||||||
const std::string &event_id);
|
|
||||||
void notifyForReadReceipts(const std::string &room_id);
|
|
||||||
std::vector<QString> pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
|
||||||
|
|
||||||
QByteArray image(const QString &url) const;
|
QByteArray image(const QString &url) const;
|
||||||
QByteArray image(lmdb::txn &txn, const std::string &url) const;
|
QByteArray image(lmdb::txn &txn, const std::string &url) const;
|
||||||
void saveImage(const std::string &url, const std::string &data);
|
void saveImage(const std::string &url, const std::string &data);
|
||||||
|
|
|
@ -173,9 +173,6 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
|
||||||
// mark our messages as read
|
// mark our messages as read
|
||||||
readEvent(event_id.toStdString());
|
readEvent(event_id.toStdString());
|
||||||
|
|
||||||
// ask to be notified for read receipts
|
|
||||||
cache::addPendingReceipt(room_id_, event_id);
|
|
||||||
|
|
||||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
emit dataChanged(index(idx, 0), index(idx, 0));
|
||||||
|
|
||||||
if (pending.size() > 0)
|
if (pending.size() > 0)
|
||||||
|
|
Loading…
Reference in a new issue