blob: 020d4025b77c0da0dd223544ba7d118d8c23f833 [file] [log] [blame]
// Copyright 2016 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/engine_impl/uss_migrator.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "components/sync/base/time.h"
#include "components/sync/engine_impl/model_type_worker.h"
#include "components/sync/protocol/sync.pb.h"
#include "components/sync/syncable/directory.h"
#include "components/sync/syncable/entry.h"
#include "components/sync/syncable/read_node.h"
#include "components/sync/syncable/read_transaction.h"
#include "components/sync/syncable/user_share.h"
namespace syncer {
namespace {
bool ExtractSyncEntity(ReadTransaction* trans,
int64_t id,
sync_pb::SyncEntity* entity) {
ReadNode read_node(trans);
if (read_node.InitByIdLookup(id) != BaseNode::INIT_OK)
return false;
const syncable::Entry& entry = *read_node.GetEntry();
// Copy the fields USS cares about from the server side of the directory so
// that we don't miss things that haven't been applied yet. See
// ModelTypeWorker::ProcessGetUpdatesResponse for which fields are used.
entity->set_id_string(entry.GetId().GetServerId());
entity->set_version(entry.GetServerVersion());
entity->set_mtime(TimeToProtoTime(entry.GetServerMtime()));
entity->set_ctime(TimeToProtoTime(entry.GetServerCtime()));
entity->set_name(entry.GetServerNonUniqueName());
entity->set_deleted(entry.GetServerIsDel());
entity->set_client_defined_unique_tag(entry.GetUniqueClientTag());
// It looks like there are fancy other ways to get e.g. passwords specifics
// out of Entry. Do we need to special-case them when we ship those types?
entity->mutable_specifics()->CopyFrom(entry.GetServerSpecifics());
return true;
}
} // namespace
bool MigrateDirectoryData(ModelType type,
UserShare* user_share,
ModelTypeWorker* worker) {
return MigrateDirectoryDataWithBatchSize(type, user_share, worker, 64);
}
bool MigrateDirectoryDataWithBatchSize(ModelType type,
UserShare* user_share,
ModelTypeWorker* worker,
int batch_size) {
DCHECK_NE(BOOKMARKS, type);
DCHECK_NE(PASSWORDS, type);
ReadTransaction trans(FROM_HERE, user_share);
ReadNode root(&trans);
if (root.InitTypeRoot(type) != BaseNode::INIT_OK) {
LOG(ERROR) << "Missing root node for " << ModelTypeToString(type);
// Inform the worker so it can trigger a fallback initial GetUpdates.
worker->AbortMigration();
return false;
}
// Get the progress marker and context from the directory.
sync_pb::DataTypeProgressMarker progress;
sync_pb::DataTypeContext context;
user_share->directory->GetDownloadProgress(type, &progress);
user_share->directory->GetDataTypeContext(trans.GetWrappedTrans(), type,
&context);
std::vector<int64_t> child_ids;
root.GetChildIds(&child_ids);
// Process |batch_size| entities at a time to reduce memory usage.
size_t i = 0;
while (i < child_ids.size()) {
// Vector to own the temporary entities.
std::vector<std::unique_ptr<sync_pb::SyncEntity>> entities;
// Vector of raw pointers for passing to ProcessGetUpdatesResponse().
SyncEntityList entity_ptrs;
const size_t batch_limit = std::min(i + batch_size, child_ids.size());
for (; i < batch_limit; i++) {
auto entity = std::make_unique<sync_pb::SyncEntity>();
if (!ExtractSyncEntity(&trans, child_ids[i], entity.get())) {
LOG(ERROR) << "Failed to fetch child node for "
<< ModelTypeToString(type);
// Inform the worker so it can clear any partial data and trigger a
// fallback initial GetUpdates.
worker->AbortMigration();
return false;
}
// Ignore tombstones; they are not included for initial GetUpdates.
if (!entity->deleted()) {
entity_ptrs.push_back(entity.get());
entities.push_back(std::move(entity));
}
}
worker->ProcessGetUpdatesResponse(progress, context, entity_ptrs, nullptr);
}
worker->PassiveApplyUpdates(nullptr);
return true;
}
} // namespace syncer