Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 Сохранение типизированной переменной в Memory Mapped File  [new]
dundin
Member

Откуда:
Сообщений: 41
Доброго дня всем!
INTRO:
Есть несколько экземпляров работающей проги на одной машине.
Каждая из них добывает JSON массив некоторых данных, парсит, обрабатывает его, и обработанный результат записывает в переменную TRecord = record...

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

Для решения этой задачи я использую Memory Mapped Files.
Т.е. у каждого экземпляра есть свой такой общедоступный Memory Mapped File.

Вопрос: как в таком файле хранить значение типизированной переменной?
В типе есть строки неопределенной длины и есть динамические массивы, длина которых заранее не известна.

Что я уже попробовал?
1. Я использовал в InMemory SQL. Создал таблицы для хранения этих данных, а в Memory Mapped Files хранил SQL дампы каждого экземпляра. В итоге каждый экземпляр мог пробежаться по всем MMF, собрать общий дамп и заполнить из него базу.
В итоге у каждого экземпляра была своя копия общей базы, и эти копии были у всех одинаковы.
Но эту процедуру нужно делать раз в секунду.
Когда начал тестировать - увидел что ежесекундное создание новой копии базы из SQL дампа, да еще и на нескольких экземплярах программы - это все значительно увеличивает нагрузку на проц. Решил отказаться.

2. Затем я выкинул InMemory SQL, и в MMF стал хранить просто JSON массивы в виде строк.
В итоге опять же каждый экземпляр мог пробежаться по всем MMF, обработать JSON массив и создать свою копию общего списка данных. И опять же, когда начал тестировать - увидел что вот это вот "обработать" грузит проц. Фактически каждый экземпляр делает одну и ту же работу, в то время когда каждый может обработать свою порцию данных и поделиться ей с другими.

Собственно отсюда и вопрос: есть переменная array of TMyType . В TMyType есть поля - динамические массивы. Нужно как то записать это все в Memory Mapped File чтоб другой экземпляр мог считать уже обработанные данные в свой array of TMyType.

Может как то можно этот array сериализовать в поток?
14 мар 19, 11:10    [21832190]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
dundin
В итоге у каждого экземпляра была своя копия общей базы


зачем? пуст ьи дальше пользуются этой общей базой.
14 мар 19, 11:36    [21832247]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
у тебя фактически получается что-то типа распределённого кэша.

"обработанными данными нужно поделиться с другими экземплярами"

соответственно тебе и копать в сторону cache coherency алгоритмов.

чтобы каждый раз, когда допустим переменную X хотят модиффицироват ьдве программы, одна из нихъ это начинала делать, а другая ждала своей очереди, а потом по реезультату работы второй - и первая её перечитывала, знала что после неё уже снова изменили.
14 мар 19, 11:39    [21832253]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
dundin
Member

Откуда:
Сообщений: 41
Arioch
у тебя фактически получается что-то типа распределённого кэша.

"обработанными данными нужно поделиться с другими экземплярами"

соответственно тебе и копать в сторону cache coherency алгоритмов.

чтобы каждый раз, когда допустим переменную X хотят модиффицироват ьдве программы, одна из нихъ это начинала делать, а другая ждала своей очереди, а потом по реезультату работы второй - и первая её перечитывала, знала что после неё уже снова изменили.


Не совсем. Переменные менять не нужно. Каждый экземпляр имеет у себя свой обработанный массив данных. Этот массив уже никто менять не будет - ни сама программа, ни другие экземпляры.
Поэтому вот эта функциональность
Arioch
чтобы каждый раз, когда допустим переменную X хотят модиффицироват ьдве программы, одна из нихъ это начинала делать, а другая ждала своей очереди, а потом по реезультату работы второй - и первая её перечитывала, знала что после неё уже снова изменили.


..не требуется.

На запрос "cache coherency delphi" гугл ничего дельного не выдает.

