Loading debian/changelog +12 −0 Original line number Diff line number Diff line mediadb (20260424+73) unstable; urgency=medium * Performance: use authdb ConnectionPool for is_authorized(), check_gpo(), repopulate_session() and list_groups — eliminates TCP connection setup per request. * Performance: session lookups now validate TTL inline, preventing stale sessions from being treated as valid. * Performance: add domain_label_index_ for O(1) find_domain_by_label() instead of linear scan across all stores. -- Jan Koester <jan.koester@tuxist.de> Thu, 24 Apr 2026 00:00:00 +0200 mediadb (20260423+72) unstable; urgency=medium * Preview: flush FFmpeg decoder after read loop so codecs that buffer Loading src/app.cpp +41 −22 Original line number Diff line number Diff line Loading @@ -200,7 +200,10 @@ std::string App::get_session_username(const HttpRequest& req) const { const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); return sit != sessions_.end() ? sit->second.username : std::string{}; if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.username; } std::string App::get_session_domain(const HttpRequest& req) const { Loading @@ -212,7 +215,10 @@ std::string App::get_session_domain(const HttpRequest& req) const { const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); return sit != sessions_.end() ? sit->second.domain_label : std::string{}; if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.domain_label; } std::vector<std::string> App::get_session_groups(const HttpRequest& req) const { Loading @@ -224,7 +230,10 @@ std::vector<std::string> App::get_session_groups(const HttpRequest& req) const { const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); return sit != sessions_.end() ? sit->second.group_ids : std::vector<std::string>{}; if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.group_ids; } std::vector<std::string> App::resolve_session_groups(authdb::client::Client& client) { Loading Loading @@ -255,17 +264,13 @@ void App::repopulate_session(const HttpRequest& req) { const std::string token = value.substr(prefix.size()); try { uuid::uuid session_id(token); authdb::client::ClientConnection con; con.setUrl(auth_.url()); con.setClientName(auth_.client_name()); con.setClientSecret(auth_.client_secret()); con.setSessionID(session_id); authdb::client::Client client(con); if (!client.ClientAuth()) return; auto pooled = auth_.pool().acquire(); pooled.setSessionID(session_id); if (!pooled.client().ClientAuth()) return; authdb::SessionData sdat; client.SessionInfo(sdat); pooled.client().SessionInfo(sdat); std::string username = sdat.getUsername() ? sdat.getUsername() : ""; auto grps = resolve_session_groups(client); auto grps = resolve_session_groups(pooled.client()); store_session(token, username, {}, grps); } catch (...) { store_session(token, ""); Loading @@ -280,7 +285,11 @@ bool App::is_session_known(const HttpRequest& req) const { if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return false; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); return sessions_.find(token) != sessions_.end(); auto sit = sessions_.find(token); if (sit == sessions_.end()) return false; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return false; return true; } bool App::is_any_authorized(const HttpRequest& req) { Loading Loading @@ -1260,25 +1269,35 @@ HttpResponse App::handle_list_groups(const HttpRequest& req) { const std::string domain = get_session_domain(req); try { authdb::client::ClientConnection con; if (!domain.empty()) { auto dom = db_.find_domain_by_label(domain); if (!dom) return error_json(404, "unknown domain: " + domain); // Domain-specific connection — not pooled authdb::client::ClientConnection con; con.setUrl(dom->url); con.setClientName(dom->client_name); con.setClientSecret(dom->client_secret); } else { con.setUrl(auth_.url()); con.setClientName(auth_.client_name()); con.setClientSecret(auth_.client_secret()); } authdb::client::Client client(con); if (!client.ClientAuth()) return error_json(500, "client authentication failed"); auto groups = client.listGroups(); json_object* arr = json_object_new_array(); for (const auto& g : groups) { json_object* jgrp = json_object_new_object(); json_object_object_add(jgrp, "gid", json_object_new_string(g.gid.c_str())); json_object_object_add(jgrp, "name", json_object_new_string(g.name.c_str())); json_object_array_add(arr, jgrp); } json_object* wrap = json_object_new_object(); json_object_object_add(wrap, "groups", arr); return HttpResponse::json(200, wrap); } auto pooled = auth_.pool().acquire(); if (!pooled.client().ClientAuth()) return error_json(500, "client authentication failed"); auto groups = pooled.client().listGroups(); json_object* arr = json_object_new_array(); for (const auto& g : groups) { Loading src/auth.cpp +9 −30 Original line number Diff line number Diff line Loading @@ -10,7 +10,8 @@ AuthService::AuthService(const std::string& authdb_url, const std::string& client_secret) : authdb_url_(authdb_url), client_name_(client_name), client_secret_(client_secret) {} client_secret_(client_secret), pool_(authdb_url, client_name, client_secret) {} void AuthService::try_init_policies(authdb::client::Client& client, const std::string& domain_key) { { Loading Loading @@ -93,21 +94,10 @@ bool AuthService::is_authorized(const HttpRequest& req) const { try { uuid::uuid session_id(token); authdb::client::ClientConnection con; con.setUrl(authdb_url_); con.setClientName(client_name_); con.setClientSecret(client_secret_); con.setSessionID(session_id); auto pooled = pool_.acquire(); pooled.setSessionID(session_id); std::unique_ptr<authdb::client::Client> client; try { client = std::make_unique<authdb::client::Client>(con); } catch (...) { con.reConnect(); client = std::make_unique<authdb::client::Client>(con); } bool ok = client->ClientAuth(); bool ok = pooled.client().ClientAuth(); if (ok) cache_token(token); return ok; } catch (const authdb::AuthBackendError&) { Loading @@ -133,22 +123,11 @@ bool AuthService::check_gpo(const HttpRequest& req, const char* gpo_id) const { uuid::uuid session_id(token); uuid::uuid gpoid(gpo_id); authdb::client::ClientConnection con; con.setUrl(authdb_url_); con.setClientName(client_name_); con.setClientSecret(client_secret_); con.setSessionID(session_id); std::unique_ptr<authdb::client::Client> client; try { client = std::make_unique<authdb::client::Client>(con); } catch (...) { con.reConnect(); client = std::make_unique<authdb::client::Client>(con); } auto pooled = pool_.acquire(); pooled.setSessionID(session_id); if (!client->ClientAuth()) return false; return client->GPOcheck(gpoid); if (!pooled.client().ClientAuth()) return false; return pooled.client().GPOcheck(gpoid); } catch (...) { return false; } Loading src/auth.h +2 −0 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ public: const std::string& url() const { return authdb_url_; } const std::string& client_name() const { return client_name_; } const std::string& client_secret() const { return client_secret_; } authdb::client::ConnectionPool& pool() const { return pool_; } private: std::string authdb_url_; Loading @@ -66,6 +67,7 @@ private: std::string client_secret_; mutable std::mutex gpo_mutex_; std::set<std::string> gpo_initialized_domains_; mutable authdb::client::ConnectionPool pool_; // Session cache: token -> expiry time static constexpr int CACHE_TTL_SECONDS = 300; // 5 minutes Loading src/backend.cpp +24 −4 Original line number Diff line number Diff line Loading @@ -101,6 +101,9 @@ bool BinDb::delete_store(const std::string& id) { std::unique_lock lock(mutex_); auto it = stores_.find(id); if (it == stores_.end()) return false; for (const auto& d : it->second.auth_domains) { domain_label_index_.erase(d.label); } for (const auto& aid : it->second.album_ids) { auto ait = albums_.find(aid); if (ait != albums_.end()) { Loading Loading @@ -140,6 +143,7 @@ std::string BinDb::add_store_domain(const std::string& store_id, AuthDomain doma if (it == stores_.end()) return {}; domain.id = make_uuid(); did = domain.id; domain_label_index_[domain.label] = {store_id, domain.id}; it->second.auth_domains.push_back(std::move(domain)); save_index_locked(); } Loading @@ -155,6 +159,7 @@ bool BinDb::remove_store_domain(const std::string& store_id, const std::string& auto dit = std::find_if(domains.begin(), domains.end(), [&](const AuthDomain& d) { return d.id == domain_id; }); if (dit == domains.end()) return false; domain_label_index_.erase(dit->label); domains.erase(dit); save_index_locked(); } Loading @@ -170,10 +175,12 @@ std::vector<AuthDomain> BinDb::list_store_domains(const std::string& store_id) c std::optional<AuthDomain> BinDb::find_domain_by_label(const std::string& label) const { std::shared_lock lock(mutex_); for (const auto& [sid, store] : stores_) { for (const auto& d : store.auth_domains) { if (d.label == label) return d; } auto it = domain_label_index_.find(label); if (it == domain_label_index_.end()) return std::nullopt; auto sit = stores_.find(it->second.first); if (sit == stores_.end()) return std::nullopt; for (const auto& d : sit->second.auth_domains) { if (d.id == it->second.second) return d; } return std::nullopt; } Loading Loading @@ -1621,10 +1628,12 @@ void BinDb::reload() { albums_.clear(); media_.clear(); mmaps_.clear(); domain_label_index_.clear(); load_index(); for (auto& [sid, store] : stores_) { load_store(sid, store); } rebuild_domain_label_index_locked(); mmap_all_stores(); } Loading Loading @@ -1652,9 +1661,19 @@ void BinDb::load() { for (auto& [sid, store] : stores_) { load_store(sid, store); } rebuild_domain_label_index_locked(); mmap_all_stores(); } void BinDb::rebuild_domain_label_index_locked() { domain_label_index_.clear(); for (const auto& [sid, store] : stores_) { for (const auto& d : store.auth_domains) { domain_label_index_[d.label] = {sid, d.id}; } } } void BinDb::load_index() { std::ifstream in(index_file(), std::ios::binary); if (!in.is_open()) return; Loading Loading @@ -2267,6 +2286,7 @@ void BinDb::load_index_from_buffer(const std::vector<std::uint8_t>& data) { // Deletes propagate explicitly via delete_store() + cluster_.remove(); // removing here would wipe out recently-created local stores when the // synced index is stale (same merge-only approach as authdb). rebuild_domain_label_index_locked(); } std::vector<std::uint8_t> BinDb::save_store_to_buffer(const std::string& store_id) { Loading Loading
debian/changelog +12 −0 Original line number Diff line number Diff line mediadb (20260424+73) unstable; urgency=medium * Performance: use authdb ConnectionPool for is_authorized(), check_gpo(), repopulate_session() and list_groups — eliminates TCP connection setup per request. * Performance: session lookups now validate TTL inline, preventing stale sessions from being treated as valid. * Performance: add domain_label_index_ for O(1) find_domain_by_label() instead of linear scan across all stores. -- Jan Koester <jan.koester@tuxist.de> Thu, 24 Apr 2026 00:00:00 +0200 mediadb (20260423+72) unstable; urgency=medium * Preview: flush FFmpeg decoder after read loop so codecs that buffer Loading
src/app.cpp +41 −22 Original line number Diff line number Diff line Loading @@ -200,7 +200,10 @@ std::string App::get_session_username(const HttpRequest& req) const { const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); return sit != sessions_.end() ? sit->second.username : std::string{}; if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.username; } std::string App::get_session_domain(const HttpRequest& req) const { Loading @@ -212,7 +215,10 @@ std::string App::get_session_domain(const HttpRequest& req) const { const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); return sit != sessions_.end() ? sit->second.domain_label : std::string{}; if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.domain_label; } std::vector<std::string> App::get_session_groups(const HttpRequest& req) const { Loading @@ -224,7 +230,10 @@ std::vector<std::string> App::get_session_groups(const HttpRequest& req) const { const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); auto sit = sessions_.find(token); return sit != sessions_.end() ? sit->second.group_ids : std::vector<std::string>{}; if (sit == sessions_.end()) return {}; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return {}; return sit->second.group_ids; } std::vector<std::string> App::resolve_session_groups(authdb::client::Client& client) { Loading Loading @@ -255,17 +264,13 @@ void App::repopulate_session(const HttpRequest& req) { const std::string token = value.substr(prefix.size()); try { uuid::uuid session_id(token); authdb::client::ClientConnection con; con.setUrl(auth_.url()); con.setClientName(auth_.client_name()); con.setClientSecret(auth_.client_secret()); con.setSessionID(session_id); authdb::client::Client client(con); if (!client.ClientAuth()) return; auto pooled = auth_.pool().acquire(); pooled.setSessionID(session_id); if (!pooled.client().ClientAuth()) return; authdb::SessionData sdat; client.SessionInfo(sdat); pooled.client().SessionInfo(sdat); std::string username = sdat.getUsername() ? sdat.getUsername() : ""; auto grps = resolve_session_groups(client); auto grps = resolve_session_groups(pooled.client()); store_session(token, username, {}, grps); } catch (...) { store_session(token, ""); Loading @@ -280,7 +285,11 @@ bool App::is_session_known(const HttpRequest& req) const { if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0) return false; const std::string token = value.substr(prefix.size()); std::lock_guard<std::mutex> lk(session_mutex_); return sessions_.find(token) != sessions_.end(); auto sit = sessions_.find(token); if (sit == sessions_.end()) return false; if (std::chrono::steady_clock::now() - sit->second.created_at > std::chrono::seconds(SESSION_TTL_SECONDS)) return false; return true; } bool App::is_any_authorized(const HttpRequest& req) { Loading Loading @@ -1260,25 +1269,35 @@ HttpResponse App::handle_list_groups(const HttpRequest& req) { const std::string domain = get_session_domain(req); try { authdb::client::ClientConnection con; if (!domain.empty()) { auto dom = db_.find_domain_by_label(domain); if (!dom) return error_json(404, "unknown domain: " + domain); // Domain-specific connection — not pooled authdb::client::ClientConnection con; con.setUrl(dom->url); con.setClientName(dom->client_name); con.setClientSecret(dom->client_secret); } else { con.setUrl(auth_.url()); con.setClientName(auth_.client_name()); con.setClientSecret(auth_.client_secret()); } authdb::client::Client client(con); if (!client.ClientAuth()) return error_json(500, "client authentication failed"); auto groups = client.listGroups(); json_object* arr = json_object_new_array(); for (const auto& g : groups) { json_object* jgrp = json_object_new_object(); json_object_object_add(jgrp, "gid", json_object_new_string(g.gid.c_str())); json_object_object_add(jgrp, "name", json_object_new_string(g.name.c_str())); json_object_array_add(arr, jgrp); } json_object* wrap = json_object_new_object(); json_object_object_add(wrap, "groups", arr); return HttpResponse::json(200, wrap); } auto pooled = auth_.pool().acquire(); if (!pooled.client().ClientAuth()) return error_json(500, "client authentication failed"); auto groups = pooled.client().listGroups(); json_object* arr = json_object_new_array(); for (const auto& g : groups) { Loading
src/auth.cpp +9 −30 Original line number Diff line number Diff line Loading @@ -10,7 +10,8 @@ AuthService::AuthService(const std::string& authdb_url, const std::string& client_secret) : authdb_url_(authdb_url), client_name_(client_name), client_secret_(client_secret) {} client_secret_(client_secret), pool_(authdb_url, client_name, client_secret) {} void AuthService::try_init_policies(authdb::client::Client& client, const std::string& domain_key) { { Loading Loading @@ -93,21 +94,10 @@ bool AuthService::is_authorized(const HttpRequest& req) const { try { uuid::uuid session_id(token); authdb::client::ClientConnection con; con.setUrl(authdb_url_); con.setClientName(client_name_); con.setClientSecret(client_secret_); con.setSessionID(session_id); auto pooled = pool_.acquire(); pooled.setSessionID(session_id); std::unique_ptr<authdb::client::Client> client; try { client = std::make_unique<authdb::client::Client>(con); } catch (...) { con.reConnect(); client = std::make_unique<authdb::client::Client>(con); } bool ok = client->ClientAuth(); bool ok = pooled.client().ClientAuth(); if (ok) cache_token(token); return ok; } catch (const authdb::AuthBackendError&) { Loading @@ -133,22 +123,11 @@ bool AuthService::check_gpo(const HttpRequest& req, const char* gpo_id) const { uuid::uuid session_id(token); uuid::uuid gpoid(gpo_id); authdb::client::ClientConnection con; con.setUrl(authdb_url_); con.setClientName(client_name_); con.setClientSecret(client_secret_); con.setSessionID(session_id); std::unique_ptr<authdb::client::Client> client; try { client = std::make_unique<authdb::client::Client>(con); } catch (...) { con.reConnect(); client = std::make_unique<authdb::client::Client>(con); } auto pooled = pool_.acquire(); pooled.setSessionID(session_id); if (!client->ClientAuth()) return false; return client->GPOcheck(gpoid); if (!pooled.client().ClientAuth()) return false; return pooled.client().GPOcheck(gpoid); } catch (...) { return false; } Loading
src/auth.h +2 −0 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ public: const std::string& url() const { return authdb_url_; } const std::string& client_name() const { return client_name_; } const std::string& client_secret() const { return client_secret_; } authdb::client::ConnectionPool& pool() const { return pool_; } private: std::string authdb_url_; Loading @@ -66,6 +67,7 @@ private: std::string client_secret_; mutable std::mutex gpo_mutex_; std::set<std::string> gpo_initialized_domains_; mutable authdb::client::ConnectionPool pool_; // Session cache: token -> expiry time static constexpr int CACHE_TTL_SECONDS = 300; // 5 minutes Loading
src/backend.cpp +24 −4 Original line number Diff line number Diff line Loading @@ -101,6 +101,9 @@ bool BinDb::delete_store(const std::string& id) { std::unique_lock lock(mutex_); auto it = stores_.find(id); if (it == stores_.end()) return false; for (const auto& d : it->second.auth_domains) { domain_label_index_.erase(d.label); } for (const auto& aid : it->second.album_ids) { auto ait = albums_.find(aid); if (ait != albums_.end()) { Loading Loading @@ -140,6 +143,7 @@ std::string BinDb::add_store_domain(const std::string& store_id, AuthDomain doma if (it == stores_.end()) return {}; domain.id = make_uuid(); did = domain.id; domain_label_index_[domain.label] = {store_id, domain.id}; it->second.auth_domains.push_back(std::move(domain)); save_index_locked(); } Loading @@ -155,6 +159,7 @@ bool BinDb::remove_store_domain(const std::string& store_id, const std::string& auto dit = std::find_if(domains.begin(), domains.end(), [&](const AuthDomain& d) { return d.id == domain_id; }); if (dit == domains.end()) return false; domain_label_index_.erase(dit->label); domains.erase(dit); save_index_locked(); } Loading @@ -170,10 +175,12 @@ std::vector<AuthDomain> BinDb::list_store_domains(const std::string& store_id) c std::optional<AuthDomain> BinDb::find_domain_by_label(const std::string& label) const { std::shared_lock lock(mutex_); for (const auto& [sid, store] : stores_) { for (const auto& d : store.auth_domains) { if (d.label == label) return d; } auto it = domain_label_index_.find(label); if (it == domain_label_index_.end()) return std::nullopt; auto sit = stores_.find(it->second.first); if (sit == stores_.end()) return std::nullopt; for (const auto& d : sit->second.auth_domains) { if (d.id == it->second.second) return d; } return std::nullopt; } Loading Loading @@ -1621,10 +1628,12 @@ void BinDb::reload() { albums_.clear(); media_.clear(); mmaps_.clear(); domain_label_index_.clear(); load_index(); for (auto& [sid, store] : stores_) { load_store(sid, store); } rebuild_domain_label_index_locked(); mmap_all_stores(); } Loading Loading @@ -1652,9 +1661,19 @@ void BinDb::load() { for (auto& [sid, store] : stores_) { load_store(sid, store); } rebuild_domain_label_index_locked(); mmap_all_stores(); } void BinDb::rebuild_domain_label_index_locked() { domain_label_index_.clear(); for (const auto& [sid, store] : stores_) { for (const auto& d : store.auth_domains) { domain_label_index_[d.label] = {sid, d.id}; } } } void BinDb::load_index() { std::ifstream in(index_file(), std::ios::binary); if (!in.is_open()) return; Loading Loading @@ -2267,6 +2286,7 @@ void BinDb::load_index_from_buffer(const std::vector<std::uint8_t>& data) { // Deletes propagate explicitly via delete_store() + cluster_.remove(); // removing here would wipe out recently-created local stores when the // synced index is stale (same merge-only approach as authdb). rebuild_domain_label_index_locked(); } std::vector<std::uint8_t> BinDb::save_store_to_buffer(const std::string& store_id) { Loading