store: implement serialize method, use it
For triggering saving the contacts / labels caches.
This commit is contained in:
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user