Когда требуется общая память - в большинстве случаев используют Memory Mapped File.
Мой вопрос только в том, как в нем хранить массивы некоторых структурированных данных.
14 мар 19, 11:55    [21832284]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
dundin
На запрос "cache coherency delphi" гугл

а при чём тут delphi ? это общие алгоритмы, пиши их на чём хочешь.
https://en.wikipedia.org/wiki/Cache_coherence

> Этот массив уже никто менять не будет

Тогда строй очереди.
Что-то типа D-BUS

Очередь заданий на обсчёт (не просчитанных ещё объектов)

и отдельно

Очередь обсчитанных объектов

Учитывай, что твои экземпляры программ могут зависнуть/отвалиться

Очереди обычно реализуются через "кольцевой буфер". Но ожно и через списки, если важно удалять/вставлять в середину очереди.

Т.е. на вход первой очереди поступает нерассчитанный элемент. ХЗ откуда - не важно. Главное факт "это надо посчитать"
Потом с выхода первой очереди берется необсчитанная задача, но с очереди не удаляется - помечается "я, программа номер такая-то (Process-ID или Thread-ID или ещё что) взяла этот элемент в работу в такое-то время (например GetTickCount), другие программы такую задачу не берут себе.
Когда она его заканчивает - она его удаляет с первой очереди и вставляет во вторую.
Если за вменяемое время задача не просчитана - считаем, что программа номе N умерла, отключаем её от обоих очередей, задачу снова помечаем как необработанную и свободную для всех. Поскольку она по прежнему торчит на самом выходе - её тут же кто-то другой возьмёт в работу.

На очереди №2 все вставленные элементы (результаты расчетов) помечаются количеством подключённых к очереди "читателей" (а можно и полный список ID читателей, но это памяти больше возьмёт) на момент вставки элемента. Каждый читатель уменьшает счётчик. Когда счетчик доходит до нуля - элемент снимается с очереди "(все и так его прочитали)". Фактически, это обычный счётчик ссылок - ARC, аналогично string и interface типам.

Опять же, надо отрабатывать ситуации "посреди работы в очередь №2 влез ещё один новый читатель" или "программа №N считается безвременно погибшей и отключается от обоих очередей". Они легко и просто не отработаются, тут придется потратить время на опрос ВСЕХ программ, но и возникать они будут крайне редко.

Само собой, что все изменения признаков должны проводиться атомарными функциями
14 мар 19, 12:09    [21832316]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
kealon(Ruslan)
Member

Откуда: Нижневартовск
Сообщений: 4481
dundin
Может как то можно этот array сериализовать в поток?
можно, TTypeMarshaller достаточно, смотри как пример TJSONMarshal в REST.JsonReflect
14 мар 19, 12:54    [21832398]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1361
dundin
Этими уже обработанными данными нужно поделиться с другими экземплярами.

Нужен обычный брокер сообщений с моделью N producer M consumer
На выбор: Kafka, RabbitMQ, Redis
Ну или свой лисапет
14 мар 19, 13:27    [21832457]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
pub-sub нужен

что для Windows есть вместо D-Bus ?
14 мар 19, 13:41    [21832495]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10852
dundin
Может как то можно этот array сериализовать в поток?
Перед каждым полем переменной длины пиши в поток эту длину
TMyRec = record
  a: Integer;
  b: UnicodeString;  // или AnsiString
  c: array of Integer;
  procedure SaveToStream(AStrm: TStream);
  procedure LoadFromStream(AStrm: TStream);
end;

procedure TMyRec.SaveToStream(AStrm: TStream);
var
  LLen: Integer;
begin
  AStrm.WriteBuffer(a, SizeOf(a));

  LLen := Length(b);
  AStrm.WriteBuffer(LLen, SizeOf(LLen));
  AStrm.WriteBuffer(b[Low(b)], LLen * SizeOf(b[Low(b)]));

  LLen := Length(c);
  AStrm.WriteBuffer(LLen, SizeOf(LLen));
  AStrm.WriteBuffer(c[Low(c)], LLen * SizeOf(c[Low(c)]));
