store: expose metadata()/set_metadata()

Allow for storing metadata in the database, and consider the cache.
This commit is contained in:
Dirk-Jan C. Binnema 2022-02-13 15:22:09 +02:00
parent 3820118246
commit 3086238b33
3 changed files with 60 additions and 31 deletions

View File

@ -235,8 +235,7 @@ Indexer::Private::item_worker()
++progress_.updated; ++progress_.updated;
break; break;
case WorkItem::Type::Dir: case WorkItem::Type::Dir:
store_.set_dirstamp(item.full_path, ::time(NULL), store_.set_dirstamp(item.full_path, ::time(NULL));
true /*use-transaction*/);
break; break;
default: default:
g_warn_if_reached(); g_warn_if_reached();

View File

@ -25,6 +25,7 @@
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <unordered_map> #include <unordered_map>
#include <atomic> #include <atomic>
#include <type_traits> #include <type_traits>
@ -194,10 +195,10 @@ struct Store::Private {
}); });
} }
g_debug("committing transaction (n=%zu,%zu)", g_debug("committing transaction (n=%zu,%zu)",
transaction_size_, metadatas_.size()); transaction_size_, metadata_cache_.size());
xapian_try([this] { xapian_try([this] {
writable_db().commit_transaction(); writable_db().commit_transaction();
for (auto&& mdata : metadatas_) for (auto&& mdata : metadata_cache_)
writable_db().set_metadata(mdata.first, mdata.second); writable_db().set_metadata(mdata.first, mdata.second);
transaction_size_ = 0; transaction_size_ = 0;
}); });
@ -276,8 +277,7 @@ struct Store::Private {
Xapian::Document new_doc_from_message(MuMsg* msg); Xapian::Document new_doc_from_message(MuMsg* msg);
/* metadata to write as part of a transaction commit */ /* metadata to write as part of a transaction commit */
using StringPair = std::pair<std::string, std::string>; std::unordered_map<std::string, std::string> metadata_cache_;
std::vector<StringPair> metadatas_;
const bool read_only_{}; const bool read_only_{};
std::unique_ptr<Xapian::Database> db_; std::unique_ptr<Xapian::Database> db_;
@ -485,41 +485,54 @@ Store::remove_messages(const std::vector<Store::Id>& ids)
priv_->transaction_maybe_commit(true /*force*/); priv_->transaction_maybe_commit(true /*force*/);
} }
time_t
Store::dirstamp(const std::string& path) const std::string
Store::metadata(const std::string& key) const
{ {
// get metadata either from the (uncommitted) cache or from the store.
std::lock_guard guard{priv_->lock_}; std::lock_guard guard{priv_->lock_};
constexpr auto epoch = static_cast<time_t>(0); const auto it = priv_->metadata_cache_.find(key);
return xapian_try([&] { if (it != priv_->metadata_cache_.end())
const auto ts = priv_->db().get_metadata(path); return it->second;
if (ts.empty()) else
return epoch; return xapian_try([&] {
else return priv_->db().get_metadata(key);
return static_cast<time_t>(strtoll(ts.c_str(), NULL, 16)); }, "");
},
epoch);
} }
void void
Store::set_dirstamp(const std::string& path, time_t tstamp, bool use_transaction) Store::set_metadata(const std::string& key, const std::string& val)
{ {
// get metadata either from the (uncommitted) cache or from the store.
std::lock_guard guard{priv_->lock_}; std::lock_guard guard{priv_->lock_};
std::array<char, 2 * sizeof(tstamp) + 1> data{}; priv_->metadata_cache_.erase(key);
priv_->metadata_cache_.emplace(key, val);
}
time_t
Store::dirstamp(const std::string& path) const
{
constexpr auto epoch = static_cast<time_t>(0);
const auto ts{metadata(path)};
if (ts.empty())
return epoch;
else
return static_cast<time_t>(strtoll(ts.c_str(), NULL, 16));
}
void
Store::set_dirstamp(const std::string& path, time_t tstamp)
{
std::array<char, 2 * sizeof(tstamp) + 1> data{};
const auto len = static_cast<size_t>( const auto len = static_cast<size_t>(
g_snprintf(data.data(), data.size(), "%zx", tstamp)); g_snprintf(data.data(), data.size(), "%zx", tstamp));
/* set_metadata is not otherwise part of a "transaction" but we want it set_metadata(path, std::string{data.data(), len});
* to be so, so a dirstamp _only_ gets updated when the messages in the
* dir are commited */
Private::StringPair item{path, std::string{data.data(), len}};
if (use_transaction)
priv_->metadatas_.emplace_back(std::move(item));
else
xapian_try([&] { priv_->writable_db().set_metadata(item.first, item.second); });
} }
MuMsg* MuMsg*

View File

@ -283,6 +283,25 @@ public:
*/ */
size_t for_each_term(const std::string& field, ForEachTermFunc func) const; size_t for_each_term(const std::string& field, ForEachTermFunc func) const;
/**
* Get the store metadata for @p key
*
* @param key the metadata key
*
* @return the metadata value or empty for none.
*/
std::string metadata(const std::string& key) const;
/**
* Write metadata to the store.
*
* @param key key
* @param val value
*/
void set_metadata(const std::string& key, const std::string& val);
/** /**
* Get the timestamp for some message, or 0 if not found * Get the timestamp for some message, or 0 if not found
* *
@ -306,10 +325,8 @@ public:
* *
* @param path a filesystem path * @param path a filesystem path
* @param tstamp the timestamp for that path * @param tstamp the timestamp for that path
* @param whether to do this as part of a transaction
*/ */
void set_dirstamp(const std::string& path, time_t tstamp, void set_dirstamp(const std::string& path, time_t tstamp);
bool use_transaction = false);
/** /**
* Get the number of documents in the document database * Get the number of documents in the document database