Внутренности реестра
Внутренности реестра
Заголовок раздела «Внутренности реестра»Реестр — это версионированное хранилище состояния, управляемое событиями. Он хранит полную историю версий, поддерживает транзакции и распространяет изменения через шину событий.
Хранение записей
Заголовок раздела «Хранение записей»Записи хранятся как упорядоченный срез с хеш-картой для O(1) поиска:
type Entry struct { ID ID // namespace:name Kind Kind // Тип записи Meta attrs.Bag // Метаданные Data payload.Payload // Содержимое}ID записей используют пакет Go unique для интернирования — идентичные ID разделяют память.
Цепочка версий
Заголовок раздела «Цепочка версий»Каждая версия указывает на родительскую. Вычисление пути использует графовый алгоритм для поиска кратчайшего маршрута между любыми двумя версиями:
flowchart LR v0[v0] --> v1[v1] --> v2[v2] --> v3[v3] --> vN[vN]ChangeSets
Заголовок раздела «ChangeSets»Changeset — это упорядоченный список операций, трансформирующих одно состояние в другое:
| Операция | OriginalEntry | Назначение |
|---|---|---|
| Create | nil | Добавление новой записи |
| Update | старое значение | Изменение существующей |
| Delete | удалённое значение | Удаление записи |
OriginalEntry позволяет откат — обновления хранят предыдущее значение, удаления хранят что было удалено.
Построение дельт
Заголовок раздела «Построение дельт»BuildDelta(oldState, newState) генерирует минимальные операции:
- Сравнивает состояния, определяет изменения
- Сортирует удаления в обратном порядке зависимостей (зависимые сначала)
- Сортирует создания/обновления в прямом порядке зависимостей (зависимости сначала)
Несколько changeset’ов объединяются отслеживанием финального состояния каждой записи:
Create + Update = Create (с обновлённым значением)Create + Delete = ∅ (взаимоуничтожаются)Update + Delete = DeleteDelete + Create = UpdateТранзакции
Заголовок раздела «Транзакции»sequenceDiagram participant R as Registry participant B as EventBus participant H as Handlers
R->>B: registry.begin loop Каждая операция R->>B: entry.create/update/delete B->>H: dispatch слушателям H-->>B: принять или отклонить B-->>R: подтверждение end alt Всё принято R->>B: registry.commit else Что-то отклонено R->>B: registry.discard R->>R: откат endУ обработчиков 30 секунд на принятие или отклонение каждой операции. При отклонении реестр откатывается, вычисляя и применяя обратную дельту.
Непропагируемые записи
Заголовок раздела «Непропагируемые записи»Некоторые виды полностью обходят шину событий:
registry.entry— конфигурации приложенияns.requirement— требования namespacens.dependency— зависимости модулейns.definition— метаданные модуля (readme, лицензия, авторы)
Разрешение зависимостей
Заголовок раздела «Разрешение зависимостей»Записи могут объявлять зависимости от других записей. Резолвер извлекает зависимости через зарегистрированные паттерны:
resolver.RegisterPattern(registry.DependencyPattern{ Path: "meta.server", AllowWildcard: true,})Зависимости извлекаются из полей Meta и Data записи, затем используются для топологической сортировки при переходах состояний.
История версий
Заголовок раздела «История версий»Бэкенды истории:
| Реализация | Применение |
|---|---|
| SQLite | Продакшен-персистентность |
| Memory | Тестирование |
| Nil | Без истории |
SQLite использует WAL-режим с таблицами для версий, changeset’ов (кодированных MessagePack) и метаданных.
Навигация
Заголовок раздела «Навигация»Вычисление пути находит кратчайший маршрут между версиями:
Path(v0, v3) = [v1, v2, v3] // Применить changeset'ы вперёдPath(v3, v1) = [v2, v1] // Применить обращённые changeset'ыLoadState() воспроизводит историю от базовой линии без создания новых версий — используется при загрузке.
Движок запросов с LRU-кешированием для поиска записей:
| Оператор | Префикс | Пример |
|---|---|---|
| Glob | (нет) | .kind=function.* |
| Regex | ~ | ~meta.path=/api/.* |
| Contains | * | *meta.tags=backend |
| Prefix | ^ | ^meta.name=user |
| Suffix | $ | $meta.path=Handler |
Кеш инвалидируется при изменении версии.