Skip to content

Commit

Permalink
ConfigServer: Sync config files to disk automatically
Browse files Browse the repository at this point in the history
At the risk of introducing premature optimization, it only syncs to disk
5 seconds after the latest valid configuration update, to handle cases
where programs might send frequent updates that would go to disk every
time otherwise.
Also syncs to disk when the client connection closes.

Co-authored-by: Timothy Flynn <[email protected]>
  • Loading branch information
2 people authored and awesomekling committed Oct 14, 2021
1 parent 410e2b4 commit a38983d
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
33 changes: 32 additions & 1 deletion Userland/Services/ConfigServer/ClientConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <ConfigServer/ConfigClientEndpoint.h>
#include <LibCore/ConfigFile.h>
#include <LibCore/FileWatcher.h>
#include <LibCore/Timer.h>

namespace ConfigServer {

Expand All @@ -20,6 +21,7 @@ struct CachedDomain {
};

static HashMap<String, NonnullOwnPtr<CachedDomain>> s_cache;
static constexpr int s_disk_sync_delay_ms = 5'000;

static Core::ConfigFile& ensure_domain_config(String const& domain)
{
Expand All @@ -34,7 +36,7 @@ static Core::ConfigFile& ensure_domain_config(String const& domain)
auto result = watcher_or_error.value()->add_watch(config->filename(), Core::FileWatcherEvent::Type::ContentModified);
VERIFY(!result.is_error());
watcher_or_error.value()->on_change = [config, domain](auto&) {
auto new_config = Core::ConfigFile::open(config->filename());
auto new_config = Core::ConfigFile::open(config->filename(), Core::ConfigFile::AllowWriting::Yes);
// FIXME: Detect removed keys.
// FIXME: Detect type of keys.
for (auto& group : new_config->groups()) {
Expand All @@ -60,6 +62,7 @@ static Core::ConfigFile& ensure_domain_config(String const& domain)

ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id)
: IPC::ClientConnection<ConfigClientEndpoint, ConfigServerEndpoint>(*this, move(client_socket), client_id)
, m_sync_timer(Core::Timer::create_single_shot(s_disk_sync_delay_ms, [this]() { sync_dirty_domains_to_disk(); }))
{
s_connections.set(client_id, *this);
}
Expand All @@ -71,6 +74,8 @@ ClientConnection::~ClientConnection()
void ClientConnection::die()
{
s_connections.remove(client_id());
m_sync_timer->stop();
sync_dirty_domains_to_disk();
}

void ClientConnection::pledge_domains(Vector<String> const& domains)
Expand Down Expand Up @@ -104,6 +109,18 @@ bool ClientConnection::validate_access(String const& domain, String const& group
return false;
}

void ClientConnection::sync_dirty_domains_to_disk()
{
if (m_dirty_domains.is_empty())
return;
auto dirty_domains = move(m_dirty_domains);
dbgln("Syncing {} dirty domains to disk", dirty_domains.size());
for (auto domain : dirty_domains) {
auto& config = ensure_domain_config(domain);
config.sync();
}
}

Messages::ConfigServer::ReadStringValueResponse ClientConnection::read_string_value(String const& domain, String const& group, String const& key)
{
if (!validate_access(domain, group, key))
Expand Down Expand Up @@ -137,6 +154,14 @@ Messages::ConfigServer::ReadBoolValueResponse ClientConnection::read_bool_value(
return Optional<bool> { config.read_bool_entry(group, key) };
}

void ClientConnection::start_or_restart_sync_timer()
{
if (m_sync_timer->is_active())
m_sync_timer->restart();
else
m_sync_timer->start();
}

void ClientConnection::write_string_value(String const& domain, String const& group, String const& key, String const& value)
{
if (!validate_access(domain, group, key))
Expand All @@ -148,6 +173,8 @@ void ClientConnection::write_string_value(String const& domain, String const& gr
return;

config.write_entry(group, key, value);
m_dirty_domains.set(domain);
start_or_restart_sync_timer();

for (auto& it : s_connections) {
if (it.value != this && it.value->m_monitored_domains.contains(domain))
Expand All @@ -166,6 +193,8 @@ void ClientConnection::write_i32_value(String const& domain, String const& group
return;

config.write_num_entry(group, key, value);
m_dirty_domains.set(domain);
start_or_restart_sync_timer();

for (auto& it : s_connections) {
if (it.value != this && it.value->m_monitored_domains.contains(domain))
Expand All @@ -184,6 +213,8 @@ void ClientConnection::write_bool_value(String const& domain, String const& grou
return;

config.write_bool_entry(group, key, value);
m_dirty_domains.set(domain);
start_or_restart_sync_timer();

for (auto& it : s_connections) {
if (it.value != this && it.value->m_monitored_domains.contains(domain))
Expand Down
5 changes: 5 additions & 0 deletions Userland/Services/ConfigServer/ClientConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,16 @@ class ClientConnection final : public IPC::ClientConnection<ConfigClientEndpoint
virtual void write_bool_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key, [[maybe_unused]] bool value) override;

bool validate_access(String const& domain, String const& group, String const& key);
void sync_dirty_domains_to_disk();
void start_or_restart_sync_timer();

bool m_has_pledged { false };
HashTable<String> m_pledged_domains;

HashTable<String> m_monitored_domains;

NonnullRefPtr<Core::Timer> m_sync_timer;
HashTable<String> m_dirty_domains;
};

}

0 comments on commit a38983d

Please sign in to comment.