При разработке и использовании распределенных систем перед нами стоит задача контроля целостности и идентичности данных между системами.
проблема примирения .
Требования, предъявляемые заказчиком, – это минимальное время проведения данной операции, поскольку чем раньше будет обнаружено несоответствие, тем легче будет устранить его последствия.
Задача существенно усложняется тем, что системы находятся в постоянном движении (~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
-
Яндекс.новости: Екатеринбург Захватывает Мир
19 Oct, 24 -
Гейб Ньюэлл Против Drm
19 Oct, 24 -
Таблица Дроидов. Выпуск 7
19 Oct, 24 -
Сравнение Sdk Для Android И Iphone
19 Oct, 24 -
Звездные Войны В Исходном Коде
19 Oct, 24