end;

procedure LoadFromStream(AStrm: TStream);
var
  LLen: Integer;
begin
  AStrm.ReadBuffer(a, SizeOf(a));

  AStrm.ReadBuffer(LLen, SizeOf(LLen));
  SetLength(b, LLen);
  AStrm.ReadBuffer(b[Low(b)], LLen * SizeOf(b[Low(b)]));

  AStrm.ReadBuffer(LLen, SizeOf(LLen));
  SetLength(c, LLen);
  AStrm.ReadBuffer(c[Low(c)], LLen * SizeOf(c[Low(c)]));
end;
14 мар 19, 13:56    [21832537]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
dundin
Member

Откуда:
Сообщений: 41
Коллеги, гигантское спасибо за ответы и помощь!
Буду пробовать все варианты.

X-Cite
Нужен обычный брокер сообщений с моделью N producer M consumer
На выбор: Kafka, RabbitMQ, Redis
Ну или свой лисапет


У меня сейчас фраза "поделиться данными" реализована с помощью MySQL. Т.е. каждый шлет свои данные запросом и может в любой момент получить общие данные.
Но цель переделок - избавиться от централизации в виде MySQL сервера и вообще от каких либо сторонних программ.
Я искренне верю что можно не палить из пушки по воробьям и программы, работающие на одной машине могут договариваться сами, а не через какое либо приложение.

Остальные варианты буду пробовать.
14 мар 19, 18:59    [21832953]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1361
dundin
Коллеги, гигантское спасибо за ответы и помощь!
Буду пробовать все варианты.

X-Cite
Нужен обычный брокер сообщений с моделью N producer M consumer
На выбор: Kafka, RabbitMQ, Redis
Ну или свой лисапет


У меня сейчас фраза "поделиться данными" реализована с помощью MySQL. Т.е. каждый шлет свои данные запросом и может в любой момент получить общие данные.
Но цель переделок - избавиться от централизации в виде MySQL сервера и вообще от каких либо сторонних программ.
Я искренне верю что можно не палить из пушки по воробьям и программы, работающие на одной машине могут договариваться сами, а не через какое либо приложение.

Остальные варианты буду пробовать.


https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-broadcastsystemmessage
https://docs.microsoft.com/en-us/windows/desktop/winmsg/about-messages-and-message-queues#broadcasting-messages
14 мар 19, 20:03    [21832999]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
dundin
можно не палить из пушки по воробьям


Можно, но иногда "не палить из пушки" означает "изобретать велосипеды", в частности долбиться о пробелмы и ошибки, об которые "в пушках" уже обдолбились и давно поправили

MySQL возможно тяжеловатый сервер. Тот же Firebird легче.
SQLite ещё легче, если в нём возможно один файл читать из разных процессов.

А главное, тебе надо решить что ты делаешь, push или pull ?


dundin
каждый .... может в любой момент получить общие данные


Это - Pull. Никто никому никаких данных не приносит. Наоборот, кому поднадобилось - тот и ищёт, когда понадобилось.


dundin
Этими уже обработанными данными нужно поделиться с другими экземплярами.


А это - Push. Если я что-то приготовил, я обхожу каждого и ему это всовываю. Даже если тебе это нафиг не надо и никогда не понадобится - мне про это неизвестно, я всё равно тебя найду и засуну тебе это в зубу и заставлю проглотить - просто на всякий случай.

Иногда лучше первый подход, иногда - второй.

У второго подхода есть варианты, когда в зубы всовывают не всё подряд, а тоьлко подходящее под некоторые условия (аналогично select с where и без).
Это называют pubsub - publish/subscribe
Это называют message bus или data bus (например D-bus на Линуксе)
Это называют "брокер сообщений" - Message Queue. Например, в самой Windows часть Distributed-COM

