diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-05 17:15:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-11 07:47:18 +0000 |
commit | 7324afb043a0b1e623d8e8eb906cdc53bdeb4685 (patch) | |
tree | a3fe2d74ea9c9e142c390dac4ca0e219382ace46 /chromium/docs/sync | |
parent | 6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (diff) | |
download | qtwebengine-chromium-7324afb043a0b1e623d8e8eb906cdc53bdeb4685.tar.gz |
BASELINE: Update Chromium to 58.0.3029.54
Change-Id: I67f57065a7afdc8e4614adb5c0230281428df4d1
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/docs/sync')
-rw-r--r-- | chromium/docs/sync/model_api.md | 297 | ||||
-rw-r--r-- | chromium/docs/sync/uss/shared_model_type_processor.md | 114 |
2 files changed, 411 insertions, 0 deletions
diff --git a/chromium/docs/sync/model_api.md b/chromium/docs/sync/model_api.md new file mode 100644 index 00000000000..eebf34d8a09 --- /dev/null +++ b/chromium/docs/sync/model_api.md @@ -0,0 +1,297 @@ +# 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 early 2016 is still used by most model types. + +[SyncableService API]: https://www.chromium.org/developers/design-documents/sync/syncable-service-api + +[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 tends to be either a [`KeyedService`][KeyedService] or owned by one. +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 has access to 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 +`ApplySyncChanges` method. [`MetadataChangeList`][MCL] is the way sync will +communicate 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 used to uniquely identify entities locally within a client. +It’s what’s 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/model/entity_data.h + +## Storage + +A crucial requirement of USS is that the model must add support for keeping +sync’s 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 doesn’t 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. It’s 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 type’s 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 +[`ProfileSyncService::GetModelTypeStoreFactory`][StoreFactory] method. + +[Store]: https://cs.chromium.org/chromium/src/components/sync/model/model_type_store.h +[LevelDB]: http://leveldb.org/ +[WriteBatch]: https://cs.chromium.org/search/?q="class+WriteBatch"+file:model_type_store.h +[StoreFactory]: https://cs.chromium.org/search/?q=GetModelTypeStoreFactory+file:profile_sync_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 don’t exist locally must be be written to local + storage. +* Any local entities that don’t 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. + +Here’s an example implementation of a type using `ModelTypeStore`: + +```cpp +base::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->TransferMetadataChanges(std::move(metadata_change_list)); + store_->CommitWriteBatch(std::move(batch), base::Bind(...)); + 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 bridge’s +[`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. + +Here’s 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, specifics->SerializeAsString()); + store_->CommitWriteBatch(std::move(batch), base::Bind(...)); +} +``` + +## Error handling + +If any errors occur during store operations that could compromise the +consistency of the data and metadata, the processor’s +[`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 doesn’t get synced. Since the metadata might no longer be +valid, the bridge will asynchronously receive a `DisableSync` call (this is +implemented by the abstract base class; subclasses don’t need to do anything). +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 + [`ProfileSyncComponentsFactoryImpl::RegisterDataTypes`][RegisterDataTypes]. +* Tell sync how to access your `ModelTypeSyncBridge` in + [`ChromeSyncClient::GetSyncBridgeForModelType`][GetSyncBridge]. +* Add to the [start order list][kStartOrder]. +* Add an field for encrypted data to [`NigoriSpecifics`][NigoriSpecifics]. +* Add to two encrypted types translation functions in + [`nigori_util.cc`][nigori_util]. +* Add a [preference][pref_names] for tracking whether your type is enabled. +* Map your type to the pref in [`GetPrefNameForDataType`][GetPrefName]. +* Check whether you should be part of a [pref group][RegisterPrefGroup]. +* Add to the `SyncModelTypes` enum and `SyncModelType` suffix in + [`histograms.xml`][histograms]. +* Add to the [`SYNC_DATA_TYPE_HISTOGRAM`][DataTypeHistogram] macro. + +[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 +[RegisterDataTypes]: https://cs.chromium.org/search/?q="ProfileSyncComponentsFactoryImpl::RegisterDataTypes" +[GetSyncBridge]: https://cs.chromium.org/search/?q=GetSyncBridgeForModelType+file:chrome_sync_client.cc +[kStartOrder]: https://cs.chromium.org/search/?q="kStartOrder[]" +[NigoriSpecifics]: https://cs.chromium.org/chromium/src/components/sync/protocol/nigori_specifics.proto +[nigori_util]: https://cs.chromium.org/chromium/src/components/sync/syncable/nigori_util.cc +[pref_names]: https://cs.chromium.org/chromium/src/components/sync/base/pref_names.h +[GetPrefName]: https://cs.chromium.org/search/?q=::GetPrefNameForDataType+file:sync_prefs.cc +[RegisterPrefGroup]: https://cs.chromium.org/search/?q=::RegisterPrefGroups+file:sync_prefs.cc +[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 [`TwoClientUssSyncTest`][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_uss_sync_test.cc diff --git a/chromium/docs/sync/uss/shared_model_type_processor.md b/chromium/docs/sync/uss/shared_model_type_processor.md new file mode 100644 index 00000000000..46bc5a1114e --- /dev/null +++ b/chromium/docs/sync/uss/shared_model_type_processor.md @@ -0,0 +1,114 @@ +# SharedModelTypeProcessor + +The [`SharedModelTypeProcessor`][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. + +[SMTP]: https://cs.chromium.org/chromium/src/components/sync/model_impl/shared_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_impl/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 receieved 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 [`ProcessorEntityTracker`][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_impl/processor_entity_tracker.h +[EM]: https://cs.chromium.org/chromium/src/components/sync/protocol/entity_metadata.proto |