Add very basic db versioning

custom
jacqueline 2 years ago
parent 542e6f5d90
commit c2dddba30a
  1. 71
      src/database/database.cpp

@ -44,10 +44,33 @@ namespace database {
static SingletonEnv<leveldb::EspEnv> sEnv; static SingletonEnv<leveldb::EspEnv> sEnv;
static const char* kTag = "DB"; static const char* kTag = "DB";
static const char kTrackIdKey[] = "next_track_id"; static const char kDbPath[] = "/.tangara-db";
static const char kKeyDbVersion[] = "schema_version";
static const uint8_t kCurrentDbVersion = 1;
static const char kKeyTrackId[] = "next_track_id";
static std::atomic<bool> sIsDbOpen(false); static std::atomic<bool> sIsDbOpen(false);
static auto CreateNewDatabase(leveldb::Options& options) -> leveldb::DB* {
Database::Destroy();
leveldb::DB* db;
options.create_if_missing = true;
auto status = leveldb::DB::Open(options, kDbPath, &db);
if (!status.ok()) {
ESP_LOGE(kTag, "failed to open db, status %s", status.ToString().c_str());
return nullptr;
}
auto version_str = std::to_string(kCurrentDbVersion);
status = db->Put(leveldb::WriteOptions{}, kKeyDbVersion, version_str);
if (!status.ok()) {
delete db;
return nullptr;
}
return db;
}
auto Database::Open(IFileGatherer& gatherer, ITagParser& parser) auto Database::Open(IFileGatherer& gatherer, ITagParser& parser)
-> cpp::result<Database*, DatabaseError> { -> cpp::result<Database*, DatabaseError> {
// TODO(jacqueline): Why isn't compare_and_exchange_* available? // TODO(jacqueline): Why isn't compare_and_exchange_* available?
@ -66,25 +89,43 @@ auto Database::Open(IFileGatherer& gatherer, ITagParser& parser)
->Dispatch<cpp::result<Database*, DatabaseError>>( ->Dispatch<cpp::result<Database*, DatabaseError>>(
[&]() -> cpp::result<Database*, DatabaseError> { [&]() -> cpp::result<Database*, DatabaseError> {
leveldb::DB* db; leveldb::DB* db;
leveldb::Cache* cache = leveldb::NewLRUCache(24 * 1024); std::unique_ptr<leveldb::Cache> cache{
leveldb::NewLRUCache(24 * 1024)};
leveldb::Options options; leveldb::Options options;
options.env = sEnv.env(); options.env = sEnv.env();
options.create_if_missing = true;
options.write_buffer_size = 48 * 1024; options.write_buffer_size = 48 * 1024;
options.max_file_size = 32; options.max_file_size = 32;
options.block_cache = cache; options.block_cache = cache.get();
options.block_size = 512; options.block_size = 512;
auto status = leveldb::DB::Open(options, "/.db", &db); auto status = leveldb::DB::Open(options, kDbPath, &db);
if (!status.ok()) { if (!status.ok()) {
delete cache; ESP_LOGI(kTag, "opening db failed. recreating.");
ESP_LOGE(kTag, "failed to open db, status %s", db = CreateNewDatabase(options);
status.ToString().c_str()); if (db == nullptr) {
return cpp::fail(FAILED_TO_OPEN); return cpp::fail(FAILED_TO_OPEN);
}
}
std::string raw_version;
std::optional<uint8_t> version{};
status =
db->Get(leveldb::ReadOptions{}, kKeyDbVersion, &raw_version);
if (status.ok()) {
version = std::stoi(raw_version);
}
if (!version || *version != kCurrentDbVersion) {
ESP_LOGI(kTag, "db version missing or incorrect. recreating.");
delete db;
db = CreateNewDatabase(options);
if (db == nullptr) {
return cpp::fail(FAILED_TO_OPEN);
}
} }
ESP_LOGI(kTag, "Database opened successfully"); ESP_LOGI(kTag, "Database opened successfully");
return new Database(db, cache, gatherer, parser, worker); return new Database(db, cache.release(), gatherer, parser, worker);
}) })
.get(); .get();
} }
@ -92,7 +133,7 @@ auto Database::Open(IFileGatherer& gatherer, ITagParser& parser)
auto Database::Destroy() -> void { auto Database::Destroy() -> void {
leveldb::Options options; leveldb::Options options;
options.env = sEnv.env(); options.env = sEnv.env();
leveldb::DestroyDB("/.db", options); leveldb::DestroyDB(kDbPath, options);
} }
Database::Database(leveldb::DB* db, Database::Database(leveldb::DB* db,
@ -412,7 +453,7 @@ template auto Database::GetPage<std::pmr::string>(Continuation* c)
auto Database::dbMintNewTrackId() -> TrackId { auto Database::dbMintNewTrackId() -> TrackId {
TrackId next_id = 1; TrackId next_id = 1;
std::string val; std::string val;
auto status = db_->Get(leveldb::ReadOptions(), kTrackIdKey, &val); auto status = db_->Get(leveldb::ReadOptions(), kKeyTrackId, &val);
if (status.ok()) { if (status.ok()) {
next_id = BytesToTrackId(val).value_or(next_id); next_id = BytesToTrackId(val).value_or(next_id);
} else if (!status.IsNotFound()) { } else if (!status.IsNotFound()) {
@ -420,7 +461,7 @@ auto Database::dbMintNewTrackId() -> TrackId {
ESP_LOGE(kTag, "failed to get next track id"); ESP_LOGE(kTag, "failed to get next track id");
} }
if (!db_->Put(leveldb::WriteOptions(), kTrackIdKey, if (!db_->Put(leveldb::WriteOptions(), kKeyTrackId,
TrackIdToBytes(next_id + 1)) TrackIdToBytes(next_id + 1))
.ok()) { .ok()) {
ESP_LOGE(kTag, "failed to write next track id"); ESP_LOGE(kTag, "failed to write next track id");
@ -560,8 +601,8 @@ auto Database::dbGetPage(const Continuation& c) -> Result<T>* {
std::optional<Continuation> prev_page; std::optional<Continuation> prev_page;
if (c.forward) { if (c.forward) {
// We were going forwards, and now we want the previous page. Set the search // We were going forwards, and now we want the previous page. Set the
// key to the first result we saw, and mark that it's off by one. // search key to the first result we saw, and mark that it's off by one.
prev_page = Continuation{ prev_page = Continuation{
.prefix = c.prefix, .prefix = c.prefix,
.start_key = *first_key, .start_key = *first_key,

Loading…
Cancel
Save