Derive the next track id from stored track data, instead of tracking it explicitly

This saves about 1ms per new track right now, but more importantly means
that minting a new track id is now a single atomic operation, rather
than being its own database write. This is a useful property that will
come in handy in a few commits time.
custom
jacqueline 9 months ago
parent f8a3c16aad
commit b5dc53670a
  1. 56
      src/tangara/database/database.cpp
  2. 2
      src/tangara/database/database.hpp

@ -24,6 +24,7 @@
#include "cppbor.h" #include "cppbor.h"
#include "cppbor_parse.h" #include "cppbor_parse.h"
#include "database/index.hpp" #include "database/index.hpp"
#include "debug.hpp"
#include "esp_log.h" #include "esp_log.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "ff.h" #include "ff.h"
@ -60,7 +61,6 @@ static const char kKeyDbVersion[] = "schema_version";
static const char kKeyCustom[] = "U\0"; static const char kKeyCustom[] = "U\0";
static const char kKeyCollator[] = "collator"; static const char kKeyCollator[] = "collator";
static const char kKeyTrackId[] = "next_track_id";
static std::atomic<bool> sIsDbOpen(false); static std::atomic<bool> sIsDbOpen(false);
@ -190,7 +190,10 @@ Database::Database(leveldb::DB* db,
file_gatherer_(file_gatherer), file_gatherer_(file_gatherer),
tag_parser_(tag_parser), tag_parser_(tag_parser),
collator_(collator), collator_(collator),
is_updating_(false) {} is_updating_(false) {
dbCalculateNextTrackId();
ESP_LOGI(kTag, "next track id is %lu", next_track_id_.load());
}
Database::~Database() { Database::~Database() {
// Delete db_ first so that any outstanding background work finishes before // Delete db_ first so that any outstanding background work finishes before
@ -492,24 +495,45 @@ auto Database::isUpdating() -> bool {
return is_updating_; return is_updating_;
} }
auto Database::dbMintNewTrackId() -> TrackId { auto Database::dbCalculateNextTrackId() -> void {
TrackId next_id = 1; std::unique_ptr<leveldb::Iterator> it{
std::string val; db_->NewIterator(leveldb::ReadOptions())};
auto status = db_->Get(leveldb::ReadOptions(), kKeyTrackId, &val);
if (status.ok()) { // Track data entries are of the format 'D/trackid', where track ids are
next_id = BytesToTrackId(val).value_or(next_id); // encoded as big-endian cbor types. They can therefore be compared through
} else if (!status.IsNotFound()) { // byte ordering, which means we can determine what the next id should be by
// TODO(jacqueline): Handle this more. // looking at the larged track data record in the database.
ESP_LOGE(kTag, "failed to get next track id"); std::string prefix = EncodeDataPrefix();
std::string prefixPlusOne = prefix;
prefixPlusOne[prefixPlusOne.size() - 1]++;
// Seek to just past the track data section.
it->Seek(prefixPlusOne);
if (!it->Valid()) {
next_track_id_ = 1;
return;
} }
if (!db_->Put(leveldb::WriteOptions(), kKeyTrackId, // Go back to the last track data record.
TrackIdToBytes(next_id + 1)) it->Prev();
.ok()) { if (!it->Valid() || !it->key().starts_with(prefix)) {
ESP_LOGE(kTag, "failed to write next track id"); next_track_id_ = 1;
return;
}
// Parse the track id back out of the key.
std::span<const char> key{it->key().data(), it->key().size()};
auto id_part = key.subspan(prefix.size());
if (id_part.empty()) {
next_track_id_ = 1;
return;
} }
return next_id; next_track_id_ = BytesToTrackId(id_part).value_or(0) + 1;
}
auto Database::dbMintNewTrackId() -> TrackId {
return next_track_id_++;
} }
auto Database::dbEntomb(TrackId id, uint64_t hash) -> void { auto Database::dbEntomb(TrackId id, uint64_t hash) -> void {

@ -100,6 +100,7 @@ class Database {
locale::ICollator& collator_; locale::ICollator& collator_;
std::atomic<bool> is_updating_; std::atomic<bool> is_updating_;
std::atomic<TrackId> next_track_id_;
Database(leveldb::DB* db, Database(leveldb::DB* db,
leveldb::Cache* cache, leveldb::Cache* cache,
@ -107,6 +108,7 @@ class Database {
ITagParser& tag_parser, ITagParser& tag_parser,
locale::ICollator& collator); locale::ICollator& collator);
auto dbCalculateNextTrackId() -> void;
auto dbMintNewTrackId() -> TrackId; auto dbMintNewTrackId() -> TrackId;
auto dbEntomb(TrackId track, uint64_t hash) -> void; auto dbEntomb(TrackId track, uint64_t hash) -> void;

Loading…
Cancel
Save