Virtual Laboratory Wiki
Advertisement

Архитектура сетевой объектно-ориентированной базы данных/знаний Cerebrum

Шуклин Д.Е. http://www.shuklin.com/ai/ht/ru/cerebrum

Назначение СООБЗ

Основная цель данной разработки - создать виртуальную машину, поддерживающую нейронные сети со свободной топологией и числом нейронов до 2 млрд в одном хранилище. Данная возможность обеспечивается путем реализации системы управления сетевой объектно-ориентированной базы знаний (СООБЗ) в которой только часть объектов находится в данный момент в оперативной памяти. Большая часть объектов заморожена в файловом хранилище. Учитывая, что в случае применения реляционной модели данных моделирование нейронной сети потребует выполнения запросов с большим количеством соединений и дальнейшего отображения табличных структур на сеть объектов, применение систем управления реляционными базами данных для решения данной задачи не оправдано. В некоторых случаях, применение ООБД может быть более целесообразно, по сравнению с РБД. Это связанно с тем, что модель реального мира представлена в виде отношений между понятиями. В случае реализации информационных систем ориентированных на сложную бизнес ситуацию возникают значительные трудности при сопровождении изменений реляционной модели описывающей отношения между бизнес объектами.

Система управления сетевой объектно-ориентированной базой знаний Cerebrum [1] обладает следующими возможностями:

  • Сохранять текущее состояние графа объектов или нейронной сети в СООБЗ между сеансами работы с пользователем. В том числе сохраняется текущая топология сети объектов. При повторном запуске приложения не понадобится создавать сеть объектов заново.
  • При большем количестве экземпляров объектов ограничить объем памяти, используемый графом объектов или нейронной сетью. Наиболее часто используемые объекты остаются в оперативной памяти, остальные вытесняются в файловое хранилище и загружаются в оперативную память по мере необходимости. При загрузке экземпляра в оперативную память он вытесняет другие, редко используемые объекты.

Ограничение объема памяти позволяет избавиться от использования файла подкачки операционной системы, что значительно повышает производительность моделирования сетей с большим количеством экземпляров объектов (при суммарном размере всех экземпляров большем, чем размер текущей свободной памяти в системе) В случае, если объем сети объектов меньше чем размер текущей свободной памяти в системе, вся сеть находится в оперативной памяти и потерь производительности, связанных с сериализацией-десериализацией .не возникает.

  • Применение СООБЗ не накладывает никаких ограничений на используемую бизнес логику или математическую модель нейрона, которую можно реализовать как методы объектов, находящихся в СООБЗ. Основное требование - организовать связи между объектами в сети не с помощью указателей, а с помощью ID объектов. При этом будет необходимо получать указатель на объект используя API СООБЗ.

Архитектура СООБЗ

Общая архитектура текущей версии СООБЗ Cerebrum представляет собой однопользовательскую файловую (desktop) базу. Поддерживаются однопользовательские одноуровневые транзакции. Во время транзакции файловое хранилище отключается, и все изменения объектов накапливаются в оперативной памяти. В будущем предполагается реализации поддержки иерархических транзакций, блокировок объектов, управление версиями (versioning) объектов, конкурентного доступа, многопользовательского режима и клиент-серверной архитектуры. Архитектура СООБЗ представлена на рисунке 1.

Файл:Cerebrum.Samples.Streams-01.image001.PNG

Рис. 1. Архитектура СООБЗ

Объекты могут содержать методы. Имеется возможность реализовать методы managed объектов на MC++, VB и C#, и методы unmanaged объектов на C & C++. Для managed объектов рекомендуется применение C# а для unmanaged – C. Методы объектов выполняются на стороне сервера. Однако отсутствие на данный момент клиент-серверной архитектуры приводит к отсутствию практического отличия client-side от server-side.

При реализации методов объектов необходимо учитывать ограничения. Необходимо следить за блокировкой и деблокировкой объектов в памяти. Если этого не делать могут возникнуть неприятности, от потери производительности вплоть до аварии - это зависит от конкретного сценария в котором не правильно была применена блокировка. Для поиска таких ошибок в Cerebrum предоставляется специальная отладочная версия ядра.

Существуют ограничения в поддержке механизма делегатов и следовательно, событий объектов. Делегаты не обрабатываются ядром Cerebrum, следовательно нельзя устанавливать связь между двумя объектами, управляемыми ядром Cerebrum. Это связанно с ограничениями по времени жизни экземпляров. Во время, когда система остановлена, все объекты хранятся в замороженном виде. Если время жизни у приемников или источников сообщений совпадает, или может быть изолированно от времени жизни объектов управляемых системой, то механизмы делегатов - сообщений работает в пределах подсети объектов развернутых в памяти .NET Framework.

Существует возможность проводить отбор хранимых объектов на основе результатов вызовов прикрепленных к ним методов. Эта возможность может быть реализована путем итерации по этим объектам. Учитывая возможность объекта реализовать любой абстрактный интерфейс, возможно использовать виртуальные методы в процессе такого отбора. Другими словами, в Cerebrum можно воспользоваться полиморфизмом, каждый объект имеет право реализовать свою собственную версию метода, на основе выполнения которого произойдет фильтрация объектов.

Интересно отметить, что нейронная сеть обычно характеризуется высоко параллельным режимом работы. Все нейроны работают параллельно и условно независимо друг от друга. Таким образом, в БЗ предусмотрена эмуляция количества threads по количеству нейронов в слое. Предполагается наличие миллионов threads одновременно. Это является важной характеристикой для задачи эмуляции ИНС на последовательной вычислительной машине.

СООБЗ Cerebrum не содержит метаданных классов. Cerebrum использует встроенный в .NET механизм отражения (reflection). Для того чтобы создать новый экземпляр объекта система должна иметь доступ к сборке (assembly) в которой находится реализация класса создаваемого объекта. Это означает, что требуется скопировать сборку на машину, на которой запущено ядро Cerebrum.

Ядро системы включает в себя уровень взаимодействия с операционной системой и уровень хранения и управления объектами. Уровень взаимодействия с операционной системой должен обеспечить адаптацию функций операционной системы к нуждам виртуальной машины, реализующей нейронную сеть. Уровень хранения объектов должен обеспечить хранение и быстрый доступ к объектам и их связям, а также управление временем жизни объектов. Совокупность API всех уровней виртуальной машины, для определенности, названа Virtual Neural Programming Interface, далее VNPI. Структура ядра VNPI приведена на рисунке 2.

