Согласование – Проверка Целостности Данных В Распределенных Системах



Согласование – проверка целостности данных в распределенных системах

При разработке и использовании распределенных систем перед нами стоит задача контроля целостности и идентичности данных между системами.

проблема примирения .

Требования, предъявляемые заказчиком, – это минимальное время проведения данной операции, поскольку чем раньше будет обнаружено несоответствие, тем легче будет устранить его последствия.

Задача существенно усложняется тем, что системы находятся в постоянном движении (~100 000 транзакций в час) и добиться 0% расхождений не удастся.



Основная идея

Основную идею решения можно описать следующей схемой.

Рассмотрим каждый из элементов отдельно.



Согласование – проверка целостности данных в распределенных системах



Адаптеры данных

Каждая из систем создается для своей предметной области и, как следствие, описания объектов могут существенно отличаться.

Нам нужно лишь сравнить определенный набор полей из этих объектов.

Для упрощения процедуры сравнения приведем объекты к единому формату, написав для каждого источника данных свой адаптер.

Приведение объектов к единому формату позволяет существенно сократить объем используемой памяти, поскольку мы будем хранить только сравниваемые поля.

Под капотом адаптер может иметь любой источник данных: HttpClient , SqlClient , DynamoDbClient и т. д.

Согласование – проверка целостности данных в распределенных системах

Ниже представлен интерфейс IАдаптер что необходимо реализовать:

  
  
  
  
  
  
   

public interface IAdapter<T> where T : IModel { int Id { get; } Task<IEnumerable<T>> GetItemsAsync(ISearchModel searchModel); } public interface IModel { Guid Id { get; } int GetHash(); }



Хранилище

Согласование данных можно начать только после того, как все данные будут прочитаны, поскольку адаптеры могут возвращать их в случайном порядке.

В этом случае объема оперативной памяти может не хватить, особенно если вы одновременно запускаете несколько сверок, указывая большие временные интервалы.

Давайте посмотрим на интерфейс ISхранилище

public interface IStorage { int SourceAdapterId { get; } int TargetAdapterId { get; } int MaxWriteCapacity { get; } Task InitializeAsync(); Task<int> WriteItemsAsync(IEnumerable<IModel> items, int adapterId); Task<IEnumerable<IResultModel>> GetDifferenceAsync(ISearchDifferenceModel model); } public interface ISearchDifferenceModel { int Offset { get; } int Limit { get; } }



Хранилище.

Реализация на базе MS SQL

Мы реализовали ISхранилище , с использованием MS SQL, что позволило выполнить сравнение полностью на стороне сервера БД.

Для хранения восстановленных значений достаточно создать следующую таблицу:

CREATE TABLE [dbo].

[Storage_1540747667] ( [id] UNIQUEIDENTIFIER NOT NULL, [adapterid] INT NOT NULL, [qty] INT NOT NULL, [price] INT NOT NULL, CONSTRAINT [PK_Storage_1540747667] PRIMARY KEY ([id], [adapterid]) )

Каждая запись содержит системные поля ( [идентификатор] , [идентификатор адаптера] ) и поля, по которым осуществляется сравнение ( [кол-во] , [цена] ).

Несколько слов о системных полях: [идентификатор] — уникальный идентификатор записи, одинаковый в обеих системах [идентификатор адаптера] — ID адаптера, через который была получена запись Поскольку процессы сверки могут идти параллельно и иметь перекрывающиеся интервалы, для каждого из них мы создаем таблицу с уникальным именем.

Если сверка прошла успешно, эта таблица удаляется, в противном случае отправляется отчет со списком записей, в которых есть расхождения.



Хранилище.

Сравнение значений



Согласование – проверка целостности данных в распределенных системах

Представим, что у нас есть 2 набора, элементы которых имеют абсолютно одинаковый набор полей.

Рассмотрим 4 возможных случая их пересечения: А .

Ээлементы присутствуют только в левом наборе Б .

