LibRequests+RequestServer: Add a method to estimate disk cache size

This allows estimating the cache size stored on disk since a provided
time stamp, and in total.
This commit is contained in:
Timothy Flynn 2025-11-02 13:10:27 -05:00 committed by Tim Flynn
parent d5c00a493c
commit ba49942b6d
Notes: github-actions[bot] 2025-11-12 14:09:00 +00:00
14 changed files with 138 additions and 3 deletions

View file

@ -1,4 +1,5 @@
set(SOURCES
CacheSizes.cpp
NetworkError.h
Request.cpp
RequestClient.cpp

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibRequests/CacheSizes.h>
namespace IPC {
template<>
ErrorOr<void> encode(Encoder& encoder, Requests::CacheSizes const& sizes)
{
TRY(encoder.encode(sizes.since_requested_time));
TRY(encoder.encode(sizes.total));
return {};
}
template<>
ErrorOr<Requests::CacheSizes> decode(Decoder& decoder)
{
auto since_requested_time = TRY(decoder.decode<u64>());
auto total = TRY(decoder.decode<u64>());
return Requests::CacheSizes { since_requested_time, total };
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <LibIPC/Forward.h>
namespace Requests {
struct CacheSizes {
u64 since_requested_time { 0 };
u64 total { 0 };
};
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder&, Requests::CacheSizes const&);
template<>
ErrorOr<Requests::CacheSizes> decode(Decoder&);
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/Promise.h>
#include <LibCore/System.h>
#include <LibRequests/Request.h>
#include <LibRequests/RequestClient.h>
@ -24,7 +25,11 @@ void RequestClient::die()
request->did_finish({}, {}, {}, NetworkError::RequestServerDied);
}
for (auto& [id, promise] : m_pending_cache_size_estimations)
promise->reject(Error::from_string_literal("RequestServer process died"));
m_requests.clear();
m_pending_cache_size_estimations.clear();
}
void RequestClient::ensure_connection(URL::URL const& url, ::RequestServer::CacheLevel cache_level)
@ -73,6 +78,24 @@ bool RequestClient::set_certificate(Badge<Request>, Request& request, ByteString
return IPCProxy::set_certificate(request.id(), move(certificate), move(key));
}
NonnullRefPtr<Core::Promise<CacheSizes>> RequestClient::estimate_cache_size_accessed_since(UnixDateTime since)
{
auto promise = Core::Promise<CacheSizes>::construct();
auto cache_size_estimation_id = m_next_cache_size_estimation_id++;
m_pending_cache_size_estimations.set(cache_size_estimation_id, promise);
async_estimate_cache_size_accessed_since(cache_size_estimation_id, since);
return promise;
}
void RequestClient::estimated_cache_size(u64 cache_size_estimation_id, CacheSizes sizes)
{
if (auto promise = m_pending_cache_size_estimations.take(cache_size_estimation_id); promise.has_value())
(*promise)->resolve(sizes);
}
void RequestClient::request_finished(i32 request_id, u64 total_size, RequestTimingInfo timing_info, Optional<NetworkError> network_error)
{
RefPtr<Request> request;

View file

@ -9,6 +9,7 @@
#include <AK/HashMap.h>
#include <LibHTTP/HeaderMap.h>
#include <LibIPC/ConnectionToServer.h>
#include <LibRequests/CacheSizes.h>
#include <LibRequests/RequestTimingInfo.h>
#include <LibRequests/WebSocket.h>
#include <LibWebSocket/WebSocket.h>
@ -39,6 +40,8 @@ public:
bool stop_request(Badge<Request>, Request&);
bool set_certificate(Badge<Request>, Request&, ByteString, ByteString);
NonnullRefPtr<Core::Promise<CacheSizes>> estimate_cache_size_accessed_since(UnixDateTime since);
Function<void()> on_request_server_died;
private:
@ -57,10 +60,15 @@ private:
virtual void websocket_subprotocol(i64 websocket_id, ByteString subprotocol) override;
virtual void websocket_certificate_requested(i64 websocket_id) override;
HashMap<i32, RefPtr<Request>> m_requests;
HashMap<i64, NonnullRefPtr<WebSocket>> m_websockets;
virtual void estimated_cache_size(u64 cache_size_estimation_id, CacheSizes sizes) override;
HashMap<i32, RefPtr<Request>> m_requests;
HashMap<i64, NonnullRefPtr<WebSocket>> m_websockets;
i64 m_next_websocket_id { 0 };
HashMap<u64, NonnullRefPtr<Core::Promise<CacheSizes>>> m_pending_cache_size_estimations;
u64 m_next_cache_size_estimation_id { 0 };
};
}

View file

