RequestServer: Add a time parameter to the clear cache endpoint

This allows removing cache entries last accessed since a provided
timestamp.
This commit is contained in:
Timothy Flynn 2025-11-02 16:44:30 -05:00 committed by Tim Flynn
parent ba49942b6d
commit 3f61f0f189
Notes: github-actions[bot] 2025-11-12 14:08:53 +00:00
11 changed files with 35 additions and 38 deletions

View file

@ -831,7 +831,7 @@ void Application::initialize_actions()
m_debug_menu->add_action(Action::create("Collect Garbage"sv, ActionID::CollectGarbage, debug_request("collect-garbage"sv)));
m_debug_menu->add_action(Action::create("Clear Cache"sv, ActionID::ClearCache, [this, clear_memory_cache = debug_request("clear-cache")]() {
m_request_server_client->async_clear_cache();
m_request_server_client->async_remove_cache_entries_accessed_since({});
clear_memory_cache();
}));
m_debug_menu->add_action(Action::create("Clear All Cookies"sv, ActionID::ClearCookies, [this]() { m_cookie_jar->clear_all_cookies(); }));

View file

@ -15,11 +15,6 @@
namespace RequestServer {
static LexicalPath path_for_cache_key(LexicalPath const& cache_directory, u64 cache_key)
{
return cache_directory.append(MUST(String::formatted("{:016x}", cache_key)));
}
ErrorOr<CacheHeader> CacheHeader::read_from_stream(Stream& stream)
{
CacheHeader header;

View file

@ -97,7 +97,7 @@ ErrorOr<CacheIndex> CacheIndex::create(Database::Database& database)
Statements statements {};
statements.insert_entry = TRY(database.prepare_statement("INSERT OR REPLACE INTO CacheIndex VALUES (?, ?, ?, ?, ?, ?, ?);"sv));
statements.remove_entry = TRY(database.prepare_statement("DELETE FROM CacheIndex WHERE cache_key = ?;"sv));
statements.remove_all_entries = TRY(database.prepare_statement("DELETE FROM CacheIndex;"sv));
statements.remove_entries_accessed_since = TRY(database.prepare_statement("DELETE FROM CacheIndex WHERE last_access_time >= ? RETURNING cache_key;"sv));
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));
@ -136,10 +136,17 @@ void CacheIndex::remove_entry(u64 cache_key)
m_entries.remove(cache_key);
}
void CacheIndex::remove_all_entries()
void CacheIndex::remove_entries_accessed_since(UnixDateTime since, Function<void(u64 cache_key)> on_entry_removed)
{
m_database.execute_statement(m_statements.remove_all_entries, {});
m_entries.clear();
m_database.execute_statement(
m_statements.remove_entries_accessed_since,
[&](auto statement_id) {
auto cache_key = m_database.result_column<u64>(statement_id, 0);
m_entries.remove(cache_key);
on_entry_removed(cache_key);
},
since);
}
void CacheIndex::update_response_headers(u64 cache_key, HTTP::HeaderMap response_headers)

View file

