summaryrefslogtreecommitdiff
path: root/src/database/database.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-04-27 12:55:30 +1000
committerjacqueline <me@jacqueline.id.au>2023-04-27 12:55:30 +1000
commit5d7cbec34cd5e473d5768b39054d99bc72ddad62 (patch)
treef83040e527dc025345b3786a298f11e914b5eb37 /src/database/database.cpp
parentfbe047a35fff100cb5f42d10984bccde137f586e (diff)
downloadtangara-fw-5d7cbec34cd5e473d5768b39054d99bc72ddad62.tar.gz
Move DB interactions to a background thread
Diffstat (limited to 'src/database/database.cpp')
-rw-r--r--src/database/database.cpp128
1 files changed, 88 insertions, 40 deletions
diff --git a/src/database/database.cpp b/src/database/database.cpp
index b677f4ba..8ca72771 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -4,75 +4,123 @@
#include "ff.h"
#include "leveldb/cache.h"
+#include "db_task.hpp"
+#include "env_esp.hpp"
#include "file_gatherer.hpp"
#include "leveldb/iterator.h"
+#include "leveldb/options.h"
#include "leveldb/slice.h"
+#include "result.hpp"
#include "tag_processor.hpp"
-#include "env_esp.hpp"
-#include "leveldb/options.h"
namespace database {
static SingletonEnv<leveldb::EspEnv> sEnv;
-static const char *kTag = "DB";
+static const char* kTag = "DB";
+
+static std::atomic<bool> sIsDbOpen(false);
auto Database::Open() -> cpp::result<Database*, DatabaseError> {
- leveldb::DB* db;
- 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_size = 512;
-
- auto status = leveldb::DB::Open(options, "/.db", &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);
+ // TODO(jacqueline): Why isn't compare_and_exchange_* available?
+ if (sIsDbOpen.exchange(true)) {
+ return cpp::fail(DatabaseError::ALREADY_OPEN);
}
- return new Database(db, cache);
+ if (!StartDbTask()) {
+ return cpp::fail(DatabaseError::ALREADY_OPEN);
+ }
+
+ return RunOnDbTask<cpp::result<Database*, DatabaseError>>(
+ []() -> cpp::result<Database*, DatabaseError> {
+ leveldb::DB* db;
+ 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_size = 512;
+
+ auto status = leveldb::DB::Open(options, "/.db", &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, "Database opened successfully");
+ return new Database(db, cache);
+ })
+ .get();
}
Database::Database(leveldb::DB* db, leveldb::Cache* cache)
: db_(db), cache_(cache) {}
-Database::~Database() {}
+Database::~Database() {
+ QuitDbTask();
+ sIsDbOpen.store(false);
+}
-auto Database::Initialise() -> void {
- leveldb::WriteOptions opt;
- opt.sync = true;
- FindFiles("", [&](const std::string &path) {
+template <typename Parser>
+auto IterateAndParse(leveldb::Iterator* it, std::size_t limit, Parser p)
+ -> void {
+ for (int i = 0; i < limit; i++) {
+ if (!it->Valid()) {
+ break;
+ }
+ std::invoke(p, it->key(), it->value());
+ it->Next();
+ }
+}
+
+auto Database::Populate() -> std::future<void> {
+ return RunOnDbTask<void>([&]() -> void {
+ leveldb::WriteOptions opt;
+ opt.sync = true;
+ FindFiles("", [&](const std::string& path) {
ESP_LOGI(kTag, "considering %s", path.c_str());
FileInfo info;
if (GetInfo(path, &info)) {
ESP_LOGI(kTag, "added as '%s'", info.title.c_str());
db_->Put(opt, "title:" + info.title, path);
}
+ });
+ db_->Put(opt, "title:coolkeywithoutval", leveldb::Slice());
});
- db_->Put(opt, "title:coolkeywithoutval", leveldb::Slice());
}
-auto Database::ByTitle() -> Iterator {
- leveldb::Iterator *it = db_->NewIterator(leveldb::ReadOptions());
- it->Seek("title:");
- while (it->Valid()) {
- ESP_LOGI(kTag, "%s : %s", it->key().ToString().c_str(), it->value().ToString().c_str());
- it->Next();
- }
- return Iterator(it);
+auto Database::GetSongs(std::size_t page_size) -> std::future<DbResult<Song>> {
+ return RunOnDbTask<DbResult<Song>>([&]() -> DbResult<Song> {
+ std::unique_ptr<leveldb::Iterator> it(
+ db_->NewIterator(leveldb::ReadOptions()));
+ it->Seek("title:");
+ std::vector<Song> results;
+ IterateAndParse(it.get(), page_size,
+ [&](const leveldb::Slice& key, const leveldb::Slice& val) {
+ Song s;
+ s.title = key.ToString();
+ results.push_back(s);
+ });
+ return DbResult<Song>(results, std::move(it));
+ });
}
-auto Iterator::Next() -> std::optional<std::string> {
- if (!it_->Valid()) {
- return {};
- }
- std::string ret = it_->key().ToString();
- it_->Next();
- return ret;
+auto Database::GetMoreSongs(std::size_t page_size, DbResult<Song> continuation)
+ -> std::future<DbResult<Song>> {
+ return RunOnDbTask<DbResult<Song>>([&]() -> DbResult<Song> {
+ std::unique_ptr<leveldb::Iterator> it(continuation.it());
+ std::vector<Song> results;
+ IterateAndParse(it.get(), page_size,
+ [&](const leveldb::Slice& key, const leveldb::Slice& val) {
+ Song s;
+ s.title = key.ToString();
+ results.push_back(s);
+ });
+ return DbResult<Song>(results, std::move(it));
+ });
}
} // namespace database