@ -41,7 +41,7 @@ target_include_directories(requestserverservice PRIVATE ${CMAKE_CURRENT_BINARY_D
target_include_directories(requestserverservice PRIVATE ${LADYBIRD_SOURCE_DIR}/Services/)
target_link_libraries(RequestServer PRIVATE requestserverservice)
target_link_libraries(requestserverservice PUBLIC LibCore LibDatabase LibDNS LibCrypto LibFileSystem LibIPC LibMain LibTLS LibWebSocket LibURL LibTextCodec LibThreading CURL::libcurl)
target_link_libraries(requestserverservice PUBLIC LibCore LibDatabase LibDNS LibCrypto LibFileSystem LibIPC LibMain LibRequests LibTLS LibWebSocket LibURL LibTextCodec LibThreading CURL::libcurl)
target_link_libraries(requestserverservice PRIVATE OpenSSL::Crypto OpenSSL::SSL)
if (WIN32)

View file

@ -101,6 +101,7 @@ ErrorOr<CacheIndex> CacheIndex::create(Database::Database& database)
statements.select_entry = TRY(database.prepare_statement("SELECT * FROM CacheIndex WHERE cache_key = ?;"sv));
statements.update_response_headers = TRY(database.prepare_statement("UPDATE CacheIndex SET response_headers = ? WHERE cache_key = ?;"sv));
statements.update_last_access_time = TRY(database.prepare_statement("UPDATE CacheIndex SET last_access_time = ? WHERE cache_key = ?;"sv));
statements.estimate_cache_size_accessed_since = TRY(database.prepare_statement("SELECT SUM(data_size) + SUM(OCTET_LENGTH(response_headers)) FROM CacheIndex WHERE last_access_time >= ?;"sv));
return CacheIndex { database, statements };
}
@ -188,4 +189,21 @@ Optional<CacheIndex::Entry&> CacheIndex::find_entry(u64 cache_key)
return m_entries.get(cache_key);
}
Requests::CacheSizes CacheIndex::estimate_cache_size_accessed_since(UnixDateTime since) const
{
Requests::CacheSizes sizes;
m_database.execute_statement(
m_statements.estimate_cache_size_accessed_since,
[&](auto statement_id) { sizes.since_requested_time = m_database.result_column<u64>(statement_id, 0); },
since);
m_database.execute_statement(
m_statements.estimate_cache_size_accessed_since,
[&](auto statement_id) { sizes.total = m_database.result_column<u64>(statement_id, 0); },
UnixDateTime::earliest());
return sizes;
}
}

View file

@ -12,6 +12,7 @@
#include <AK/Types.h>
#include <LibDatabase/Database.h>
#include <LibHTTP/HeaderMap.h>
#include <LibRequests/CacheSizes.h>
namespace RequestServer {
@ -42,6 +43,8 @@ public:
void update_response_headers(u64 cache_key, HTTP::HeaderMap);
void update_last_access_time(u64 cache_key);
Requests::CacheSizes estimate_cache_size_accessed_since(UnixDateTime since) const;
private:
struct Statements {
Database::StatementID insert_entry { 0 };
@ -50,6 +53,7 @@ private:
Database::StatementID select_entry { 0 };
Database::StatementID update_response_headers { 0 };
Database::StatementID update_last_access_time { 0 };
Database::StatementID estimate_cache_size_accessed_since { 0 };
};
CacheIndex(Database::Database&, Statements);

View file

@ -139,6 +139,11 @@ bool DiskCache::check_if_cache_has_open_entry(Request& request, u64 cache_key, C
return false;
}
Requests::CacheSizes DiskCache::estimate_cache_size_accessed_since(UnixDateTime since) const
{
return m_index.estimate_cache_size_accessed_since(since);
}
void DiskCache::clear_cache()
{
for (auto const& [_, open_entries] : m_open_cache_entries) {

View file

@ -27,6 +27,7 @@ public:
Variant<Optional<CacheEntryWriter&>, CacheHasOpenEntry> create_entry(Request&);
Variant<Optional<CacheEntryReader&>, CacheHasOpenEntry> open_entry(Request&);
Requests::CacheSizes estimate_cache_size_accessed_since(UnixDateTime since) const;
void clear_cache();
LexicalPath const& cache_directory() { return m_cache_directory; }

View file

@ -302,6 +302,16 @@ void ConnectionFromClient::ensure_connection(URL::URL url, ::RequestServer::Cach
m_active_requests.set(connect_only_request_id, move(request));
}
void ConnectionFromClient::estimate_cache_size_accessed_since(u64 cache_size_estimation_id, UnixDateTime since)
{
Requests::CacheSizes sizes;
if (g_disk_cache.has_value())
sizes = g_disk_cache->estimate_cache_size_accessed_since(since);
async_estimated_cache_size(cache_size_estimation_id, sizes);
}
void ConnectionFromClient::clear_cache()
{
if (g_disk_cache.has_value())

View file

@ -42,6 +42,7 @@ private:
virtual Messages::RequestServer::SetCertificateResponse set_certificate(i32, ByteString, ByteString) override;
virtual void ensure_connection(URL::URL url, ::RequestServer::CacheLevel cache_level) override;
virtual void estimate_cache_size_accessed_since(u64 cache_size_estimation_id, UnixDateTime since) override;
virtual void clear_cache() override;
virtual void websocket_connect(i64 websocket_id, URL::URL, ByteString, Vector<ByteString>, Vector<ByteString>, HTTP::HeaderMap) override;

View file

@ -1,4 +1,5 @@
#include <LibHTTP/HeaderMap.h>
#include <LibRequests/CacheSizes.h>
#include <LibRequests/NetworkError.h>
#include <LibRequests/RequestTimingInfo.h>
#include <LibURL/URL.h>
@ -21,4 +22,6 @@ endpoint RequestClient
// Certificate requests
certificate_requested(i32 request_id) =|
estimated_cache_size(u64 cache_size_estimation_id, Requests::CacheSizes sizes) =|
}

View file

@ -22,6 +22,7 @@ endpoint RequestServer
ensure_connection(URL::URL url, ::RequestServer::CacheLevel cache_level) =|
estimate_cache_size_accessed_since(u64 cache_size_estimation_id, UnixDateTime since) =|
clear_cache() =|
// Websocket Connection API