Flexible IDs are used to identify replicas, items, or change units. Sync Framework supports the following types of flexible IDs:
A fixed-length ID is always the same length each time it is used. The length is specified in the ID format schema.
A variable-length ID can vary in length each time it is used. The maximum length is specified in the ID format schema.
Managed code An ID is represented by the SyncId class. This class contains the ID and a value that indicates whether the ID is variable length.
Unmanaged code An ID is represented as an array of bytes. Variable-length IDs are represented by the SYNC_ID structure. This structure contains the length of the ID and the ID represented as an array of bytes.
The ID formats are defined in a schema that specifies the following information about each ID in the system:
Whether the ID is fixed length or variable length.
The size if the ID is fixed length, or the maximum size if the ID is variable length.
Managed code The ID format schema is specified by using the SyncIdFormatGroup class.
Unmanaged code The ID format schema is specified by using the ID_PARAMETERS structure.
This schema must be provided to various Sync Framework methods and is used to ensure that all objects use the same ID schema information.
In a synchronization session, both providers are queried for their schemas. The schemas are then compared to verify that both providers are using the same schema. If this verification fails, the session will not be created.
Managed code The session gets the schema of a provider by using the IdFormats property.
Unmanaged code The session gets the schema of a provider by using the ISyncProvider::GetIdParameters method.
The format of an ID that is passed to a Sync Framework method must match the format that is specified in the ID format schema or the method will fail.
Recommended Format for Global IDs
A global ID is required for each item in the synchronization scope and must be unique across a synchronization community. A global ID is a flexible ID and, therefore, can use any format. However, because the metadata can represent an ordered group of IDs as a single range, the metadata is more compact when IDs are enumerated as ordered groups. The use of ranges also makes metadata maintenance more efficient. Therefore, it can be useful to make the order of the IDs fit logically with the order that is convenient for enumeration. For the format for a global ID, we recommend that you use a 16-byte GUID with an 8-byte prefix, such as the managed SyncGlobalId class or the unmanaged SYNC_GID structure.
Special Considerations for Hierarchical Data
In situations in which the items that are being synchronized have a hierarchy, it can be useful to place a monotonically increasing number, such as a clock time, in the ID prefix. Because parents will typically be created before children, parents will appear earlier in the enumeration order. This enables a provider to minimize the requirement to deal with items out of the order in which they must be applied.
One approach to assigning global IDs for hierarchical data is the following:
Create a counter that is large enough to index into all unique items in the replica.
Starting at the root of the scope, favor depth over breadth and traverse the system.
For each item (parent or child), generate a GUID.
Prefix the GUID with the wall clock time.
New items that are created can be assigned global IDs by using this same method, regardless of their containers or nested individual items.
Memory Handling in Unmanaged Code
IDs are represented as pointers to byte arrays. Therefore, for fixed-length IDs, casting the ID that is being used to a byte pointer is sufficient. For variable-length IDs, the ID data must be prefixed with the buffer size, as in the SYNC_ID structure.
Sync Framework will never maintain a reference to an ID that is allocated by a caller. If Sync Framework must keep an ID, it will allocate its own memory for the ID and copy the ID internally. Callers are responsible for allocating and freeing memory that is associated with IDs.
IDs As Return Values in Unmanaged Code
Variable-length IDs are returned from Sync Framework methods in a two-call process:
The method is called to determine the size of the ID.
The caller allocates memory and calls the function again, and this time provides the newly allocated buffer and its size.
A user can optimize this process by either allocating a buffer of the correct size or allocating a buffer of the maximum size. In this case, the first call is unnecessary, although the caller should always verify the returned HRESULT to make sure that the buffer was the correct size.
Methods that return fixed-length IDs do not use the two-step process. Instead, methods just supply a buffer of the correct length. The size parameter is optional when you request a fixed-length ID (NULL can be supplied). If the size is passed in, the methods act as in the variable length case. This means that if the size is smaller than the ID that is to be returned, an error code indicating that more data is available will be returned and the size variable will store the size that is required. If the size is larger than the required size, the method will succeed and the required size will be returned.
Blocks of ordered IDs can be referred to as a range. A range uses the lower and upper IDs as the closed bounds for the range.
Managed code The range bounds are passed directly to methods that work with ranges.
Unmanaged code A range is represented by using the SYNC_RANGE structure.