blob: 6eff6b84a364eb246d9b6a3d68fd919191cc6cf8 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "components/sync_sessions/sessions_global_id_mapper.h"
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
namespace sync_sessions {
namespace {
// Clean up navigation tracking when we have over this many global_ids.
const size_t kNavigationTrackingCleanupThreshold = 100;
// When we clean up navigation tracking, delete this many global_ids.
const int kNavigationTrackingCleanupAmount = 10;
// Used to record conflict information into histogram Sync.GlobalIdConflict.
enum SyncGlobalIdConflict {
CONFLICT = 0,
NO_CONFLICT_NEW_ID,
NO_CONFLICT_SAME_IDS,
CONFLICT_MAX,
};
} // namespace
SessionsGlobalIdMapper::SessionsGlobalIdMapper() = default;
SessionsGlobalIdMapper::~SessionsGlobalIdMapper() = default;
void SessionsGlobalIdMapper::AddGlobalIdChangeObserver(
syncer::GlobalIdChange callback) {
global_id_change_observers_.push_back(std::move(callback));
}
int64_t SessionsGlobalIdMapper::GetLatestGlobalId(int64_t global_id) {
auto g2u_iter = global_to_unique_.find(global_id);
if (g2u_iter != global_to_unique_.end()) {
auto u2g_iter = unique_to_current_global_.find(g2u_iter->second);
if (u2g_iter != unique_to_current_global_.end()) {
return u2g_iter->second;
}
}
return global_id;
}
void SessionsGlobalIdMapper::TrackNavigationId(const base::Time& timestamp,
int unique_id) {
// The expectation is that global_id will update for a given unique_id, which
// should accurately and uniquely represent a single navigation. It is
// theoretically possible for two unique_ids to map to the same global_id, but
// hopefully rare enough that it doesn't cause much harm. Lets record metrics
// verify this theory.
int64_t global_id = timestamp.ToInternalValue();
// It is possible that the global_id has not been set yet for this navigation.
// In this case there's nothing here for us to track yet.
if (global_id == 0) {
return;
}
DCHECK_NE(0, unique_id);
auto g2u_iter = global_to_unique_.find(global_id);
if (g2u_iter == global_to_unique_.end()) {
global_to_unique_.insert(g2u_iter, std::make_pair(global_id, unique_id));
UMA_HISTOGRAM_ENUMERATION("Sync.GlobalIdConflict", NO_CONFLICT_NEW_ID,
CONFLICT_MAX);
} else if (g2u_iter->second != unique_id) {
UMA_HISTOGRAM_ENUMERATION("Sync.GlobalIdConflict", CONFLICT, CONFLICT_MAX);
} else {
UMA_HISTOGRAM_ENUMERATION("Sync.GlobalIdConflict", NO_CONFLICT_SAME_IDS,
CONFLICT_MAX);
}
auto u2g_iter = unique_to_current_global_.find(unique_id);
if (u2g_iter == unique_to_current_global_.end()) {
unique_to_current_global_.insert(u2g_iter,
std::make_pair(unique_id, global_id));
} else if (u2g_iter->second != global_id) {
// Remember the old_global_id before we insert and invalidate out iter.
int64_t old_global_id = u2g_iter->second;
// TODO(skym): Use insert_or_assign with hint once on C++17.
unique_to_current_global_[unique_id] = global_id;
// This should be done after updating unique_to_current_global_ in case one
// of our observers calls into GetLatestGlobalId().
for (auto& observer : global_id_change_observers_) {
observer.Run(old_global_id, global_id);
}
}
CleanupNavigationTracking();
}
void SessionsGlobalIdMapper::CleanupNavigationTracking() {
DCHECK(kNavigationTrackingCleanupThreshold >
kNavigationTrackingCleanupAmount);
// |global_to_unique_| is implicitly ordered by least recently created, which
// means we can drop from the beginning.
if (global_to_unique_.size() > kNavigationTrackingCleanupThreshold) {
auto iter = global_to_unique_.begin();
std::advance(iter, kNavigationTrackingCleanupAmount);
global_to_unique_.erase(global_to_unique_.begin(), iter);
// While |unique_id|s do get bigger for the most part, this isn't a great
// thing to make assumptions about, and an old tab may get refreshed often
// and still be very important. So instead just delete anything that's
// orphaned from |global_to_unique_|.
base::EraseIf(unique_to_current_global_,
[this](const std::pair<int, int64_t> kv) {
return !base::ContainsKey(global_to_unique_, kv.second);
});
}
}
} // namespace sync_sessions