Ээлементы присутствуют в обоих наборах, но имеют разное значение С .

Ээлементы присутствуют только в правильном наборе Д .

Ээлементы присутствуют в обоих наборах и имеют одинаковые значения В конкретной задаче нам необходимо найти элементы, описанные в случаях А, Б, С .

Получить нужный результат можно за один запрос MS SQL через ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ :

select [s1].

[id], [s1].

[adapterid] from [dbo].

[Storage_1540758006] as [s1] full outer join [dbo].

[Storage_1540758006] as [s2] on [s2].

[id] = [s1].

[id] and [s2].

[adapterid] != [s1].

[adapterid] and [s2].

[qty] = [s1].

[qty] and [s2].

[price] = [s1].

[price] where [s2].

[id] is nul

Вывод этого запроса может содержать 4 типа записей, соответствующих исходным требованиям.

# идентификатор идентификатор адаптера комментарий
1 руководство1 adp1 Запись присутствует только в левом наборе.

Происходит А

2 руководство2 adp2 Запись присутствует только в правильном наборе.

Происходит С

3 руководство3 adp1 Записи присутствуют в обоих наборах, но имеют разное значение.

Происходит Б

4 руководство3 adp2 Записи присутствуют в обоих наборах, но имеют разное значение.

Происходит Б



Хранилище.

Хеширование

Используя хеширование сравниваемых объектов, можно существенно снизить стоимость операций записи и сравнения.

Особенно, если речь идет о сравнении десятков полей.

Самым универсальным методом оказалось хеширование сериализованного представления объекта.



Согласование – проверка целостности данных в распределенных системах

1. Для хеширования используем стандартный метод ПолучитьХэшКод() , который возвращает int32 и переопределяется для всех примитивных типов.

2. В этом случае вероятность коллизий маловероятна, поскольку сравниваются только записи с одинаковыми идентификаторами.

Давайте посмотрим на структуру таблицы, используемую для этой оптимизации:

CREATE TABLE [dbo].

[Storage_1540758006] ( [id] UNIQUEIDENTIFIER NOT NULL, [adapterid] INT NOT NULL, [hash] INT NOT NULL, CONSTRAINT [PK_Storage_1540758006] PRIMARY KEY ([id], [adapterid], [hash]) )

Плюсом такой структуры является постоянная стоимость хранения одной записи (24 байта), которая не будет зависеть от количества сравниваемых полей.

Естественно, процедура сравнения претерпевает изменения и становится значительно проще.



select [s1].

[id], [s1].

[adapterid] from [dbo].

[Storage_1540758006] as [s1] full outer join [dbo].

[Storage_1540758006] as [s2] on [s2].

[id] = [s1].

[id] and [s2].

[adapterid] != [s1].

[adapterid] and [s2].

[hash] = [s1].

[hash] where [s2].

[id] is null



Процессор

В этом разделе мы поговорим о классе, содержащем всю бизнес-логику сверки, а именно: 1. параллельное чтение данных с адаптеров 2. хеширование данных 3. буферизованная запись значений в базу данных 4. выдача результатов Более полное описание процесса согласования можно получить, рассмотрев диаграмму последовательности и интерфейс.

IПроцессор.



public interface IProcessor<T> where T : IModel { IAdapter<T> SourceAdapter { get; } IAdapter<T> TargetAdapter { get; } IStorage Storage { get; } Task<IProcessResult> ProcessAsync(); Task<IEnumerable<IResultModel>> GetDifferenceAsync(ISearchDifferenceModel model); }



Согласование – проверка целостности данных в распределенных системах



Благодарности

Огромное спасибо моим коллегам из MySale Group за отзыв: АнтонСтрахов , Несстория , Барлог_5 , Костя Кривцун и ВетерМанве - автор идеи.

Теги: #Микросервисы #C++ #.

NET #Распределенные системы #sql #mssql #mssql #reconciliation

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.