https://en.wikipedia.org/wiki/Microsoft_Message_Queuing
https://www.osp.ru/winitpro/2002/08/175133/

В частности cache coherence обычно объединяет оба подхода но для разных данных. Сообщения "я тут что-то поменял, если у тебя есть копия - выкинь её немедленно" идёт как push. А вот реальный пакет с данными, если понадобится, как pull.

В общем, палитра у тебя примерно такая, краску смешивай исходя из твоей реальной задачи и реальных в ней потоков данных.
14 мар 19, 20:04    [21833001]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1361
+ WM_COPYDATA
каждое броадкастом уведомляет всех: Вот он я, запишите мой хендл. либо уведомляет я закрываюсь не шлите мне.
А через WM_COPYDATA уже передаете данные
14 мар 19, 20:05    [21833003]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
dundin
Но цель переделок - избавиться от централизации в виде MySQL сервера и вообще от каких либо сторонних программ.


Супер!
Выкинь нафиг Delphi RTl а также Delphi VCL и Delphi FMX - они ведь тоже "сторонние программы".
Напиши всё сам.
Это возможно.
14 мар 19, 20:07    [21833007]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
X-Cite
либо уведомляет я закрываюсь не шлите мне


...либо жёстко крэшится посреди транзакции и никого ни о чём не уведомляет. А респондент остаётся висеть в середине обмена, ожидая сообщения, которое уже никогда не придёт...

Лисапеты - они такие.
14 мар 19, 20:08    [21833009]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1361
Arioch
X-Cite
либо уведомляет я закрываюсь не шлите мне


...либо жёстко крэшится посреди транзакции и никого ни о чём не уведомляет. А респондент остаётся висеть в середине обмена, ожидая сообщения, которое уже никогда не придёт...

Лисапеты - они такие.

Если consumer покрешился, то producer будет слать в невалидный хендл..
Из условия задачи сказано надо делится данными.. ну поделится в пустоту ничего страшного... суть обмена как я понял. отдал и забыл
14 мар 19, 20:12    [21833013]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
dundin
избавиться от централизации в виде MySQL сервера


Кстати, как ты отлаживать-то будешь ?

Вот допустим у тебя, или у клиетна, "что-то пошло не так".
Допустим, ты даже смог это воспроизвести.

С SQL-сервером ты можешь заглянуть в процессе отладки в БД и посмотреть, что там реально где происходит в любйо момент работы программы.

У разных MSMQ и RabbitMQ тоже наверное свои инструменты есть, но они менее известные и менее тебе привычнее.

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

Централизацию не на пустом месте придумали. Децентрализация возможна, но она не бесплатна.

Ту же очередь "N producer M consumer" можно внутри Firebird БД держать.

Опять же, как часто ты что делаешь.
Если у тебя 100 сообщений в секунду, каждое по 20 КБ - то SQL-сервер прекрасно подойдёт. Закеширует "горячую" часть БД в память и будет молотить.
Если у тебя 100 000 сообщений в секунду каждое по 20 байт - то сервер ляжет, накладных расходов будет в разы больше, чем данных. Впрочем, тут и WM_COPYDATA ляжет наверное.
Если у тебя одно сообщение в час, но оно сразу по гигабайту - WM_COPYDATA сразу пролетает, SQL будет скрипеть и плакать, тут лучше файлами обмениваться.

В общем, какие у тебя данные - от этого и танцуй
14 мар 19, 20:16    [21833015]     Ответить | Цитировать Сообщить модератору
 Re: Сохранение типизированной переменной в Memory Mapped File  [new]
Arioch
Member

Откуда:
Сообщений: 10807
X-Cite
то producer будет слать в невалидный хендл.


...пока его кто-то другой себе не возьмёт.

но хуже не это, хуже зависание пира в середине протокола обмена.
14 мар 19, 20:18    [21833016]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить