Add very basic db versioning
This commit is contained in:
+56
-15
@@ -44,10 +44,33 @@ namespace database {
|
||||
static SingletonEnv<leveldb::EspEnv> sEnv;
|
||||
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 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)
|
||||
-> cpp::result<Database*, DatabaseError> {
|
||||
// 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>>(
|
||||
[&]() -> cpp::result<Database*, DatabaseError> {
|
||||
leveldb::DB* db;
|
||||
leveldb::Cache* cache = leveldb::NewLRUCache(24 * 1024);
|
||||
std::unique_ptr<leveldb::Cache> cache{
|
||||
leveldb::NewLRUCache(24 * 1024)};
|
||||
|
||||
leveldb::Options options;
|
||||
options.env = sEnv.env();
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 48 * 1024;
|
||||
options.max_file_size = 32;
|
||||
options.block_cache = cache;
|
||||
options.block_cache = cache.get();
|
||||
options.block_size = 512;
|
||||
|
||||
auto status = leveldb::DB::Open(options, "/.db", &db);
|
||||
auto status = leveldb::DB::Open(options, kDbPath, &db);
|
||||
if (!status.ok()) {
|
||||
delete cache;
|
||||
ESP_LOGE(kTag, "failed to open db, status %s",
|
||||
status.ToString().c_str());
|
||||
return cpp::fail(FAILED_TO_OPEN);
|
||||
ESP_LOGI(kTag, "opening db failed. recreating.");
|
||||
db = CreateNewDatabase(options);
|
||||
if (db == nullptr) {
|
||||
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");
|
||||
return new Database(db, cache, gatherer, parser, worker);
|
||||
return new Database(db, cache.release(), gatherer, parser, worker);
|
||||
})
|
||||
.get();
|
||||
}
|
||||
@@ -92,7 +133,7 @@ auto Database::Open(IFileGatherer& gatherer, ITagParser& parser)
|
||||
auto Database::Destroy() -> void {
|
||||
leveldb::Options options;
|
||||
options.env = sEnv.env();
|
||||
leveldb::DestroyDB("/.db", options);
|
||||
leveldb::DestroyDB(kDbPath, options);
|
||||
}
|
||||
|
||||
Database::Database(leveldb::DB* db,
|
||||
@@ -412,7 +453,7 @@ template auto Database::GetPage<std::pmr::string>(Continuation* c)
|
||||
auto Database::dbMintNewTrackId() -> TrackId {
|
||||
TrackId next_id = 1;
|
||||
std::string val;
|
||||
auto status = db_->Get(leveldb::ReadOptions(), kTrackIdKey, &val);
|
||||
auto status = db_->Get(leveldb::ReadOptions(), kKeyTrackId, &val);
|
||||
if (status.ok()) {
|
||||
next_id = BytesToTrackId(val).value_or(next_id);
|
||||
} else if (!status.IsNotFound()) {
|
||||
@@ -420,7 +461,7 @@ auto Database::dbMintNewTrackId() -> TrackId {
|
||||
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))
|
||||
.ok()) {
|
||||
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;
|
||||
if (c.forward) {
|
||||
// We were going forwards, and now we want the previous page. Set the search
|
||||
// key to the first result we saw, and mark that it's off by one.
|
||||
// We were going forwards, and now we want the previous page. Set the
|
||||
// search key to the first result we saw, and mark that it's off by one.
|
||||
prev_page = Continuation{
|
||||
.prefix = c.prefix,
|
||||
.start_key = *first_key,
|
||||
|
||||
Reference in New Issue
Block a user