Как доставить изменения в пакетах (SQL Server)
В этом разделе описывается, как выполнять пакетную передачу изменений для синхронизации в платформе Sync Framework баз данных с помощью поставщиков SqlSyncProvider, SqlCeSyncProvider или DbSyncProvider. Код из этого раздела построен на следующих классах Sync Framework:
Основные сведения о пакетной передаче
По умолчанию платформа Sync Framework передает изменения на каждый узел в виде отдельного объекта DataSet. Пока изменения применяются к узлу, этот объект хранится в памяти. Такое поведение по умолчанию вполне применимо, если компьютер, на котором применяются изменения, располагает достаточным объемом памяти, а соединение надежное. Однако для некоторых приложений удобнее, если изменения разделены на пакеты. Рассмотрите следующий сценарий для приложения синхронизации.
Большое количество клиентов, использующих SqlCeSyncProvider, периодически синхронизируются с сервером, использующим SqlSyncProvider.
Объем памяти и место на диске ограничены на каждом клиенте.
Соединения между сервером и клиентами имеют низкую пропускную способность и подвержены перебоям, что часто затягивает процесс синхронизации и приводит к потере соединений.
Изменения, передаваемые в ходе типичного сеанса синхронизации, имеют большой размер (в КБ).
Для сценариев такого типа идеально подходит пакетная передача изменений, обладающая следующими возможностями.
Позволяет разработчику управлять объемом памяти (размера кэша данных в памяти), используемой для хранения изменений на клиенте. Это позволяет избежать возникновения ошибок нехватки памяти на клиенте.
Позволяет Sync Framework перезапускать неуспешно завершившуюся операцию синхронизации с начала текущего пакета, а не с начала всего набора изменений.
Может снизить или устранить потребность в повторной загрузке или повторном перечислении изменений на сервере в результате ошибочных операций.
Пакетную передачу просто настроить для двухуровневых и многоуровневых приложений. Ее можно использовать как для исходного сеанса синхронизации, так и для последующих сеансов.
Настройка и использование пакетной передачи
Пакетная передача в платформе Sync Framework работает следующим образом.
Приложение указывает размер кэша данных в памяти для каждого поставщика, участвующего в сеансе синхронизации.
Если оба поставщика указывают размер кэша, то Sync Framework использует для обоих поставщиков меньшее значение из указанных. Фактический размер кэша не будет превышать 110 % от меньшего из указанных размеров. Если в ходе сеанса синхронизации размер одной строки превысит 110 % от размера кэша, сеанс будет завершен с вызовом исключения.
Значение 0 (по умолчанию) отключает пакетную передачу. Если пакетная передача включена только в одном поставщике, то пакетная передача выполняется и для отправки, и для загрузки.
Приложение указывает расположение файлов буферизации для каждого поставщика. По умолчанию файлы буферизации записываются во временный каталог учетной записи, с которой работает процесс синхронизации.
Приложение вызывает метод Synchronize.
Sync Framework перечисляет изменения по одной строке. Если достигнут предельный размер кэша данных в памяти для поставщика источника, то изменения сохраняются в локальном файле буферизации, а данные записываются из памяти на диск. Этот процесс продолжается до завершения перечисления всех изменений.
В многоуровневых сценариях код службы и учетная запись-посредник в приложении направляет файлы буферизации в назначение. Дополнительные сведения см. в подразделе Код, относящийся к многоуровневым сценариям в этом разделе. В двухуровневых сценариях локальный файл уже находится в назначении, поскольку в этом случае весь код синхронизации выполняется в назначении.
Sync Framework выполняет десериализацию изменений из файлов буферизации и применяет эти изменения. Этот процесс продолжается до завершения применения всех изменений в назначении.
Все пакеты применяются в рамках одной транзакции. Транзакция не создается, пока последний пакет не получен поставщиком назначения.
В двухуровневых сценариях платформа Sync Framework очищает файл буферизации. В многоуровневых сценариях платформа Sync Framework очищает файлы буферизации на компьютере, где запущена синхронизация, однако файлы на промежуточном уровне должны очищаться учетной записью-посредником (как показано в образце метода Cleanup() далее в этом разделе). Для обработки ситуаций, в которых работа сеанса прерывается, на среднем уровне должен применяться процесс очистки файлов старше определенного срока.
Изменения данных, применяемые к узлу, доступны через свойство Context объекта DbChangesSelectedEventArgs. Если данные не имеют пакетной структуры, то событие ChangesSelected вызывается только один раз и все изменения доступны через свойство Context. Если данные имеют пакетную структуру, то событие ChangesSelected вызывается для каждого пакета и во время этого вызова доступны только изменения из этого пакета. Если необходимы изменения из всех пакетов, то обработайте все события ChangesSelected, сохранив возвращаемые данные.
В следующей таблице описаны типы и элементы, относящиеся к пакетной передаче данных. Для пакетирования является обязательным только свойство MemoryDataCacheSize, однако рекомендуется настроить и свойство BatchingDirectory.
BatchingDirectory
Возвращает или задает каталог, в котором сохраняются пакетные файлы, сохраняемые на диске. Указанный путь должен быть локальным каталогом выполняющегося поставщика или учетной записью-посредником. Пути к файлам в формате UNC и пути URI к объектам, не являющимся файлами, не поддерживаются.
Возвращает или задает значение, определяющее, следует ли выполнять очистку пакетных файлов после применения содержащихся в них изменений к объектам назначения. По умолчанию файлы очищаются.
MemoryDataCacheSize
Возвращает или задает максимальный объем памяти (в КБ), используемой Sync Framework для кэширования изменений перед сохранением на диске.
Событие, вызываемое после применения каждого пакета изменений в назначении.
Событие, вызываемое после записи каждого пакета изменений на диск.
DbBatchAppliedEventArgs
Предоставляет данные для события BatchApplied, включая номер текущего пакета и общее количество применяемых пакетов.
DbBatchSpooledEventArgs
Предоставляет данные для события BatchSpooled, включая номер текущего пакета и размер пакета.
Возвращает или задает имя файла, в который будут записаны буферизованные изменения.
Возвращает или задает значение, определяющее способ отправки данных — несколькими пакетами или единым объектом DataSet.
Возвращает или задает значение, определяющее, является ли текущий пакет изменений последним.
Возвращает или задает количество операций удаления, выполненных повторно за время сеанса синхронизации с пакетной обработкой изменений.
Повторное выполнение удалений в пакетах может быть вызвано порядком удаления значений первичного и внешнего ключей. Если удаление внешнего ключа отсутствует в текущем или более раннем пакете, то соответствующее удаление первичного ключа завершится ошибкой. После применения всех пакетов неуспешно завершившиеся операции удаления будут повторены.
SelectIncrementalChangesCommand (применимо только для DbSyncProvider)
Возвращает или задает запрос или хранимую процедуру для выборки добавочных изменений в локальной базе данных.
Возвращает или задает объект DataTable, содержащий изменения, подлежащие синхронизации. Если включена пакетная передача, то при доступе к этому свойству выполняется десериализация файла буферизации с диска. Затем все изменения, внесенные в таблицы, снова сохраняются в файл буферизации.
Возвращает или задает объект DataSet, который содержит строки из одноранговой базы данных. Возвращает значение NULL, если свойство IsDataBatched имеет значение true.
Общий код для двухуровневых и многоуровневых сценариевВ примерах кода из этого раздела показано, как обрабатывать пакетную передачу в двухуровневых и многоуровневых сценариях. Этот код взят из двух образцов, входящих в пакет SDK платформы Sync Framework: SharingAppDemo-CEProviderEndToEnd и WebSharingAppDemo-CEProviderEndToEnd . В начале каждого примера указывается расположение кода, например SharingAppDemo/CESharingForm . С точки зрения пакетной передачи, главным различием между этими двумя приложениями является дополнительный код, необходимый в многоуровневом сценарии для отправки и загрузки файлов буферизации и создания каталогов для каждого узла, выполняющего перечисление изменений.
В следующем примере кода из обработчика события synchronizeBtn_Click в SharingAppDemo/CESharingForm задается размер кэша данных в памяти и каталог, в который будут записываться файлы буферизации. Путь, указываемый в BatchingDirectory , должен быть локальным каталогом выполняющегося поставщика или учетной записью-посредником. Пути к файлам в формате UNC и пути URI к объектам, не являющимся файлами, не поддерживаются. Путь, указанный для BatchingDirectory , является корневым каталогом. Для каждого сеанса синхронизации платформа Sync Framework создает уникальный вложенный каталог, в котором хранятся файлы буферизации для этого сеанса. Этот каталог является уникальным для текущего сочетания источника и назначения, что позволяет изолировать файлы, используемые в различных сеансах.
В следующем примере кода из обработчика события synchronizeBtn_Click в WebSharingAppDemo/CESharingForm задаются те же свойства, однако каталог пакетной передачи в назначение задается для учетной записи-посредника, а не сразу для поставщика, как в двухуровневом сценарии:
В следующих примерах кода из файла SynchronizationHelper в обоих приложениях создаются методы для обработки событий BatchSpooled и BatchAppliedEvents , которые вызываются поставщиком в ходе перечисления и применения изменений:
Код, относящийся к многоуровневым сценариямОстальные примеры кода относятся только к многоуровневому сценарию в WebSharingAppDemo . Код, относящийся к многоуровневым сценариям, содержится в следующих трех файлах.
Контракт службы: IRelationalSyncContract
Учетная запись-посредник: RelationalProviderProxy
Оба поставщика, SqlSyncProvider и SqlCeSyncProvider, наследуют класс RelationalSyncProvider, и поэтому данный код применяется к обоим поставщикам. Дополнительные функции, связанные с хранилищем, разделяются на файлы учетной записи-посредника и службы для каждого типа поставщика.
Чтобы показать работу пакетной передачи в многоуровневом сценарии, рассмотрим сеанс синхронизации, где сервер выступает в роли источника, а клиент — в роли назначения. После записи изменений в локальный каталог на сервере для загруженных изменений выполняется следующий процесс.
На посреднике клиента вызывается метод GetChangeBatch . Как показано далее в образце кода, этот метод должен содержать специальный код для обработки пакетной передачи.
Служба получает файл пакета от поставщика SqlSyncProvider . Служба удаляет полный путь и отправляет по сети только имя файла. Это предотвращает передачу сведений о серверной структуре каталогов на клиенты.
Вызов посредником метода GetChangeBatch завершает работу.
Посредник обнаруживает, что изменения организованы в пакеты, и вызывает метод DownloadBatchFile , передавая имя файла пакета в качестве аргумента.
Посредник создает уникальный каталог (если для сеанса еще не существует каталог) в каталоге RelationalProviderProxy.BatchingDirectory для локального хранения этих файлов пакетов. Именем каталога служит идентификатор реплики однорангового узла, который выполняет перечисление изменений. Это гарантирует наличие в службе и учетной записи-посреднике уникального каталога для каждого однорангового узла, выполняющего перечисление.
Учетная запись-посредник загружает файл и сохраняет его локально. Учетная запись-посредник заменяет имя файла в контексте на новый полный путь к файлу пакета на локальном диске.
Учетная запись-посредник возвращает контекст в модуль взаимодействия.
Повторяйте шаги с 1 по 6, пока учетная запись-посредник не получит последний пакет.
Для переданных изменений выполняется следующий процесс.
Модуль взаимодействия вызывает ProcessChangeBatch на учетной записи-посреднике.
Учетная запись-посредник определяет, что файл относится к пакету, и выполняет следующие шаги.
Удаляет полный путь и отправляет по сети только имя файла.
Вызывает метод HasUploadedBatchFile , чтобы определить, был ли файл передан ранее. В таком случае шаг C выполнять необязательно.
Если метод HasUploadedBatchFile возвращает значение false, вызывается метод UploadBatchFile для службы и передает содержимое файла пакета.
Служба получает вызов UploadBatchFile и сохраняет пакет локально. Создание каталога выполняет аналогично шагу 4, указанному выше.
Вызывает метод ApplyChanges в службе.
Сервер получает вызов ApplyChanges и определяет, что файл относится к пакету. Сервер заменяет имя файла в контексте на новый полный путь к файлу пакета на локальном диске.
Сервер передает объект DbSyncContext локальному поставщику SqlSyncProvider .
Повторяйте шаги с 1 по 6, пока не будет отправлен последний пакет.
В следующем примере кода из IRelationalSyncContract указываются методы отправки и загрузки, которые используются для передачи файлов буферизации на средний уровень и обратно:
В следующих примерах кода из RelationalWebSyncService доступны методы UploadBatchFile и DownloadBatchFile , определенные в контракте, и приводится дополнительная логика обработки пакетной передачи в следующих методах:
Cleanup : очищает все файлы буферизации из указанного каталога (или временного каталога, если каталог не указан).
GetChanges : проверяет, имеют ли данные пакетную структуру, и в таком случае удаляет путь к каталогу файла буферизации, чтобы путь не отправлялся по сети. В многоуровневых сценариях отправка полных путей к каталогам по сетевому подключению представляет угрозу безопасности. Именем файла является идентификатор GUID.
HasUploadedBatchFile : возвращает значение, показывающее, был ли указанный файл пакета отправлен в службу.
ApplyChanges : проверяет, имеют ли данные пакетную структуру, и в таком случае проверяет, был ли передан ожидаемый файл пакета. Если файл еще не передан, вызывается исключение. Клиент должен передать файл буферизации перед вызовом метода ApplyChanges .
В следующих примерах кода из RelationalProviderProxy задаются свойства и вызываются методы веб-службы.
BatchingDirectory : позволяет приложению задавать каталог пакетной передачи для среднего уровня.
EndSession : очищает все файлы буферизации из указанного каталога.
GetChangeBatch : загружает пакеты изменений, вызывая метод DownloadBatchFile .
ProcessChangeBatch : передает пакеты изменений, вызывая метод UploadBatchFile .