store: implement serialize method, use it

For triggering saving the contacts / labels caches.
This commit is contained in:
Dirk-Jan C. Binnema
2025-09-01 08:21:37 +03:00
committed by Seth Ladygo
parent 5ba4791add
commit bb399bc37a
4 changed files with 59 additions and 6 deletions

View File

@ -889,6 +889,7 @@ Server::Private::index_handler(const Command& cmd)
throw Error{Error::Code::Xapian, "indexer is already running"}; throw Error{Error::Code::Xapian, "indexer is already running"};
do_index(conf); do_index(conf);
store().serialize();
} }
void void

View File

@ -17,7 +17,6 @@
** **
*/ */
#ifndef MU_LABELS_CACHE_HH #ifndef MU_LABELS_CACHE_HH
#define MU_LABELS_CACHE_HH #define MU_LABELS_CACHE_HH
@ -59,6 +58,7 @@ public:
label_map_.insert({label, 1}); label_map_.insert({label, 1});
else else
++it->second; ++it->second;
dirty_ = true;
} }
/** /**
* Remove a label occurrence from the cache * Remove a label occurrence from the cache
@ -73,6 +73,7 @@ public:
label_map_.erase(it); label_map_.erase(it);
else else
--it->second; --it->second;
dirty_ = true;
} }
} }
@ -107,13 +108,15 @@ public:
/** /**
* Serialize the cache into a string. * Serialize the cache into a string.
* *
* Note: this also marks the cache a _non_ dirty;
*
* @return serialized cache * @return serialized cache
*/ */
std::string serialize() const { [[nodiscard]] std::string serialize() const {
std::string s; std::string s;
for (const auto&[label, n]: label_map_) for (const auto&[label, n]: label_map_)
s += mu_format("{}{}{}\n", label, SepaChar2, n); s += mu_format("{}{}{}\n", label, SepaChar2, n);
dirty_ = false;
return s; return s;
} }
@ -140,8 +143,20 @@ public:
return map; return map;
} }
/**
* Is the cache "dirty"?
*
* I.e. have there been changes since "serialize()" was called?
*
* @return true or false
*/
bool dirty() const {
return dirty_;
}
private: private:
Map label_map_; Map label_map_;
mutable bool dirty_{};
}; };
class Store; class Store;

View File

@ -85,11 +85,11 @@ struct Store::Private {
} }
~Private() try { ~Private() try {
mu_debug("closing store @ {}", xapian_db_.path());
if (!xapian_db_.read_only()) { if (!xapian_db_.read_only()) {
contacts_cache_.serialize(); if (const auto res = serialize(); !res)
config_.set<Config::Id::Labels>(labels_cache_.serialize()); mu_critical("failed to serialize: {}", res.error().what());
} }
mu_debug("closing store @ {}", xapian_db_.path());
} catch (...) { } catch (...) {
mu_critical("caught exception in store dtor"); mu_critical("caught exception in store dtor");
} }
@ -119,6 +119,8 @@ struct Store::Private {
return Message::Options::None; return Message::Options::None;
} }
Result<void> serialize();
Option<Message> find_message_unlocked(Store::Id docid) const; Option<Message> find_message_unlocked(Store::Id docid) const;
Store::IdVec find_duplicates_unlocked(const Store& store, Store::IdVec find_duplicates_unlocked(const Store& store,
const std::string& message_id) const; const std::string& message_id) const;
@ -145,6 +147,23 @@ struct Store::Private {
}; };
Result<void>
Store::Private::serialize()
{
if (xapian_db_.read_only())
return Err(Error::Code::Store, "store must be writable");
mu_debug("serialize data into store @ {}", xapian_db_.path());
contacts_cache_.serialize(); // does the 'dirty' check internally.
if (labels_cache_.dirty())
config_.set<Config::Id::Labels>(labels_cache_.serialize());
return Ok();
}
Result<Store::Id> Result<Store::Id>
Store::Private::add_message_unlocked(Message& msg) Store::Private::add_message_unlocked(Message& msg)
{ {
@ -335,6 +354,13 @@ Store::contacts_cache() const
return priv_->contacts_cache_; return priv_->contacts_cache_;
} }
Result<void>
Store::serialize()
{
std::lock_guard guard{priv_->lock_};
return priv_->serialize();
}
Indexer& Indexer&
Store::indexer() Store::indexer()
{ {

View File

@ -140,6 +140,17 @@ public:
*/ */
const ContactsCache& contacts_cache() const; const ContactsCache& contacts_cache() const;
/**
* Serialize ephemeral information such as contacts, labels,
* in the database.
*
* Only available when using a writable database.
*
* @return Ok() or some error.
*/
Result<void> serialize();
/** /**
* Get the Indexer associated with this store. It is an error to call * Get the Indexer associated with this store. It is an error to call
* this on a read-only store. * this on a read-only store.