0

Add README to sync's components/ directories pointing to new docs

crrev.com/c/3401841 moves the docs/sync/ content to docs/website/
(different repository). Indicate this in the README-s and delete
docs/sync/.

Bug: 1006699
Fixed: 1101716
Change-Id: I9096664d9bc1064a33e1b4cfdaa8adc2bdab9eb9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3404277
Reviewed-by: Marc Treib <treib@chromium.org>
Commit-Queue: Victor Vianna <victorvianna@google.com>
Auto-Submit: Victor Vianna <victorvianna@google.com>
Cr-Commit-Position: refs/heads/main@{#964023}
This commit is contained in:
Victor Hugo Vianna Silva
2022-01-27 14:36:52 +00:00
committed by Chromium LUCI CQ
parent 42d73a5cb1
commit d1b14e1da4
11 changed files with 12 additions and 438 deletions
components
browser_sync
sync
sync_bookmarks
sync_device_info
sync_preferences
sync_sessions
sync_user_events
docs

@ -0,0 +1 @@
See components/sync/README.md.

@ -0,0 +1,4 @@
The component responsible for syncing user data to their account.
Docs are in [docs/website/site/developers/design-documents/sync/](https://source.chromium.org/chromium/chromium/src/+/main:docs/website/site/developers/design-documents/sync).
You can also visit them via
[chromium.org](https://www.chromium.org/developers/design-documents/sync).

@ -0,0 +1 @@
See components/sync/README.md.

@ -0,0 +1 @@
See components/sync/README.md.

@ -0,0 +1 @@
See components/sync/README.md.

@ -0,0 +1 @@
See components/sync/README.md.

@ -0,0 +1 @@
See components/sync/README.md.

@ -144,10 +144,8 @@ used when committed.
renderer to the screen.
* [Using the Origin Trials Framework](origin_trials_integration.md) - A
framework for conditionally enabling experimental APIs for testing.
* [`ClientTagBasedModelTypeProcessor` in Unified Sync and Storage](sync/uss/client_tag_based_model_type_processor.md) -
Notes on the central data structure used in Chrome Sync.
* [Chrome Sync's Model API](sync/model_api.md) - Data models used for syncing
information across devices using Chrome Sync.
* [Chrome Sync](https://source.chromium.org/chromium/chromium/src/+/main:docs/website/site/developers/design-documents/sync) -
Docs for the subsystem that allows one to sync data across devices.
* [Ozone Overview](ozone_overview.md) - Ozone is an abstraction layer between
the window system and low level input and graphics.
* [Optimizing Chrome Web UIs](optimizing_web_uis.md) - Notes on making webuis

@ -1,12 +0,0 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Services>Sync"
}
team_email: "chromium-reviews@chromium.org"

@ -1,302 +0,0 @@
# Chrome Sync's Model API
Chrome Sync operates on discrete, explicitly defined model types (bookmarks,
preferences, tabs, etc). These model types are individually responsible for
implementing their own local storage and responding to remote changes. This
guide is for developers interested in syncing data for their model type to the
cloud using Chrome Sync. It describes the newest version of the API, known as
Unified Sync and Storage (USS). There is also the deprecated [SyncableService
API] (aka Directory), which as of mid-2019 is still used by several legacy model
types, but "wrapped into" USS (see [SyncableServiceBasedBridge]).
[SyncableService API]: https://www.chromium.org/developers/design-documents/sync/syncable-service-api
[SyncableServiceBasedBridge]: https://cs.chromium.org/chromium/src/components/sync/model/syncable_service_based_bridge.h
[TOC]
## Overview
To correctly sync data, USS requires that sync metadata be stored alongside your
model data in a way such that they are written together atomically. **This is
very important!** Sync must be able to update the metadata for any local data
changes as part of the same write to disk. If you attempt to write data to disk
and only notify sync afterwards, a crash in between the two writes can result in
changes being dropped and never synced to the server, or data being duplicated
due to being committed more than once.
[`ModelTypeSyncBridge`][Bridge] is the interface the model code must implement.
The bridge is usually owned by a [`KeyedService`][KeyedService].
The correct place for the bridge generally lies as close to where your model
data is stored as possible, as the bridge needs to be able to inject metadata
updates into any local data changes that occur.
The bridge owns a [`ModelTypeChangeProcessor`][MTCP] object, which it uses to
communicate local changes to sync using the `Put` and `Delete` methods.
The processor will communicate remote changes from sync to the bridge using the
`MergeSyncData` and `ApplySyncChanges` methods, respectively for the initial
merge of remote and local data, and for incremental changes coming from sync.
[`MetadataChangeList`][MCL] is the way sync communicates metadata changes to the
storage mechanism. Note that it is typically implemented on a per-storage basis,
not a per-type basis.
[Bridge]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_sync_bridge.h
[KeyedService]: https://cs.chromium.org/chromium/src/components/keyed_service/core/keyed_service.h
[MTCP]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_change_processor.h
[MCL]: https://cs.chromium.org/chromium/src/components/sync/model/metadata_change_list.h
## Data
### Specifics
Model types will define a proto that contains the necessary fields of the
corresponding native type (e.g. [`TypedUrlSpecifics`][TypedUrlSpecifics]
contains a URL and a list of visit timestamps) and include it as a field in the
generic [`EntitySpecifics`][EntitySpecifics] proto. This is the form that all
communications with sync will use. This proto form of the model data is referred
to as the specifics.
[TypedUrlSpecifics]: https://cs.chromium.org/chromium/src/components/sync/protocol/typed_url_specifics.proto
[EntitySpecifics]: https://cs.chromium.org/search/?q="message+EntitySpecifics"+file:sync.proto
### Identifiers
There are two primary identifiers for entities: **storage key** and **client
tag**. The bridge will need to take an [`EntityData`][EntityData] object (which
contains the specifics) and be able generate both of these from it. For
non-legacy types without significant performance concerns, these will generally
be the same.
The storage key is meant to be the primary key in the local model/database.
Its whats used to refer to entities most of the time and, as its name implies,
the bridge needs to be able to look up local data and metadata entries in the
store using it. Because it is a local identifier, it can change as part of
database migrations, etc. This may be desirable for efficiency reasons.
The client tag is used to generate the **client tag hash**, which will identify
entities **across clients**. This means that its implementation can **never
change** once entities have begun to sync, without risking massive duplication
of entities. This means it must be generated using only immutable data in the
specifics. If your type does not have any immutable fields to use, you will need
to add one (e.g. a GUID, though be wary as they have the potential to conflict).
While the hash gets written to disk as part of the metadata, the tag itself is
never persisted locally.
[EntityData]: https://cs.chromium.org/chromium/src/components/sync/protocol/entity_data.h
## Storage
A crucial requirement of USS is that the model must add support for keeping
syncs metadata in the same storage as its normal data. The metadata consists of
one [`EntityMetadata`][EntityMetadata] proto for each data entity, and one
[`ModelTypeState`][ModelTypeState] proto containing metadata pertaining to the
state of the entire type (the progress marker, for example). This typically
requires two extra tables in a database to do (one for each type of proto).
Since the processor doesnt know anything about the store, the bridge provides
it with an implementation of the [`MetadataChangeList`][MCL] interface. The
change processor writes metadata through this interface when changes occur, and
the bridge simply has to ensure it gets passed along to the store and written
along with the data changes.
[EntityMetadata]: https://cs.chromium.org/chromium/src/components/sync/protocol/entity_metadata.proto
[ModelTypeState]: https://cs.chromium.org/chromium/src/components/sync/protocol/model_type_state.proto
### ModelTypeStore
While the model type may store its data however it chooses, many types use
[`ModelTypeStore`][Store], which was created specifically to provide a
convenient persistence solution. Its backed by a [LevelDB] to store serialized
protos to disk. `ModelTypeStore` provides two `MetadataChangeList`
implementations for convenience; both accessed via
[`ModelTypeStore::WriteBatch`][WriteBatch]. One passes metadata changes directly
into an existing `WriteBatch` and another caches them in memory until a
`WriteBatch` exists to consume them.
The store interface abstracts away the type and will handle setting up tables
for the types data, so multiple `ModelTypeStore` objects for different types
can share the same LevelDB backend just by specifying the same path and task
runner. Sync already has a backend it uses for DeviceInfo that can be shared by
other types via the [`ModelTypeStoreService`][StoreService].
[Store]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_store.h
[LevelDB]: https://github.com/google/leveldb/blob/master/doc/index.md
[WriteBatch]: https://cs.chromium.org/search/?q="class+WriteBatch"+file:model_type_store_base.h
[StoreService]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_store_service.h
## Implementing ModelTypeSyncBridge
### Initialization
The bridge is required to load all of the metadata for its type from storage and
provide it to the processor via the [`ModelReadyToSync`][ModelReadyToSync]
method **before any local changes occur**. This can be tricky if the thread the
bridge runs on is different from the storage mechanism. No data will be synced
with the server if the processor is never informed that the model is ready.
Since the tracking of changes and updating of metadata is completely
independent, there is no need to wait for the sync engine to start before
changes can be made. This prevents the need for an expensive association step in
the initialization.
[ModelReadyToSync]: https://cs.chromium.org/search/?q=ModelReadyToSync+file:/model_type_change_processor.h
### MergeSyncData
This method is called only once, when a type is first enabled. Sync will
download all the data it has for the type from the server and provide it to the
bridge using this method. Sync filters out any tombstones for this call, so
`EntityData::is_deleted()` will never be true for the provided entities. The
bridge must then examine the sync data and the local data and merge them
together:
* Any remote entities that dont exist locally must be be written to local
storage.
* Any local entities that dont exist remotely must be provided to sync via
[`ModelTypeChangeProcessor::Put`][Put].
* Any entities that appear in both sets must be merged and the model and sync
informed accordingly. Decide which copy of the data to use (or a merged
version or neither) and update the local store and sync as necessary to
reflect the decision. How the decision is made can vary by model type.
The [`MetadataChangeList`][MCL] passed into the function is already populated
with metadata for all the data passed in (note that neither the data nor the
metadata have been committed to storage yet at this point). It must be given to
the processor for any `Put` or `Delete` calls so the relevant metadata can be
added/updated/deleted, and then passed to the store for persisting along with
the data.
Note that if sync gets disabled and the metadata cleared, entities that
originated from other clients will exist as “local” entities the next time sync
starts and merge is called. Since tombstones are not provided for merge, this
can result in reviving the entity if it had been deleted on another client in
the meantime.
[Put]: https://cs.chromium.org/search/?q=Put+file:/model_type_change_processor.h
### ApplySyncChanges
While `MergeSyncData` provides the state of sync data using `EntityData`
objects, `ApplySyncChanges` provides changes to the state using
[`EntityChange`][EntityChange] objects. These changes must be applied to the
local state.
Heres an example implementation of a type using `ModelTypeStore`:
```cpp
absl::optional<ModelError> DeviceInfoSyncBridge::ApplySyncChanges(
std::unique_ptr<MetadataChangeList> metadata_change_list,
EntityChangeList entity_changes) {
std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
for (const EntityChange& change : entity_changes) {
if (change.type() == EntityChange::ACTION_DELETE) {
batch->DeleteData(change.storage_key());
} else {
batch->WriteData(change.storage_key(),
change.data().specifics.your_type().SerializeAsString());
}
}
batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
store_->CommitWriteBatch(std::move(batch), base::BindOnce(...));
NotifyModelOfChanges();
return {};
}
```
A conflict can occur when an entity has a pending local commit when an update
for the same entity comes from another client. In this case, the bridges
[`ResolveConflict`][ResolveConflict] method will have been called prior to the
`ApplySyncChanges` call in order to determine what should happen. This method
defaults to having the remote version overwrite the local version unless the
remote version is a tombstone, in which case the local version wins.
[EntityChange]: https://cs.chromium.org/chromium/src/components/sync/model/entity_change.h
[ResolveConflict]: https://cs.chromium.org/search/?q=ResolveConflict+file:/model_type_sync_bridge.h
### Local changes
The [`ModelTypeChangeProcessor`][MTCP] must be informed of any local changes via
its `Put` and `Delete` methods. Since the processor cannot do any useful
metadata tracking until `MergeSyncData` is called, the `IsTrackingMetadata`
method is provided. It can be checked as an optimization to prevent unnecessary
processing preparing the parameters to a `Put` or `Delete` call.
Heres an example of handling a local write using `ModelTypeStore`:
```cpp
void WriteLocalChange(std::string key, ModelData data) {
std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
if (change_processor()->IsTrackingMetadata()) {
change_processor()->Put(key, ModelToEntityData(data),
batch->GetMetadataChangeList());
}
batch->WriteData(key, data.specifics->SerializeAsString());
store_->CommitWriteBatch(std::move(batch), base::BindOnce(...));
}
```
## Error handling
If any errors occur during store operations that could compromise the
consistency of the data and metadata, the processors
[`ReportError`][ReportError] method should be called. The only exception to this
is errors during `MergeSyncData` or `ApplySyncChanges`, which should just return
a [`ModelError`][ModelError].
This will inform sync of the error, which will stop all communications with the
server so bad data doesnt get synced. Since the metadata might no longer be
valid, the bridge will asynchronously receive an `ApplyStopSyncChanges` call
with a non-null `MetadataChangeList` parameter. All the metadata will be cleared
from the store (if possible), and the type will be started again from scratch on
the next client restart.
[ReportError]: https://cs.chromium.org/search/?q=ReportError+file:/model_type_change_processor.h
[ModelError]: https://cs.chromium.org/chromium/src/components/sync/model/model_error.h
## Sync Integration Checklist
* Define your specifics proto in [`//components/sync/protocol/`][protocol].
* Add a field for it to [`EntitySpecifics`][EntitySpecifics].
* Add it to the [`ModelType`][ModelType] enum and
[`kModelTypeInfoMap`][info_map].
* Add it to the [proto value conversions][conversions] files.
* Register a [`ModelTypeController`][ModelTypeController] for your type in
[`SyncApiComponentFactoryImpl::CreateCommonDataTypeControllers`][CreateCommonDataTypeControllers] or platform-specific equivalent in
[`ChromeSyncClient::CreateDataTypeControllers`][CreateDataTypeControllers].
* Add your KeyedService dependency to
[`SyncServiceFactory`][SyncServiceFactory].
* Add an field for encrypted data to [`NigoriSpecifics`][NigoriSpecifics].
* If your type should have its own toggle in sync settings, add an entry to
the [`UserSelectableType`][UserSelectableType] enum, add a
[preference][pref_names] for tracking whether your type is enabled, and
map your type to the pref in [`GetPrefNameForType`][GetPrefName].
* Otherwise, if your type should be included in an existing toggle in sync
settings, add it in [`GetUserSelectableTypeInfo`]
[GetUserSelectableTypeInfo].
* Add to the `SyncModelTypes` enum in [`enums.xml`][enums] and to the
`SyncModelType` suffix in [`histograms.xml`][histograms].
[protocol]: https://cs.chromium.org/chromium/src/components/sync/protocol/
[ModelType]: https://cs.chromium.org/chromium/src/components/sync/base/model_type.h
[info_map]: https://cs.chromium.org/search/?q="kModelTypeInfoMap%5B%5D"+file:model_type.cc
[conversions]: https://cs.chromium.org/chromium/src/components/sync/protocol/proto_value_conversions.h
[ModelTypeController]: https://cs.chromium.org/chromium/src/components/sync/driver/model_type_controller.h
[CreateCommonDataTypeControllers]: https://cs.chromium.org/search/?q="SyncApiComponentFactoryImpl::CreateCommonDataTypeControllers"
[CreateDataTypeControllers]: https://cs.chromium.org/search/?q="ChromeSyncClient::CreateDataTypeControllers"
[SyncServiceFactory]: https://cs.chromium.org/search/?q=:SyncServiceFactory%5C(%5C)
[NigoriSpecifics]: https://cs.chromium.org/chromium/src/components/sync/protocol/nigori_specifics.proto
[UserSelectableType]: https://cs.chromium.org/chromium/src/components/sync/base/user_selectable_type.h?type=cs&q="enum+class+UserSelectableType"
[pref_names]: https://cs.chromium.org/chromium/src/components/sync/base/pref_names.h
[GetPrefName]: https://cs.chromium.org/search/?q=GetPrefNameForType+file:sync_prefs.cc
[GetUserSelectableTypeInfo]: https://cs.chromium.org/chromium/src/components/sync/base/user_selectable_type.cc?type=cs&q="UserSelectableTypeInfo+GetUserSelectableTypeInfo"+f:components/sync/base/user_selectable_type.cc
[enums]: https://cs.chromium.org/chromium/src/tools/metrics/histograms/enums.xml
[histograms]: https://cs.chromium.org/chromium/src/tools/metrics/histograms/histograms.xml
[DataTypeHistogram]: https://cs.chromium.org/chromium/src/components/sync/base/data_type_histogram.h
## Testing
The [`TwoClientTypedUrlsSyncTest`][UssTest] suite is probably a good place to start
for integration testing. Especially note the use of a `StatusChangeChecker` to
wait for events to happen.
[UssTest]: https://cs.chromium.org/chromium/src/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc

@ -1,120 +0,0 @@
# ClientTagBasedModelTypeProcessor
The [`ClientTagBasedModelTypeProcessor`][SMTP] is a crucial piece of the USS
codepath. It lives on the model thread and performs the tracking of sync
metadata for the [`ModelTypeSyncBridge`][MTSB] that owns it by implementing the
[`ModelTypeChangeProcessor`][MTCP] interface, as well as sending commit requests
to the [`ModelTypeWorker`][MTW] on the sync thread via the [`CommitQueue`][CQ]
interface and receiving updates from the same worker via the
[`ModelTypeProcessor`][MTP] interface.
This processor supports types that use a client tag, which is currently
includes all except bookmarks. This means all changes in flight (either incoming
remote changes provided via the [`ModelTypeWorker`][MTW], or local changes
reported by the [`ModelTypeSyncBridge`][MTSB]) must specify a client tag, which
is considered (after being hashed) the main global identifier of a sync entity.
[SMTP]: https://cs.chromium.org/chromium/src/components/sync/model/client_tag_based_model_type_processor.h
[MTSB]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_sync_bridge.h
[MTCP]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_change_processor.h
[MTW]: https://cs.chromium.org/chromium/src/components/sync/engine/model_type_worker.h
[CQ]: https://cs.chromium.org/chromium/src/components/sync/engine/commit_queue.h
[MTP]: https://cs.chromium.org/chromium/src/components/sync/engine/model_type_processor.h
[TOC]
## Lifetime
The bridge owns a processor object at all times and operates on the same thread
as it. If sync is disabled, the processor is destroyed but a new one is
immediately created to replace it.
## Processor State Machines
The processor sits between the model bridge and the sync engine. It has
knowledge of what state each is in based on the calls it has received and
performed. The states are not stored explicitly, but are implicit based on
state stored in the processor. Here are the states of each, with notes on their
transitions and how to determine them.
### Model States
* `UNREADY`
* Waiting for `ModelReadyToStart` to be called.
* Determined by: `waiting_for_metadata_ && !model_error_`
* `NEEDS_DATA`
* Waiting for data for pending commits to be loaded.
* This state is skipped if there are no pending commits.
* Determined by: `waiting_for_pending_data_ && !model_error_`
* `READY`
* The model is completely ready to sync.
* Determined by: `!waiting_for_metadata_ && !waiting_for_pending_data &&
!model_error`
* `ERROR`
* Something in the model or storage broke.
* This state is permanent until DisableSync destroys the object.
* Determined by: `!!model_error_`
### Sync States
* `DISCONNECTED`
* Sync for this type has not started.
* This state can be re-entered from any other state if Disconnect is
called.
* Determined by: `!error_handler_`.
* `STARTED`
* Sync has started but the model is not yet `READY` (or `ERROR`).
* This state is skipped if the model is ready before sync is.
* Determined by: `error_handler_ && start_callback_`
* `CONNECT_PENDING`
* Both the model and sync are ready. The start callback has been called
and we're waiting to connect to the sync thread.
* If the model was `ERROR`, the error is passed along and the callback is
cleared; we're really waiting for DisableSync instead of connect.
* Determined by: `error_handler_ && !start_callback_`
* `CONNECTED`
* We have a [`CommitQueue`][CQ] that passes changes to the
[`ModelTypeWorker`][MTW] on the sync thread.
* Determined by: `!!worker_`
### Processor States
Based on the interplay of the model and sync states, the processor effectively
progresses through 3 states worth noting:
* `UNINITIALIZED`
* Metadata isn't loaded so we have no knowledge of entities.
* `Put` and `Delete` calls are not allowed in this state (will DCHECK).
* `NOT_TRACKING`
* Indicates that not metadata is being tracked and that `Put` and `Delete`
calls will be ignored.
* This state is entered if the loaded metadata shows an initial merge
hasn't happened (`ModelTypeState::initial_sync_done` is false).
* Exposed via `IsTrackingMetadata` for optimization, not correctness.
* `TRACKING`
* Indicates that metadata is being tracked and `Put` and `Delete` calls
must happen for entity changes.
* This state is entered if the loaded metadata shows an initial merge
has happened (`ModelTypeState::initial_sync_done` is true).
* `SYNCING`
* Indicates that commits can be sent and updates can be received from the
sync server. This is a superstate of `TRACKING`.
* If the processor was in `TRACKING`, it progresses to this state as soon
as it gets connected to the worker.
* If the processor was in `NOT_TRACKING`, it progresses to this state
after `MergeSyncData` is called and the metadata is initialized.
## Entity Tracker
The [`ProcessorEntity`][PET] tracks the state of individual entities for
the processor. It keeps the [`EntityMetadata`][EM] proto in memory, as well as
any pending commit data until it gets acked by the server. It also stores the
special `commit_requested_sequence_number_`, which tracks the sequence number of
the last version that's been sent to the server.
The tracker holds the metadata in memory forever, which is needed so we know
what to update the on-disk memory with when we get a new local or remote change.
Changing this would require being able to handle updates asynchronously.
[PET]: https://cs.chromium.org/chromium/src/components/sync/model/processor_entity.h
[EM]: https://cs.chromium.org/chromium/src/components/sync/protocol/entity_metadata.proto