blob: 1a7e8c9820e59e2b544b47e592693885b551f208 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#include "src/ledger/bin/app/background_sync_manager.h"
#include <lib/async/cpp/task.h>
#include "src/ledger/lib/convert/convert.h"
#include "src/ledger/lib/logging/logging.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
// #include <algorithm>
namespace ledger {
namespace {
// TODO(https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=35727): Change the limit from zero once
// page sync state is available.
// A maximum number of pages that should be opened at once.
inline constexpr size_t kOpenPagesLimit = 0;
// Returns the list of the |free_slots| pages (or less, if the initial size of the given list is
// smaller) with the earliest closure timestamps.
std::vector<PageInfo> SelectSyncCandidates(
std::unique_ptr<storage::Iterator<const PageInfo>> pages_it, size_t free_slots) {
std::vector<PageInfo> pages;
while (pages_it->Valid()) {
zx::time_utc timestamp = (*pages_it)->timestamp;
if (timestamp != PageInfo::kOpenedPageTimestamp) {
pages.push_back(**pages_it);
}
pages_it->Next();
}
if (free_slots < pages.size()) {
std::nth_element(pages.begin(), pages.begin() + free_slots, pages.end(),
[](const PageInfo& info1, const PageInfo& info2) {
return std::tie(info1.timestamp, info1.ledger_name, info1.page_id) <
std::tie(info2.timestamp, info2.ledger_name, info2.page_id);
});
pages.erase(pages.begin() + std::min(free_slots, pages.size()), pages.end());
}
return pages;
}
} // namespace
BackgroundSyncManager::BackgroundSyncManager(Environment* environment, PageUsageDb* db)
: BackgroundSyncManager(environment, db, kOpenPagesLimit) {}
BackgroundSyncManager::BackgroundSyncManager(Environment* environment, PageUsageDb* db,
size_t open_pages_limit)
: environment_(environment),
db_(db),
coroutine_manager_(environment_->coroutine_service()),
open_pages_limit_(open_pages_limit) {}
void BackgroundSyncManager::SetDelegate(Delegate* delegate) {
LEDGER_DCHECK(delegate);
LEDGER_DCHECK(!sync_delegate_);
sync_delegate_ = delegate;
}
void BackgroundSyncManager::OnExternallyUsed(absl::string_view ledger_name,
storage::PageIdView page_id) {
pages_connection_count_[{convert::ToString(ledger_name), convert::ToString(page_id)}]++;
}
void BackgroundSyncManager::OnExternallyUnused(absl::string_view ledger_name,
storage::PageIdView page_id) {
auto it =
pages_connection_count_.find({convert::ToString(ledger_name), convert::ToString(page_id)});
LEDGER_DCHECK(it != pages_connection_count_.end());
LEDGER_DCHECK(it->second > 0);
it->second--;
HandlePageIfUnused(it);
}
void BackgroundSyncManager::OnInternallyUsed(absl::string_view ledger_name,
storage::PageIdView page_id) {
// Behavior is the same of external and internal connections.
OnExternallyUsed(ledger_name, page_id);
}
void BackgroundSyncManager::OnInternallyUnused(absl::string_view ledger_name,
storage::PageIdView page_id) {
// Behavior is the same of external and internal connections.
OnExternallyUnused(ledger_name, page_id);
}
bool BackgroundSyncManager::IsDiscardable() const { return token_manager_.IsDiscardable(); }
void BackgroundSyncManager::SetOnDiscardable(fit::closure on_discardable) {
token_manager_.SetOnDiscardable(std::move(on_discardable));
}
void BackgroundSyncManager::HandlePageIfUnused(
std::map<std::pair<std::string, storage::PageId>, int32_t>::iterator it) {
if (it->second > 0) {
return;
}
pages_connection_count_.erase(it);
TrySync();
}
void BackgroundSyncManager::TrySync() {
LEDGER_DCHECK(sync_delegate_);
coroutine_manager_.StartCoroutine([this](coroutine::CoroutineHandler* handler) {
// Ensure |this| is not destructed until the coroutine has completed.
ExpiringToken token = token_manager_.CreateToken();
std::unique_ptr<storage::Iterator<const PageInfo>> pages_it;
Status status = db_->GetPages(handler, &pages_it);
if (status != Status::OK) {
return;
}
size_t open_pages = pages_connection_count_.size();
if (open_pages < open_pages_limit_) {
std::vector<PageInfo> sync_candidates =
SelectSyncCandidates(std::move(pages_it), open_pages_limit_ - open_pages);
for (const auto& page : sync_candidates) {
sync_delegate_->TrySyncClosedPage(page.ledger_name, page.page_id);
}
}
});
}
} // namespace ledger