store: support reinit

Support reinitializing, based on some current store. This is useful for
upgrading. Note that this is only the backend implementation + tests.
This commit is contained in:
Dirk-Jan C. Binnema 2022-09-28 22:38:06 +03:00
parent 9e60ebb683
commit 5367122c08
3 changed files with 84 additions and 44 deletions

View File

@ -303,44 +303,31 @@ Store::Private::find_message_unlocked(Store::Id docid) const
Store::Store(const std::string& path, Store::Options opts)
: priv_{std::make_unique<Private>(path, none_of(opts & Store::Options::Writable))}
{
if (properties().schema_version == ExpectedSchemaVersion)
return; // all is good.
if (none_of(opts & Store::Options::Writable) &&
any_of(opts & Store::Options::ReInit))
throw Mu::Error(Error::Code::InvalidArgument,
"Options::ReInit requires Options::Writable");
// Now, it seems the schema versions do not match; shall we automatically
// update?
if (none_of(opts & Store::Options::AutoUpgrade)) {
// not allowed to auto-upgrade, so we give up.
throw Mu::Error(Error::Code::SchemaMismatch,
"expected schema-version %s, but got %s; "
"cannot auto-upgrade; please use 'mu init'",
ExpectedSchemaVersion,
properties().schema_version.c_str());
if (any_of(opts & Store::Options::ReInit)) {
/* user wants to re-initialize an existing store */
Config conf{};
conf.batch_size = properties().batch_size;
conf.max_message_size = properties().max_message_size;
const auto root_maildir{properties().root_maildir};
const auto addrs{properties().personal_addresses};
/* close the old one */
this->priv_.reset();
/* and create a new one. */
Store new_store(path, root_maildir, addrs, conf);
this->priv_ = std::move(new_store.priv_);
}
// Okay, let's attempt an auto-upgrade.
g_info("attempt reinit database from schema %s --> %s",
properties().schema_version.c_str(), ExpectedSchemaVersion);
Config conf;
conf.batch_size = properties().batch_size;
conf.max_message_size = properties().max_message_size;
priv_.reset();
priv_ = std::make_unique<Private>(path,
properties().root_maildir,
properties().personal_addresses,
conf);
// Now let's try again.
priv_.reset();
priv_ = std::make_unique<Private>(path, none_of(opts & Store::Options::Writable));
/* otherwise, the schema version should match. */
if (properties().schema_version != ExpectedSchemaVersion)
// Nope, we failed.
throw Mu::Error(Error::Code::SchemaMismatch,
"failed to auto-upgrade from %s to %s; "
"please use 'mu init'",
properties().schema_version.c_str(),
ExpectedSchemaVersion);
"expected schema-version %s, but got %s",
ExpectedSchemaVersion,
properties().schema_version.c_str());
}
Store::Store(const std::string& path,

View File

@ -45,15 +45,11 @@ public:
/**
* Configuration options.
*
* @param path
* @param readonly
*/
enum struct Options {
None = 0, /**< No specific options */
Writable = 1 << 0, /**< Open in writable mode */
AutoUpgrade = 1 << 1, /**< automatically re-initialize
* versions do not match */
None = 0, /**< No specific options */
Writable = 1 << 0, /**< Open in writable mode */
ReInit = 1 << 1, /**< Re-initialize based on existing */
};
/**
@ -62,7 +58,7 @@ public:
* @param path path to the database
* @param options startup options
*
* A store or an error.
* @return A store or an error.
*/
static Result<Store> make(const std::string& path,
Options opts=Options::None) noexcept try {
@ -93,6 +89,8 @@ public:
* @param personal_addresses addresses that should be recognized as
* 'personal' for identifying personal messages.
* @param config a configuration object
*
* @return a store or an error
*/
static Result<Store> make_new(const std::string& path,
const std::string& maildir,
@ -448,6 +446,7 @@ private:
*/
Store(const std::string& path, Options opts=Options::None);
/**
* Construct a store for a not-yet-existing document database
*

View File

@ -53,6 +53,62 @@ test_store_ctor_dtor()
store->properties().schema_version.c_str());
}
static void
test_store_reinit()
{
TempDir tempdir;
{
Store::Config conf{};
conf.max_message_size = 1234567;
conf.batch_size = 7654321;
StringVec my_addresses{ "foo@example.com", "bar@example.com" };
auto store{Store::make_new(tempdir.path(), MuTestMaildir, my_addresses, conf)};
assert_valid_result(store);
g_assert_true(store->empty());
g_assert_cmpuint(0, ==, store->size());
g_assert_cmpstr(MU_STORE_SCHEMA_VERSION, ==,
store->properties().schema_version.c_str());
const auto msgpath{MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"};
const auto id = store->add_message(msgpath);
assert_valid_result(id);
g_assert_true(store->contains_message(msgpath));
g_assert_cmpuint(store->size(), ==, 1);
}
//now let's reinitialize it.
{
auto store{Store::make(tempdir.path(),
Store::Options::Writable|Store::Options::ReInit)};
assert_valid_result(store);
g_assert_true(store->empty());
assert_equal(store->properties().database_path, tempdir.path());
g_assert_cmpuint(store->properties().batch_size,==,7654321);
g_assert_cmpuint(store->properties().max_message_size,==,1234567);
const auto addrs{store->properties().personal_addresses};
g_assert_cmpuint(addrs.size(),==,2);
g_assert_true(seq_some(addrs, [](auto&& a){return a=="foo@example.com";}));
g_assert_true(seq_some(addrs, [](auto&& a){return a=="bar@example.com";}));
const auto msgpath{MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"};
const auto id = store->add_message(msgpath);
assert_valid_result(id);
g_assert_true(store->contains_message(msgpath));
g_assert_cmpuint(store->size(), ==, 1);
}
}
static void
test_store_add_count_remove()
{
@ -69,8 +125,6 @@ test_store_add_count_remove()
g_assert_cmpuint(store->size(), ==, 1);
g_assert_true(store->contains_message(msgpath));
g_assert_true(store->contains_message(msgpath));
const auto id2 = store->add_message(MuTestMaildir2 + "/bar/cur/mail3");
g_assert_false(!!id2); // wrong maildir.
store->commit();
@ -346,13 +400,13 @@ test_store_fail()
}
}
int
main(int argc, char* argv[])
{
mu_test_init(&argc, &argv);
g_test_add_func("/store/ctor-dtor", test_store_ctor_dtor);
g_test_add_func("/store/reinit", test_store_reinit);
g_test_add_func("/store/add-count-remove", test_store_add_count_remove);
g_test_add_func("/store/message/mailing-list",
test_message_mailing_list);