diff --git a/lib/mu-server.cc b/lib/mu-server.cc index f5f455fa..9aae7cfe 100644 --- a/lib/mu-server.cc +++ b/lib/mu-server.cc @@ -889,6 +889,7 @@ Server::Private::index_handler(const Command& cmd) throw Error{Error::Code::Xapian, "indexer is already running"}; do_index(conf); + store().serialize(); } void diff --git a/lib/mu-store-labels.hh b/lib/mu-store-labels.hh index b36c9bd1..df4dde02 100644 --- a/lib/mu-store-labels.hh +++ b/lib/mu-store-labels.hh @@ -17,7 +17,6 @@ ** */ - #ifndef MU_LABELS_CACHE_HH #define MU_LABELS_CACHE_HH @@ -59,6 +58,7 @@ public: label_map_.insert({label, 1}); else ++it->second; + dirty_ = true; } /** * Remove a label occurrence from the cache @@ -73,6 +73,7 @@ public: label_map_.erase(it); else --it->second; + dirty_ = true; } } @@ -107,13 +108,15 @@ public: /** * Serialize the cache into a string. * + * Note: this also marks the cache a _non_ dirty; + * * @return serialized cache */ - std::string serialize() const { + [[nodiscard]] std::string serialize() const { std::string s; for (const auto&[label, n]: label_map_) s += mu_format("{}{}{}\n", label, SepaChar2, n); - + dirty_ = false; return s; } @@ -140,8 +143,20 @@ public: 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: Map label_map_; + mutable bool dirty_{}; }; class Store; diff --git a/lib/mu-store.cc b/lib/mu-store.cc index 8d0f1219..0d4dba64 100644 --- a/lib/mu-store.cc +++ b/lib/mu-store.cc @@ -85,11 +85,11 @@ struct Store::Private { } ~Private() try { - mu_debug("closing store @ {}", xapian_db_.path()); if (!xapian_db_.read_only()) { - contacts_cache_.serialize(); - config_.set(labels_cache_.serialize()); + if (const auto res = serialize(); !res) + mu_critical("failed to serialize: {}", res.error().what()); } + mu_debug("closing store @ {}", xapian_db_.path()); } catch (...) { mu_critical("caught exception in store dtor"); } @@ -119,6 +119,8 @@ struct Store::Private { return Message::Options::None; } + Result serialize(); + Option find_message_unlocked(Store::Id docid) const; Store::IdVec find_duplicates_unlocked(const Store& store, const std::string& message_id) const; @@ -145,6 +147,23 @@ struct Store::Private { }; +Result +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(labels_cache_.serialize()); + + return Ok(); +} + + Result Store::Private::add_message_unlocked(Message& msg) { @@ -335,6 +354,13 @@ Store::contacts_cache() const return priv_->contacts_cache_; } +Result +Store::serialize() +{ + std::lock_guard guard{priv_->lock_}; + return priv_->serialize(); +} + Indexer& Store::indexer() { diff --git a/lib/mu-store.hh b/lib/mu-store.hh index e23978be..555f2f03 100644 --- a/lib/mu-store.hh +++ b/lib/mu-store.hh @@ -140,6 +140,17 @@ public: */ 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 serialize(); + /** * Get the Indexer associated with this store. It is an error to call * this on a read-only store.