@ -36,7 +36,7 @@ public:
void create_entry(u64 cache_key, String url, HTTP::HeaderMap, u64 data_size, UnixDateTime request_time, UnixDateTime response_time);
void remove_entry(u64 cache_key);
void remove_all_entries();
void remove_entries_accessed_since(UnixDateTime, Function<void(u64 cache_key)> on_entry_removed);
Optional<Entry&> find_entry(u64 cache_key);
@ -49,7 +49,7 @@ private:
struct Statements {
Database::StatementID insert_entry { 0 };
Database::StatementID remove_entry { 0 };
Database::StatementID remove_all_entries { 0 };
Database::StatementID remove_entries_accessed_since { 0 };
Database::StatementID select_entry { 0 };
Database::StatementID update_response_headers { 0 };
Database::StatementID update_last_access_time { 0 };

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/DirIterator.h>
#include <LibCore/StandardPaths.h>
#include <LibFileSystem/FileSystem.h>
#include <LibURL/URL.h>
@ -144,28 +143,17 @@ Requests::CacheSizes DiskCache::estimate_cache_size_accessed_since(UnixDateTime
return m_index.estimate_cache_size_accessed_since(since);
}
void DiskCache::clear_cache()
void DiskCache::remove_entries_accessed_since(UnixDateTime since)
{
for (auto const& [_, open_entries] : m_open_cache_entries) {
for (auto const& open_entry : open_entries)
open_entry->mark_for_deletion({});
}
m_index.remove_entries_accessed_since(since, [&](auto cache_key) {
if (auto open_entries = m_open_cache_entries.get(cache_key); open_entries.has_value()) {
for (auto const& open_entry : *open_entries)
open_entry->mark_for_deletion({});
}
m_index.remove_all_entries();
Core::DirIterator it { m_cache_directory.string(), Core::DirIterator::SkipDots };
size_t cache_entries { 0 };
while (it.has_next()) {
auto entry = it.next_full_path();
if (LexicalPath { entry }.title() == INDEX_DATABASE)
continue;
(void)FileSystem::remove(entry, FileSystem::RecursionMode::Disallowed);
++cache_entries;
}
dbgln("Cleared {} disk cache entries", cache_entries);
auto cache_path = path_for_cache_key(m_cache_directory, cache_key);
(void)FileSystem::remove(cache_path.string(), FileSystem::RecursionMode::Disallowed);
});
}
void DiskCache::cache_entry_closed(Badge<CacheEntry>, CacheEntry const& cache_entry)

View file

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

View file

@ -70,6 +70,11 @@ u64 create_cache_key(StringView url, StringView method)
return result;
}
LexicalPath path_for_cache_key(LexicalPath const& cache_directory, u64 cache_key)
{
return cache_directory.append(MUST(String::formatted("{:016x}", cache_key)));
}
// https://httpwg.org/specs/rfc9111.html#response.cacheability
bool is_cacheable(StringView method)
{

View file

@ -6,6 +6,7 @@
#pragma once
#include <AK/LexicalPath.h>
#include <AK/StringView.h>
#include <AK/Time.h>
#include <AK/Types.h>
@ -16,6 +17,7 @@ namespace RequestServer {
String serialize_url_for_cache_storage(URL::URL const&);
u64 create_cache_key(StringView url, StringView method);
LexicalPath path_for_cache_key(LexicalPath const& cache_directory, u64 cache_key);
bool is_cacheable(StringView method);
bool is_cacheable(u32 status_code, HTTP::HeaderMap const&);

View file

@ -312,10 +312,10 @@ void ConnectionFromClient::estimate_cache_size_accessed_since(u64 cache_size_est
async_estimated_cache_size(cache_size_estimation_id, sizes);
}
void ConnectionFromClient::clear_cache()
void ConnectionFromClient::remove_cache_entries_accessed_since(UnixDateTime since)
{
if (g_disk_cache.has_value())
g_disk_cache->clear_cache();
g_disk_cache->remove_entries_accessed_since(since);
}
void ConnectionFromClient::websocket_connect(i64 websocket_id, URL::URL url, ByteString origin, Vector<ByteString> protocols, Vector<ByteString> extensions, HTTP::HeaderMap additional_request_headers)

View file

@ -43,7 +43,7 @@ private:
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 remove_cache_entries_accessed_since(UnixDateTime since) override;
virtual void websocket_connect(i64 websocket_id, URL::URL, ByteString, Vector<ByteString>, Vector<ByteString>, HTTP::HeaderMap) override;
virtual void websocket_send(i64 websocket_id, bool, ByteBuffer) override;

View file

@ -23,7 +23,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() =|
remove_cache_entries_accessed_since(UnixDateTime since) =|
// Websocket Connection API
websocket_connect(i64 websocket_id, URL::URL url, ByteString origin, Vector<ByteString> protocols, Vector<ByteString> extensions, HTTP::HeaderMap additional_request_headers) =|