Рисунок 2. Структура ядра VNPI

СУСООБЗ Cerebrum это многоуровневая система, состоящая из нескольких сборок (assembly). Модули составляют иерархию, так что более высокий уровень использует в своей работе сборки более низкого уровня. Это позволяет подключить к проекту и использовать несколько сборок Cerebrum оптимально соответствующих решению поставленной задачи. Cerebrum.Runtime.dll - это ядро системы. В нем расположены самые низкоуровневые операции. Его можно рассматривать как усовершенствованный менеджер памяти и файловой системы. Например, в нем находится сборщик мусора, управляющий persistent объектами. На основе этого компонента можно реализовать различные архитектуры баз данных и знаний, однако, так как функции, реализованные в нем, достаточно низкого уровня и не определяют архитектуру приложения, использовать этот модуль независимо от остальных будет неудобно - потребуется продублировать код уже доступный в других модулях. Эту сборку удобно использовать в комплекте с несколькими вышестоящими модулями. Cerebrum.Integrator.dll - это реализация БД конфигурации. Ядро требует метаинформацию о классах .NET управляемых системой. Здесь находятся реализации дескрипторов метаинформации, например AttributeDescriptor, TableDescriptor, TypeDescriptor и.т.п. Эта сборка не накладывает ограничений на остальную архитектуру программы. Если необходимо использовать Cerebrum в web сайте через ASP.NET то следует ограничиться использованием этого модуля, так как архитектура Windows.Forms приложения отличается от Web.

Cerebrum.Windows.Forms.dll - это ядро приложения для Windows.Forms. Если предполагается использовать Cerebrum в своем приложении, то потребуется включить в проект эту и 2 ранее перечисленные DLL . Cerebrum.DesktopClient.exe - это законченное приложение для просмотра и изменения БД конфигурации. Так же оно разработано с учетом поддержки add-ins. Существует возможность разработать адд-инс и включить его как составную часть Cerebrum.DesktopClient.exe. Выбор между включением Cerebrum в независимое приложение и включением приложения в Cerebrum зависит от целей разработки.

Сборки Cerebrum.Runtime.dll и Cerebrum.Integrator.dll не содержат реализацию пользовательских объектов. В Cerebrum.Runtime.dll реализована система управления экземплярами объектов, а Cerebrum.Integrator.dll управляет метаинформацией о классах пользовательских объектов.

Когда пользователю требуется разработать собственное приложение, например модель нейронной сети, то следует воспользоваться этими сборками. Следует определить классы для своих нейронов и зарегистрировать описатели этих классов в базе данных конфигурации. Так же требуется обеспечить доступность сборок с реализацией пользовательских классов из ядра Cerebrum. Проще всего это сделать, разместив пользовательские сборки в одной папке с Cerebrum.Runtime.dll. Cerebrum поддерживает реализацию пользовательских классов не только с данными, но и с поведением. Допускается наследование пользовательских классов. Поддерживаются виртуальные методы и множественное наследование интерфейсов. Благодаря этому разработчик может реализовать поведение нейрона как методы своего класса. Разработчик не ограничен использованием какой то одной модели нейрона – разработчик может сам реализовать любую модель, а Cerebrum возьмет на себя функции управления экземплярами объектов. Удобно провести соответствие между нейроном и объектом базы. Cerebrum возможно использовать для решения задач, требующих синтез структуры НС на основе задания. Синтез более похож на обычное программирование, однако в результате проектирования и синтеза сети является нейронная сеть, а не программа. В Cerebrum встроена поддержка коллекций указателей на экземпляры объектов, что позволяет реализовать связи с другими нейронами как коллекции указателей на эти нейроны. На данный момент в одной БД может находиться до 2 млрд. экземпляров.

Идентификация экземпляров объектов

В СООБЗ Cerebrum применяются "мягкие" указатели. Идентификатор объекта можно рассматривать как указатель на объект в памяти системы. Однако от запуска к запуску указатель в памяти на объект, очевидно, не будет постоянен. Для того чтобы избежать необходимости сканирования объектов в БД систему реализуют так, чтобы идентификатор объекта не зависел от времени. Это можно сделать по-разному, через индексы, двойную разадресацию ... Этот момент весьма сильно критикуется сторонниками реляционных баз данных (РБД) т.к. сильно сближает РБД и ООБД снижая скоростные преимущества ООБД при навигации: и там и там доступ к объекту осуществляется через дополнительную операцию - обычно и там и там - поиск по индексу. В Cerebrum применяются мягкие идентификаторы объектов. В одном контексте адресации одному идентификатору всегда соответствует один и тот же объект. Тип данных для идентификатора объекта - Cerebrum.ObjectHandle. Размер идентификатора объекта - 32 бита. Поиск объекта в индексе по его идентификатору происходит в три этапа. 32 бита разбиваются на 12, 12 и 8 бит. По этим значением строится 3х уровневое дерево индексов. Коллекции объектов организованы на основании того же механизма. Коллекция представляет собой такой же индекс 12-12-8, который производит отображение одного идентификатора на другой идентификатор. Для доступа к объекту таблицы первым этапом производится восстановление идентификатора объекта по его индексу в коллекции (это 3 операции поиска в дереве). Вторым этапом производится восстановление указателя на объект по его идентификатору (такие же 3 операции по аналогичному, но другому дереву). В итоге, для доступа к строке таблицы надо выполнить 6 операций поиска в индексе, не зависимо от количества объектов в базе.

Важной особенностью СООБЗ Cerebrum является отличие в адресации объектов по сравнению с манифестом объектных баз данных ODMG [2]. В Cerebrum каждый объект, так же как и в ODMG адресуется с использованием мягкого указателя или ID объекта Cerebrum.ObjectHandle . Получение указателей на экземпляры объектов внешние по отношению к некоторому текущему объекту производится в пределах контекста текущего объекта. Объект может адресовать только те объекты, с которыми он установил связь. Каждый, связанный с текущим, объект имеет идентификатор. Каждый уникальный идентификатор определяет в пределах текущего объекта экземпляр связанного с ним объекта. В пределах текущего объекта каждый идентификатор может адресовать только один экземпляр. Однако важным отличием от ODMG является возможность иметь в пределах некоторого объекта несколько различных Cerebrum.ObjectHandle адресующих один и тот же экземпляр. Тоесть в пределах некоторого заданного объекта несколько разных идентификаторов могут ссылаться на один и тот же экземпляр объекта. В отличие от ODMG, где каждый объект имеет только один уникальный идентификатор в Cerebrum один и тот же объект может иметь различные идентификаторы. Если рассмотреть не один экземпляр, а всю базу, то по аналогии с явлениями естественного языка синонимии и омонимии в СООБЗ Cerebrum возникает явления синонимии и омонимии идентификаторов объектов. Так в различных контекстах определяемых различными экземплярами объектов один и тот же идентификатор объекта Cerebrum.ObjectHandle может адресовать как разные экземпляры, так и один и тот же экземпляр. Это явление можно назвать омонимией объектных идентификаторов. Так же как и в естественном языке при омонимии один и тот же идентификатор в разных контекстах одной и той же базы данных может адресовать как различные, так и одинаковые экземпляры объектов. Так же как в одном и том же контексте, так и в разных контекстах различные идентификаторы могут ссылаться на один и тот же экземпляр объекта. В этом случае возникает явление синонимии объектных идентификаторов, когда один экземпляр может иметь в пределах базы множество различных идентификаторов - синонимов. Эту возможность удобно использовать для построения семантических и иерархических семантических сетей.

Сборка мусора

В настоящее время в СУСООБЗ Cerebrum работают 3 подсистемы сборки мусора. Первые две подсистемы обеспечивают сборку объектов в оперативной памяти, третья подсистема в хранилище. Учитывая различные цели и требования стоящие перед .NET Framework и Cerebrum идеология системы сборки мусора в оперативной памяти кардинально отличается. Так в .NET уничтожение объекта возможно только после того как не останется возможности его использования в текущем процессе. В отличие от этого в ядре Cerebrum сборка мусора в оперативной памяти осуществляется, когда исчерпана память допустимая для размещения объектов. В этом случае ненужные объекты вытесняются на устройство долговременного хранения. Первая подсистема сборки мусора встроена в Microsoft .NET Framework. Она используется в Cerebrum для сборки non-persistent пользовательских объектов. Для persistent объектов эта подсистема блокируется путем установки на такой объект ссылки из ядра ООБД. Вторая и третья подсистемы реализованы в ядре ООБД. Вторая подсистема сборки мусора служит для сохранения не используемых на данный момент экземпляров пользовательских persistent объектов. Эта подсистема работает на основе Win32 API размещения блоков памяти. Она отслеживает инстанциированные объекты и количество их блокировок. При блокировке/разблокировке обновляется список last recently used объектов. При превышении заданного количества размещенных блоков или максимального размера выделенной памяти эта подсистема пытается выгрузить в дисковое хранилище незаблокированные объекты. В первую очередь производится попытка выгрузить объекты, попавшие в хвост списка наиболее часто используемых объектов. Эта система размещения работает только для объектов ядра, сопровождающих пользовательские экземпляры. Инициировать сохранение пользовательских объектов в ручную не нужно. Остальные служебные объекты размещаются дополнительными подсистемами выделения памяти. Наименее часто используемые объекты автоматически вытесняются на систему долговременного хранения, освобождая оперативную память. Поэтому в любой момент возможно принудительное вытеснение и разрушение любого незаблокированного пользовательского объекта. Если пользователь получил указатель на некоторый объект и не провел операцию блокировки, то сборщик мусора Cerebrum может посчитать этот объект неиспользуемым и принудительно вытеснить его из оперативной памяти. В результате по этому указателю в памяти будет находиться разрушенный экземпляр объекта. Для предотвращения такого явления вместо указателей на экземпляры пользовательских объектов при разадресации идентификатора возвращается указатель на объект-оболочку IConnector. В конструкторе объекта-оболочки производится счетчика блокировок объекта, в деструкторе - уменьшение счетчика блокировок. Пока хоть одна оболочка находится в памяти, счетчик блокировок не равен 0 и соответствующий этой оболочке объект защищен от разрушения. Это позволяет предотвратить вытеснение пользовательского экземпляра во время его использования. У этой оболочки есть свойство Component, через которое доступен пользовательский экземпляр. Использовать указатели на пользовательский экземпляр без сохранения указателя на Connector нельзя. Важной особенностью данной системы является необходимость вызова метода Dispose у всех объектов-оболочек для предотвращения чрезмерного расхода памяти. В большинстве случаев паттерн работы с объектом выглядит следующим образом:

using(IComposite composite = this.m_Connector.Workspace.AttachConnector(h))
{
	ISomeInterface node = composite.Component as ISomeInterface;
	result = node.SomeMethod(connector, );
	
}

При разадресации по идентификатору h объекта в методе this.m_Connector.Workspace.AttachConnector возвращается его оболочка IConnector. После завершения работы с объектом эту оболочку необходимо разрушить, вызвав метод Dispose. Директивы языка C# using() нужны, чтобы гарантировать удержание объекта в памяти во время вызова его методов, а затем гарантировать вызов Dispose после окончания работы с объектом. По выходу из using автоматически вызывается Dispose и разрушает объект-оболочку. При разрушении объект-оболочка уменьшит счетчик блокировок. Когда все оболочки будут разрушены и счетчик блокировок достигнет 0, то экземпляр пользовательского объекта станет доступным для вытеснения из оперативной памяти. Если же по каким то причинам using не применим, и пользователь забыл или не смог провести вызов Dispose то сам .NET Framework вызовет Dispose в процессе сборки мусора. В результате утечки памяти не возникнет даже при наличии ошибок программиста.

Третья подсистема сборки мусора рассчитана на удаление persistent объектов из хранилища. Работа этой подсистемы основана на счетчике ссылок. В каждом persistent объекте есть счетчик входящих указателей. Когда на объект не указывает ни одного persistent указателя объект удаляется из системы по завершению транзакции или при сборке мусора, если он вне транзакции. При изменении объекта в пределах транзакции на этот объект создается указатель из объекта текущей транзакции. Пока на объект существует хоть одна ссылка, он будет существовать в хранилище. Установка дополнительной ссылки на объект из объекта транзакции позволяет задержать физическое удаление объекта до окончания транзакции и при необходимости позволить отменить это удаление. Если все пользовательские ссылки с объекта были сняты, и в конце транзакции с объекта была снята ссылка из объекта транзакции, то объект помечается специальным флагом. Этот флаг действует в пределах оперативной памяти. Выгрузка объекта из оперативной памяти осуществляется, если у объекта не осталось блокировок. При выгрузке объекта из оперативной памяти на основе этого флага принимается решение, будет ли объект уничтожен окончательно. Это позволяет сначала удалить все ссылки на данный экземпляр объекта в хранилище, оставив его в оперативной памяти. Затем, воспользовавшись указателем на объект в оперативной памяти установить с этим объектом ссылку их другого объекта не допустив его физического удаления. В связи с наличием в Cerebrum только операций установки / снятия ссылок на объект и отсутствием принудительного удаления объектов, логических парадоксов не возникает.

Сборка persistent мусора на основе подсчета ссылок имеет недостаток: невозможность детектировать недостижимые из корня базы данных циклы объектов. Таким неодостаком так же обладают другие, успешно эксплуатирующиеся системы, со сборкой мусора, основанной на подсчете ссылок, например Microsoft COM. Разработка в таком случае требует особой аккуратности при удалении ссылок на объекты. Программист обязан недопускать циклических ссылок недостижимых из корня базы данных. В перспективе в СООБЗ Cerebrum возможно сделать четвертую подсистему сборки мусора, которая будет работать на основе достижимости объекта из корня базы данных. В связи с очень большими вычислительными затратами на такой механизм сборки мусора его следует запускаться во время "сна" или бездействия системы.

Транзакции

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

  • В случае если TransactionCount = 0 транзакция находится в чистом состоянии.
  • В случае начала чистой транзакции по BeginTransaction транзакция остается в чистом состоянии. счетчик TransactionCount увеличивается на 1
  • В случае завершения чистой транзакции по CommitTransaction транзакция остается в чистом состоянии, счетчик TransactionCount уменьшается на 1
  • В случае завершения чистой транзакции по RollbackTransaction транзакция переходит в грязное состояние, счетчик TransactionCount уменьшается на 1 Если счетчик TransactionCount равен 0 по завершении транзакции, то она становится чистой.
  • В случае завершения грязной транзакции по RollbackTransaction транзакция остается в грязном состоянии, счетчик TransactionCount уменьшается на 1. Если счетчик TransactionCount равен 0 по завершении транзакции, то она становится чистой.
  • В случае начала грязной транзакции по BeginTransaction транзакция остается в грязном состоянии. счетчик TransactionCount не изменяется. Транзакция не начинается. BeginTransaction вызывает исключительную ситуацию (Exception)
  • В случае завершения грязной транзакции по CommitTransaction транзакция остается в грязном состоянии, счетчик TransactionCount уменьшается на 1. Если счетчик TransactionCount равен 0 по завершении транзакции, то она становится чистой. CommitTransaction вызывает исключительную ситуацию (Exception)
  • В случае завершения любой транзакции по RollbackTransactionAll все изменения аннулируются, TransactionCount сбрасывается в 0. Текущее состояние транзакции становится чистым.

Объектная модель и API

С точки зрения разработчика Cerebrum представляет собой совокупность объектов различного назначения. В одном процессе возможно существование нескольких открытых баз данных. Каждая база данных доступна разработчику в виде объекта реализующего интерфейс IWorkspace. Экземпляр, реализующий этот интерфейс можно считать корнем базы данных.

public interface INativeDomain : System.IDisposable
{
	IActivator Activator { get; }

	IContainerEx GetSector();
}

public interface IWorkspace : INativeDomain, IContainer
{
	IActivator Activator { set; }

	void BeginTransaction();
	void CommitTransaction();
	Cerebrum.ObjectHandle CurrSequence();
	Cerebrum.ObjectHandle NextSequence();
	void RollbackTransaction();
	void RollbackTransaction(bool all);
}

где Activator - свойство, позволяющее установить фабрику пользовательских объектов;

GetSector- метод, возвращающий оболочку корневого индекса пользовательских экземпляров;

NextSequence- метод, возвращающий следующий уникальный идентификатор объекта;

CurrSequence- метод, возвращающий последний сгенерированный идентификатор.

Этот объект так же обладает контекстом позволяющим проводить разадресацию идентификаторов в указатели на объекты. Разадресация производится с использованием интерфейса IContainer являющегося базовым для интерфейса IWorkspace. IWorkspace так же является оболочкой к объекту DomainContext представляющему базу данных на уровне Cerebrum.Integrator.

Интерфейс IContainer содержит следующие методы:

public interface IContainer : IDisposable
{
	IComposite AttachConnector(Cerebrum.ObjectHandle plasma);
	IComposite CreateConnector(Cerebrum.ObjectHandle plasma, Cerebrum.ObjectHandle typeID);
	void EngageConnector(Cerebrum.ObjectHandle plasma, IConnector connector);
	void RemoveConnector(Cerebrum.ObjectHandle plasma);
}

где AttachConnector - метод возвращающий объект-оболочку пользовательского объекта по идентификатору этого объекта;

CreateConnector - метод, создающий экземпляр пользовательского объекта и возвращающий его оболочку;

RemoveConnector - метод, удаляющий экземпляр пользовательского объекта из текущего контейнера. В случае удаления пользовательского объекта из всех контейнеров, объект полностью удаляется из базы данных;

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

При использовании AttachConnector или CreateConnector возвращается оболочка экземпляра IComposite. Интерфейс IComposite наследует у интерфейса IConnector. IConnector имеет следующие свойства:

public interface IConnector : IDisposable
{
	object Component { get; set; }
	IWorkspace Workspace { get; }
}

где

Component - свойство, возвращающее экземпляр пользовательского объекта;

Workspace - свойство возвращающее корневой интерфейс базы данных.

IComposite имеет следующие свойства:

public interface IComposite : IConnector
{
	Cerebrum.ObjectHandle GroundHandle { get; }
	Cerebrum.ObjectHandle LiquidHandle { get; }
	Cerebrum.ObjectHandle PlasmaHandle { get; }
	Cerebrum.ObjectHandle TypeIdHandle { get; }

	IConnector GetPrecedent();
}

где PlasmaHandle - свойство, возвращающее логический идентификатор экземпляра;

TypeIdHandle - свойство, возвращающее идентификатор типа экземпляра в таблице Types.

GetPrecedent - свойство возвращающее объект-оболочку родительского объекта;

GroundHandle - свойство, возвращающее идентификатор объекта в хранилище;

LiquidHandle - свойство, возвращающее идентификатор объекта в кэш.

Контекст экземпляра определяется объектом оболочкой IConnector. Если данный экземпляр имеет возможность устанавливать связи с другими объектами, то объект оболочку IConnector возможно привести к типу IContainer и использовать его для разадресации некоторого идентификатора Cerebrum.ObjectHandle вместо IWorkspace. Модель данных в СООБЗ Cerebrum представляет собой граф с раскрашенными ребрами, узлы графа являются объектами. Объект так же может представлять собой граф с раскрашенными ребрами, в узлах которого находятся другие объекты. Каждое ребро имеет только один "цвет". Цвет не является объектом, цвет имеет тот же тип что и идентификатор объекта (Cerebrum.Runtime.Cerebrum.ObjectHandle). Цвет - это тип отношения. Если не обходимо сопоставить цвет ребра, с каким либо объектом, нужно создать некоторый объект и взять его Cerebrum.ObjectHandle в качестве “цвета” для раскраски ребра. Таким образом, можно раскрасить ребра в "цвета" экземпляров объектов. Или другими словами иметь объекты, соответствующие цветам ребер. Если нужно имитировать раскраску ребра множеством цветов, то надо делать несколько ребер каждое разного "цвета".

В пределах некоторого узла возможно создание дочерних объектов, адресуемых в пределах этого узла некоторыми уникальными Cerebrum.ObjectHandle. Эти идентификаторы могут быть одинаковыми для разных дочерних объектов в пределах разных родительских объектов. Это позволяет устанавливать реляционную связь (по значению) между дочерними объектами разного уровня разных родительских объектов.

Узел в Cerebrum является агрегатом нескольких объектов. Во первых это объект ядра Kernel Object. Тип объекта ядра KernelObjectClass определяет роль данного узла в модели данных. Kernel Object представляет собой Native VNPI Object реализованный на языке C. У прикладного разработчика непосредственный доступ к этому объекту отсутствует. Работа с Kernel Object осуществляется посредством объекта-оболочки. Тип объекта-оболочки, возвращаемого при разадресации зависит от типа объекта ядра. Непосредсвенно в Cerebrum.Runtime.dll для использования доступны 3 различные типа объектов ядра. Это Scalar, Stream и Warden. Scalar объекты служат для создания узлов, хранящих скалярные значения, например System.Int32 или System.String. Такие узлы не могут вступать в связь с другими узлами в качестве родительских объектов. Или что то же самое такие узлы не могут являться источниками связей. Узлы типа Scalar могут выступать только в качестве дочерних объектов. Узлы типа Stream похожи на узлы Scalar, но в место скалярных значений они хранят потоки байт по аналогии с полем BLOB в РСУБД.

Узлы типа Warden могут быть связанны с другими узлами с помощью однонаправленных связей. Каждой связи должен быть назначен идентификатор. Из узла источника связи возможно обнаружить связанный с ним экземпляр. Однако из узла, на который указывает связь, невозможно определить ни узлы, их которых исходят связи, ни сам факт наличия связи. Можно сказать, что узел источник связи является родительским объектом, а узел приемник связи – дочерним. При этом нельзя для некоторого конкретного объекта определить родительский объект. Таких родительских узлов у каждого экземпляра может быть несколько. Для совместимости с паттерном flyweight в узлах в Cerebrum не храниться ссылки на родительские узлы, и также невозможно определить идентификатор текущего объекта из внутреннего контекста этого объекта.

Вторым объектом в агрегате является экземпляр реализующий интерфейс IConnector. Экземпляр этого объекта создается и предоставляется СООБЗ. Этот экземпляр передается в пользовательский объект при его инициализации. Через свойство IConnector.Workspace пользовательский объект имеет доступ к экземпляру, реализующему интерфейс IWorkspace. Третьим объектом в агрегате является экземпляр пользовательского persistent объекта. Особые ограничения на этот объект не накладываются. Он может быть экземпляром любого класса среды .NET Framework унаследованного от System.Object. В случае если пользовательский объект является reference type то будет рационально реализовать в этом объекте интерфейсы IComponent и при необходимости ISerializable. Рекомендуется наследовать пользовательские reference type объекты от Cerebrum.Integrator.GenericComponent. Если родительский объект получил по идентификатору адрес объекта оболочки дочернего объекта, то экземпляр дочернего пользовательского объекта доступен через свойство IConnector.Component.

Для того чтобы получить указатель на экземпляр пользовательского объекта необходимо вызвать метод AttachConnector в контексте родительского объекта, передав в качестве параметра идентификатор экземпляра, адресующий дочерний объект в пределах родительского контекста. В результате вызова данного метода система вернет новый экземпляр объекта-оболочки, реализующей интерфейсы IConnector и IComposite. Тип объекта оболочки зависит от типа Kernel Object находящегося в данном узле. Объект-оболочка также предоставляет прикладному программисту интерфейсы к функциональности Kernel Object. В случае если в качестве объекта ядра был задан Warden объект оболочка дополнительно реализует и интерфейс IContainer.

В сети нет четкого понятия иерархии. В БД любой объект может выступать как родительский и как дочерний, мало того, объекты могут быть своими собственными родительскими и дочерними объектами. Так же разрешается иметь циклы когда объект А является родительским у объекта B и объект B является родительским у объекта A. Это приводит к затруднению в определении «начала» БД с которой необходимо начать работу непосредственно после запуска системы. Для этого в БД реализован специальный корневой экземпляр объекта - Sector. Sector представляет собой отдельный экземпляр объекта ядра Warden. При необходимости указатель на его оболочку можно получить как IWorkspace.GetSector() однако для удобства и повышения эффективности его интерфейс IContainer совмещен с интерфейсом IWorkspace и доступен через IWorkspace напрямую.

Удобно, когда дочерними узлами объекта Sector является большинство экземпляров пользовательских объектов имеющие объекты ядра Warden. Их дочерними объектами являются скалярные значения, имеющие объекты ядра Scalar. В результате объекты ядра образуют трехуровневое дерево. На первом уровне этого дерева находится объект ядра Sector. На втором уровне находятся экземпляры пользовательских объектов, каждый узел имеет дочерние узлы. Экземпляры пользовательских объектов, находящиеся на втором уровне удобно адресовать уникальными в пределах всей базы данных идентификаторами, аналогично предложению ODMG. Для обеспечения этой возможности объект Sector предоставляет sequence для получения уникальных значений идентификаторов объектов как IWorkspace.NextSequence(). На третьем уровне находятся скалярные значения, представляющие собой атрибуты пользовательских объектов. Дочерние узлы в пределах узла пользовательского экземпляра адресуются с помощью идентификаторов Cerebrum.ObjectHandle. Если пользовательские объекты имеют уникальные идентификаторы в пределах объекта Sector то их атрибуты имеют уникальные идентификаторы только в пределах одного экземпляра. Атрибуты различных пользовательских объектов, обладающие одинаковой семантикой имеют одинаковые идентификаторы. Можно провести аналогию с XML в котором у каждого узла есть дочерние узлы, имеющие некоторое имя. Тоесть именем объекта в пределах некоторого узла выступает идентификатор этого объекта. Создание persistent экземпляра пользовательского класса может быть осуществлено различными способами. Штатным способом является следующий: Необходимо зарегистрировать в таблице Tables тип пользовательского класса. Атрибут QualifiedTypeName должен содержать полное .NET имя пользовательского класса. Атрибут KernelObjectClass содержит тип объекта ядра. Для Scalar = 8, для Warden = 9. Затем следует определить идентификатор создаваемого объекта. Уникальный в пределах текущей базы данных идентификатор можно получить воспользовавшись функцией NextSequence. Cerebrum.Runtime.Cerebrum.ObjectHandle h = this.m_Connector.Workspace.NextSequence();

Затем следует воспользоваться функцией CreateConnector для получения указателя на объект-оболочку созданного экземпляра. Передав ей в качестве параметров идентификатор создаваемого экземпляра h и идентификатор дескриптора пользовательского типа typeId из таблицы Tables. Указатель на созданный экземпляр доступен через свойство Component объекта-оболочки.

using(IComposite composite = this.m_Connector.Workspace.CreateConnector(h, typeId))
{
	ISomeInterface node = composite.Component as ISomeInterface;
	result = node.SomeMethod(connector, );
	
}

Пользовательские классы удобно наследовать от класса GenericComponent. У каждого GenericComponent есть свойство DomainContext. Это свойство возвращает экземпляр класса DomainContext представляющий базу данных модуля Cerebrum.Integrator Метод GetChildComponents доступен изнутри пользовательского объекта и возвращает экземпляр класса, реализующий интерфейс IContainer. Это позволяет проводить разадресацию указателей на дочерние объекты изнутри пользовательского экземпляра не имея доступа к объекту оболочке возвращаемому AttachConnector/CreateConnector.

База данных конфигурации

База данных конфигурации обеспечивает хранение метаинформации о зарегистрированных в системе пользовательских классах. Она обеспечивает создание экземпляров пользовательских классов. Так же она может быть использована для управления атрибутами объектов. Каждый объект сетевой объектно-ориентированной базы может иметь несколько атрибутов. В качестве значений атрибутов могут выступать другие объекты или простейшие (скалярные) типы данных. Однотипные экземпляры объектов (с совместимыми наборами атрибутов) удобно представлять таблицами и управлять ими с помощью интерфейса пользователя ориентированного на обработку таблиц. Однотипные объекты имеют одинаковый набор атрибутов. Поэтому возможно представить коллекции однотипных объектов в виде таблицы, в которой сроки являются объектами, а колонки - атрибутами этих объектов.

Для создания объектов необходимо иметь информацию об их типах. Средствами сетевой объектно-ориентированной базы можно реализовать табличную структуру, близкую к структуре реляционной базы данных. Информация о типах объектов зарегистрированных в системе организована в виде в виде связанных друг с другом таблиц Types, Attributes, Tables. Это позволяет определять в БД типы объектов .NET в унифицированном виде. Однако, свойства таблиц, строк и колонок в ОО базе отличаются от свойств, принятых в реляционных базах данных. Объектно-ориентированная база позволяет хранить не строки таблиц, как это принято в табличных базах данных, а экземпляры объектов. В пространстве имен Cerebrum.Reflection этим таблицам соответствуют классы TypeDescriptor, AttributeDescriptor, TableDescriptor. Класс MemberDescriptor представляет собой базовый класс, от которого унаследованы классы TypeDescriptor, AttributeDescriptor, TableDescriptor.

public class MemberDescriptor : Cerebrum.Integrator.GenericComponent { public string Name public string DisplayName public string Description }

где

Name - имя экземпляра;

DisplayName - дружественное имя экземпляра предъявляемое пользователю;

Description - описание данного экземпляра.

MemberDescriptor - это базовый класс для классов которые обладают атрибутами имя, дружественное имя и описание. Многие дескрипторы могут иметь такие атрибуты, поэтому удобно наследовать такие дескрипторы объектов от MemberDescriptor.

Колонка таблицы представляет значения некоторого атрибута всех объектов данной таблицы. Атрибут объекта отделен и независим от таблицы. Один и тот же атрибут может принадлежать разным таблицам. Колонка таблицы соответствует некоторому атрибуту объекта. Таблица Attributes содержит информацию об атрибутах объектов, находящихся в сетевой объектно-ориентированной базе. Эта таблица содержит экземпляры класса, реализующие интерфейс IAttributeDescriptor. В настоящее время этот интерфейс реализован классами AttributeDescriptor и FieldDescriptor. Интерфейс IAttributeDescriptor описывает некоторый атрибут объекта.

public interface IAttributeDescriptor { string Name {get;} Cerebrum.ObjectHandle AttributePlasmaHandle(IComposite composite); Cerebrum.ObjectHandle AttributeTypeIdHandle{get;set;} System.Type AttributeType {get;} IConnector GetTypeDescriptor(); bool Derived {get;} }

где

Name - имя аттрибута;

AttributePlasmaHandle возвращает идентификатор данного атрибута;

AttributeTypeHandle возвращает идентификатор типа;

AttributeType возвращает тип данного атрибута;

GetTypeDescriptor() возвращает объект-оболочку дескриптора типа TypeDescriptor данного атрибута;

Derived определяет, является ли данный экземпляр дескриптора расширителем для другого экземпляра дескриптора аттрибута.

Экземпляры AttributeDescriptor описывают атрибуты объектов. Этот класс расширяет MemberDescriptor добавляя реализацию интерфейса IAttributeDescriptor.

public class AttributeDescriptor : MemberDescriptor, IAttributeDescriptor

Экземпляры FieldDescriptor описывают связь аттрибута с некоторой таблицей и расширяют некоторый уже существующий экземпляр дескриптора аттрибута, наследуя у него описание аттрибута. public class FieldDescriptor :

Cerebrum.Integrator.GenericComponent,

Cerebrum.Reflection.IAttributeDescriptor { public Cerebrum.Runtime.IConnector GetRelatedAttribute() public Cerebrum.Runtime.IConnector GetRelatedTable() }

где

GetRelatedAttribute – возвращает объект-оболочку связанного дескриптора аттрибута;

GetRelatedTable возвращает объект-оболочку связанного дескриптора таблицы.

Если в базе находится несколько объектов, которые имеют атрибут имя, то значениями для этого атрибута (колонки) являются имена этих объектов. Дескрипторы атрибутов (колонки) так же являются объектами ООБД. Например, объект-атрибут "Object name" так же имеет имя, так как класс AttributeDescriptor унаследован от класса MemberDescriptor. Очевидно что, значением атрибута "Object Name" для экземпляра класса AttributeDescriptor, описывающего имя объекта является строка "Object name".

Рассмотрим пример. Для свойства Name объекта MemberDescriptor в ООБД создается экземпляр объекта AttributeDescriptor с значениями свойств соответственно:

Name = “Name”; DisplayName = “Name”; Description = “Object name”; AttributeType = typeof(System.String); GetTypeDescriptor() = экземпляр TypeDescriptor описывающий System.String .

Таким образом экземпляры класса AttributeDescriptor описывают свойства объектов, хранимых в ООБД, в том числе и свойства собственно класса AttributeDescriptor.

Таблица Types содержит описание типов данных (включая пользовательские), которые известны системе и на основе которых система создает экземпляры объектов, помещаемые в сетевую базу. Эта таблица содержит экземпляры классов TypeDescriptor.

public class TypeDescriptor : MemberDescriptor { public string QualifiedTypeName public Cerebrum.ObjectHandle KernelObjectClass public System.Type ComponentType }

где

QualifiedTypeName - возвращает имя .NET класса на основе которого создаются пользовательские объекты, например “System.String” ;

KernelObjectClass - возвращает тип объекта ядра VNPI ассоциированного с экземплярами описываемого типа;

ComponentType - возвращает typeof(QualifiedTypeName) тоесть .NET тип описываемый дескриптором. Свойства, унаследованные от MemberDescriptor были описаны ранее.


Рассмотрим пример. Для свойства QualifiedTypeName создается экземпляр AttributeDescriptor у которого Name = “QualifiedTypeName” DisplayName = “Qualified Type Name” Description = “.NET Qualified Type Name” AttributeType = typeof(System.String) GetTypeDescriptor() = экземпляр TypeDescriptor описывающий System.String

Данная структура позволяет описывать типы классов объектов, содержащиеся в ООБД. Описатели типов также являются объектами, содержащимися в ООБД. Поэтому описатели типов описывают сами себя. Можно заметить, что у объектов разных классов имеются совместимые атрибуты – например у многих объектов есть атрибут “Name” который описывает имя объекта. Объекты являющиеся экземплярами одного и того же класса имеют одинаковый список атрибутов. Логично сгруппировать однотипные объекты в таблицы у которых колонки будут представлять атрибуты, а сроки – собственно экземпляры объектов. Таблица Tables содержит описание всех таблиц, содержащихся в объектной базе данных. Таблицы это тоже объекты. Имя таблицы - значение атрибута "Object name" у объекта таблицы. Каждая таблица имеет две коллекции указателей на объекты. Коллекцию атрибутов (колонок) и коллекцию компонентов (строк). Каждая строка любой таблицы это тоже объект. Если в коллекцию указателей на строки включить указатель на саму таблицу, то таблица станет собственной строкой. Таблица Tables содержит сама себя в качестве строки. И таблица Tables и строка Tables в таблице Tables - это один и тот же экземпляр одного и того же класса TableDescriptor. Этот экземпляр расположен по одному и тому же адресу в памяти. Значениями для колонок таблицы являются значения атрибутов объектов находящихся в строках таблицы. А значением ее колонки "SelectedAttributes" является коллекция колонок этой же таблицы. Хотя желательно держать в одной и той же таблице объекты одного и того же типа - это вовсе не обязательно. Главное чтоб у этих объектов были атрибуты, хотя бы частично совпадающие с колонками данной таблицы. Я рекомендую держать в одной таблице объекты, классы которых унаследованы от некоторого, базового класса, который содержит все колонки данной таблицы в качестве собственных атрибутов. Например, колонка "Object name" имеет, смыл практически в любой таблице. Объекты могут входить одновременно в разные таблицы как их строки. Дескрипторы атрибутов объектов сами являются объектами и зарегистрированы в таблице атрибутов.

Класс TableDescriptor описывает таблицу из экземпляров некоторых объектов. Свойства, унаследованные от MemberDescriptor, соответственно описывают имя, дружественное имя и описание данной таблицы.

public class TableDescriptor : MemberDescriptor, System.ComponentModel.IListSource { public Cerebrum.Runtime.NativeWarden GetSelectedAttributesVector() public Cerebrum.Runtime.NativeWarden GetSelectedComponentsVector() public System.Type ComponentType } где GetSelectedAttributesVector - метод возвращает коллекцию атрибутов таблицы; GetSelectedComponentsVector - метод возвращает коллекцию строк таблицы; ComponentType - свойство возвращает тип объектов содержащихся в строках таблицы.

Методу GetSelectedAttributesVector соответствует экземпляр AttributeDescriptor у которого Name = “SelectedAttributes” DisplayName = “SelectedAttributes” Description = “SelectedAttributes collection” AttributeType = typeof(System.ComponentModel.IBindingList)

Метод GetSelectedComponentsVector возвращает коллекцию объектов - строк, этому методу соответствует экземпляр AttributeDescriptor у которого Name = “SelectedComponents” DisplayName = “SelectedComponents” Description = “SelectedComponentscollection” AttributeType = typeof(System.ComponentModel.IBindingList)

Все экземпляры AttributeDescriptor перечислены в таблице TableDescriptor.Name = “Attributes”. Все экземпляры TypeDescriptor перечислены в таблице TableDescriptor.Name = “Types”. Все экземпляры TableDescriptor перечислены в таблице TableDescriptor.Name = “Tables”. При этом экземпляр класса AttributeDescriptor.Name = “Name” содержится в коллекции строк таблицы Attributes, а так же в коллекциях атрибутов (колонок) таблиц “Attributes”, “Types” и “Tables”. Экземпляр класса TableDescriptor.Name = “Tables” хранится в своей же коллекции строк таблицы (SelectedComponents). Можно заметить, что применение тавтологии для эмуляции РБД на основе ООБД является оправданным и полезным. Экземпляры объектов могут одновременно входить в разные таблицы при этом важно, чтоб они поддерживали все атрибуты, являющиеся колонками таблиц в которые они входят в качестве строк. Таким образом, каждая таблица содержит не некоторые абстрактные строки с данными, а конкретные экземпляры .NET объектов. Колонки таблиц представляют собой атрибуты (custom properties), поддерживаемые этими объектами. Сами типы, атрибуты и таблицы так же являются объектами и представлены строками в соответствующих таблицах. Каждая строка некоторой таблицы представляет собой экземпляр объекта, содержащийся в базе данных. Каждая колонка некоторой таблицы представляет собой атрибут, описанный в таблице Atributes. Экземпляр класса (объект) не зависит от таблицы. Не обязательно создав объект помещать его строкой в какую то таблицу. Будучи созданным, объект будет продолжать существовать либо в оперативной памяти системы либо в качестве потока в файловом хранилище до тех пор, пока на него будет существовать хотябы одна ссылка. Созданный экземпляр будет сохранять свою идентичность и значения атрибутов через различные циклы запуска и останова системы.

Один и тот же объект имеет право не быть строкой ни в одной таблице или быть строкой в нескольких таблицах одновременно. Так как каждая строка таблицы является экземпляром объекта и имеет свой Cerebrum.ObjectHandle то эффективнее работать с такими строками методом прямой адресации. Объект не превратится в таблицу, если его добавить строкой в таблицу Tables или в атрибут, если его добавили в таблицу Attributes. Все экземпляры объектов, добавляемые в одну таблицу, должны быть унаследованы от одного базового типа, который ожидается пользовательским кодом. В этом случае имеет место стандартный полиморфизм. Чтобы быть добавленным в таблицу Tables объект должен быть либо экземпляром класса TableDescriptor, либо экземпляром класса, унаследованного от класса TableDescriptor. Чтобы быть добавленным в таблицу Attributes объект должен быть либо экземпляром класса, реализующим интерфейс IAttributeDescriptor например AttributeDescriptor, либо экземпляром класса, унаследованного от класса AttributeDescriptor. Эта возможность используется при добавлении объектов различных типов в таблицу UiServices. Таблица UiServices хранит пользовательские объекты, унаследованные от класса Cerebrum.Windows.Forms.Reflection.UiServiceDescriptor

Пользователь имеет возможность создавать новые таблицы путем добавления новых записей в таблицу Tables. Созданные экземпляры таблиц, атрибутов и других объектов так же не обязательно регистрировать в соответствующих таблицах. Однако практика регистрации таких объектов в соответствующих таблицах является рекомендуемой, так как позволяет изменять их атрибуты через унифицированный интерфейс пользователя. Обязательным требованием является регистрация пользовательского типа в таблице Types. В этом случае Integrator.Acivator при необходимости автоматически произведет создание нового экземпляра. В противном случае программист будет обязан реализовать собственную версию интерфейса IActivator.

В ОО БД поддерживаются полноценные C# объекты, у объектов поддерживаются методы, свойства, поля и события. В данной разработке статические методы классов - просто обычные статические методы в .NET а экземплярные методы - обычные методы объектов. Рекомендуется создавать свойства для поддерживаемых классом атрибутов. Механизм делегатов и событий не будет поддерживаться, в случае если связь реализуется между объектами, хранящимися в БД. Это связанно с ограничениями по времени жизни экземпляров. Во время, когда БД остановлена, все объекты хранятся в сериализированном (serialized) виде. Если время жизни у приемников или источников сообщений может быть изолированно от времени жизни объектов БД, то механизмы делегатов - сообщений работают в пределах подсети объектов развернутых в памяти .NET Framework.

Система Cerebrum разрабатывается в первую очередь для поддержки нейронных сетей со свободной топологией и числом нейронов до 2 млрд в одном хранилище. Поэтому только часть ОО БД находится в данный момент в RAM. Большая часть объектов заморожена в файловом хранилище и десериализируется (deserializing) по мере необходимости. Учитывая, что у каждого объекта в такой базе имеется поведение, реализованное в виде методов этого объекта, логично называть такую базу активной. Используя данную разработку, программист полностью свободен создавать собственные таблицы, содержащие объекты-строки собственных типов. Так как классы можно разрабатывать самостоятельно, то строки таблиц могут иметь методы, свойства, события и все другие конструкции, допустимые в Microsoft .NET Framework.

Список литературы

  1. Шуклин Д.Е. Cerebrum: Система управления сетевой объектно-ориентированной базой знаний // Интернет публикация 2005, http://www.shuklin.com/ai/ht/ru/cerebrum/
  2. Кузнецов С.Д. Три манифеста баз данных: ретроспектива и перспективы// Доклад на международной научной конференции "Базы данных и информационные технологии XXI века" http://www.citforum.ru/database/articles/manifests/
Advertisement