Обновить
32K+
4
Kozin Rinat@grelikt

Пользователь

37,2
Рейтинг
7
Подписчики
Отправить сообщение
redb.Core
redb.Core

«Это написал Клод» — комментарий из-за рубежа, и почему идее хранилища больше двадцати лет (даже если так было бы, то клод выпивая тонные кофе это всё родил ...)

Под англоязычной версией статьи про SQLite-провайдер на dev.to появился комментарий в духе «даже без "load-bearing" и тире через всё предложение видно, что это писал Клод». Не первый раз слышу что-то подобное, так что решил ответить не в комментариях, а отдельным постом — заодно расскажу то, что давно собирался: откуда вообще взялась идея хранилища, на котором всё это стоит.

Сначала честно про долю ИИ

Не скрываю и не буду: текст статей — да, пишу в паре с Клодом, я не копирайтер и не техрайтер по профессии. Но код библиотек — другая история. Скелет и инженерные решения — мои, всегда были моими. Клод дописывает рутину поверх уже заданного паттерна, полирует, ловит ошибки, пишет тесты и комментарии. Изобретать архитектуру и решать неочевидные компромиссы — не его работа, это разные навыки, и Клод хорош ровно в первом, не во втором.

Показательный пример — сам релиз 3.6.0: баги, которые в него вошли, нашёл не Клод, а реальная эксплуатация. Модель не может споткнуться о краевой случай, который проявляется только под живой нагрузкой с непредсказуемым вводом — такое не ловится по аналогии с соседним кодом.

А теперь — откуда это всё вообще взялось

Основная идея хранилища — не изобретение последних месяцев. Первая версия появилась в 2004 году, когда я писал на Delphi — за двадцать с лишним лет до того, как Клод вообще начал существовать.

Задача была — дать объекту динамические поля на ходу, не фиксируя их жёстким классом заранее. Delphi для этого уже нёс нужный кусок — RTTI (Run-Time Type Information): каждый класс несёт метаданные о своих полях и свойствах, доступные в рантайме, не только на этапе компиляции. Поверх этого — интерфейс IDispatch из COM: GetIDsOfNames резолвит имя поля в DISPID, Invoke вызывает по этому DISPID, передавая значение через VARIANT. Вместе это давало то, чего не даёт обычный жёсткий класс: объект мог обзавестись полем, которого не существовало на момент компиляции, а вызывающая сторона — спросить о нём по имени и получить настоящий типизированный ответ.

Система прожила у меня внутри собственных проектов много лет, никуда не публикуясь. За это время она полностью пережила Delphi и COM — переехала на .NET, механизм сменился до неузнаваемости (никакого VARIANT, никакого DISPID — сейчас это типизированные колонки в Postgres/MSSQL/SQLite, которые я уже разбирал построчно в статье про 13 таблиц), а вопрос остался ровно тем же: как дать объекту гибкий набор полей, не потеряв возможность спросить "а какого оно вообще типа".

Отсюда, кстати, и название — RTTI-based storage, не маркетинговый термин, а прямое родство с тем самым механизмом Delphi. И не EAV — там никогда не было обезличенной колонки "значение", RTTI всегда знало настоящий тип.

Клод помогал полировать это перед тем, как это увидело свет публично. Сама идея и её первое воплощение — старше Клода на десятилетия.

Цикл про redb:

Всё лежит в публичных репозиториях — redbase-app. Предлагаю не гадать по стилю прозы, а склонировать к себе и прогнать нейронкой — глубоко, очень глубоко. И приглашаю на дискуссию: если найдёте что-то, что выглядит как архитектурное решение именно от модели, а не от человека, который держит всю систему в голове годами — с удовольствием обсужу предметно.

Теги:
+5
Комментарии0

Пассажир меняет билет прямо в дороге — а маршрут собран из трёх GDS и ж/д. Что происходит с данными

Сотруднику нужно долететь до одного города, доехать поездом до другого, и обратно тем же путём — одна командировка, билеты из разных систем бронирования. А потом он уже в дороге пишет в телеграм: "планы изменились, летим не туда, перебронируй".

Три системы под самолёты — Amadeus, Sabre, Travelport: исторически несовместимые XML-диалекты одного и того же понятия перелёта, выросшие из мейнфреймов 60-80-х, каждый со своими причудами и полями, которых нет у соседа. Плюс отдельная, никак не связанная с ними система бронирования под железную дорогу — свой формат, своя логика мест и классов, ничего общего по структуре с авиационными GDS. Запросить всё это параллельно, свести разноформатные ответы в одно и собрать из них валидный маршрут по стыковкам — уже само по себе задача не для россыпи if и ручных мапперов.

А потом человек уже в дороге меняет одно плечо маршрута. Один перелёт уже случился — трогать нельзя. Один — впереди, подлежит замене. И весь маршрут не по шаблону "туда-обратно", а любой длины и состава: сегодня две пересадки, завтра — четыре, послезавтра прямо в дороге вставили лишний вылет.

Тут ломаются две разные вещи, и почти всегда решают только одну.

Первая — как описать саму логику поверх этого зоопарка источников. И сбор из четырёх систем разом, и ветка "можно менять / нельзя менять" превращаются либо в DSL на языке общепринятых интеграционных паттернов (Scatter-Gather, Content-Based Router — тот же словарь, что у Apache Camel), который прочитает и поймёт человек, ни разу его не писавший, — либо в код, который через полгода не восстановит и автор.

Вторая — куда положить результат. Маршрут — не два поля outbound/return, а последовательность разнотипных плеч (самолёт ≠ поезд), где число элементов и состав не известны заранее и меняются посреди собственной жизни объекта. Стандартный ответ — либо гора nullable-колонок под все виды транспорта разом, либо миграция на каждый новый вид.

Разбираю, что происходит, когда обе оси — читаемый DSL для потока и пластичная типизация для формы — закрывает один связный инструментарий, а не сшиваются вручную посередине, каждая своими костылями.

Скоро.

ссылки: хабр redb.ru

Теги:
+3
Комментарии3

Как redb хранит сложные объекты: не бесхемная свалка, а RTTI

Про redb (RedBase) есть два зеркальных заблуждения. Первое: раз объект пишется «как документ», внутри лежит сериализованный JSON или блоб. Второе, противоположное: раз всё падает в общую таблицу значений — значит это плоский мешок пар «ключ-значение» без схемы.

Оба мимо. То, что внутри — это RTTI, полноценная система типов, живущая в самой базе. И устроена она заметно сложнее, чем и блоб, и «атрибут-значение». Разбор архитектуры я подробно давал в отдельной статье на Хабре: «redb: реляционное хранилище объектов» — ниже сжатая суть.

Слой типов: база знает настоящий тип каждого поля

redb хранит не только данные, но и их описание типов — три связанных уровня:

  • types — реальные дескрипторы типов (db_type и соответствие .NET-типу);

  • _schemes — сами типы (классы), с поддержкой наследования через self-reference;

  • structures — типизированные поля схемы: имя (name), тип (_id_type, FK на types), признак коллекции (collection_type) и вложенность (_id_parent).

Значения в values всегда привязаны к конкретной структуре (id_structure, FK на structures). Поэтому строка values — это не безымянная пара «атрибут-значение»: это значение известного, именованного, типизированного, возможно вложенного поля известного класса. База в рантайме знает, что перед ней — decimal Salary в схеме Employee, а не абстрактный «атрибут №42». Это и есть RTTI.

Хранение коллекций: построчно, реляционно

Вложенные массивы, словари и глубокие иерархии redb раскладывает в _values построчно, а не строкой:

  • Никаких JSON-блобов на диске. Каждый элемент коллекции — отдельная строка с типизированными колонками (_value_longvaluestringvaluedatetimevalueguid, …), внешними ключами и обычными индексами.

  • Связь и порядок — реляционные. Вложенность собирается self-reference колонкой arrayparent_id (FK на values.idON DELETE CASCADE). Порядок массива и ключи словаря — в arrayindex (text: '0','1','2' для массивов, строковый ключ — для словарей).

  • Один элемент — одна строка. List<OrderItem> внутри класса не превращается ни в JSON-поле, ни в десяток физических таблиц, которые вы заводите руками.

Что это даёт на практике

  1. Честный LINQ на уровне СУБД. Данные лежат в типизированных колонках, а метаданные структур позволяют движку собрать нативный SQL: Where / OrderBy / GroupBy / оконные функции идут по реальным индексам базы, а не перебором JSON в памяти бэкенда.

  2. Загрузка за один запрос без каскада JOIN-ов. Чтобы поднять объект со всей глубиной вложенности (пусть там 20–30 списков), не нужен каскад JOIN, как у EF с .Include(). Плоская структура забирается из _values одним запросом и собирается в объект в памяти.

  3. Точечный Change Tracking (Pro). При сохранении Pro-версия строит деревья ValueTreeNode (память против БД), сравнивает их (ValueTreeBuilder / ValueTreeDiff) и шлёт UPDATE только по изменившимся узлам — граф целиком не перезаписывается.

Итог: redb совмещает удобство работы с объектами «как с документами» и фундамент реляционной СУБД — типизацию, индексы, FK и запросы, которые исполняет база, а не бэкенд. Ключ к этому — не блоб и не плоский мешок атрибутов, а persisted-RTTI: typesschemesstructuresvalues.

Ссылки

Теги:
+4
Комментарии0

redb ecosystem
вышла версия 3.5.0(1) nuget
в ней Локальная база на клиенте
также новый коннектор redb.Route.As2 github
добавлены EIP паттерны
Message History EIP 
XSLT transformation
Routing Slip EIP 

посмотреть можно здесь github.com/redbase-app

хабр

Теги:
+5
Комментарии0

Прогнал официальный conformance suite OpenID Foundation против redb.Identity.

Basic OP: 35 модулей, 0 failures. Config OP: 0 failures.

Что нашли и исправили:

  • PII утечка: scope-derived claims (phone, email) попадали в id_token — они должны быть только в UserInfo

  • UserInfo возвращал внутренности OpenIddict через deny-list

  • Отсутствовал Cache-Control: no-store на token endpoint (RFC 6749 §5.1)

  • prompt=login / max_age ре-аутентификация была сломана

Одно WARNING осталось намеренно — два приватных claim в id_token с обоснованием.

Полный отчёт

Теги:
+2
Комментарии0

SQLite-провайдер для RedBase — скоро.

Тот же API что PostgreSQL и MSSQL. Без миграций, полный LINQ, типизированные колонки.

Free: нативное расширение (.so / .dll / .dylib).
Pro: чистый C# — работает в Blazor WASM.

Минимальная версия SQLite 3.44.0+.

Теги:
Всего голосов 2: ↑1 и ↓1+2
Комментарии0
впечатлялся )))
впечатлялся )))

Сегодня к вечеру я совсем обленился и решил доверить нейронке storege создать
к коннектору в библиотеку redb.route используя redb
Изучала дольше чем писала. 😊 Накидала в одну сессию за один проход с тестами.
__
Аудитория: разработчики уже подключают DSL-маршруты к redb.Route, которые хотят, чтобы LLM был полноценным пользователем конвейера - с памятью, бюджетом, разрешениями и аудиторским журналом, а не HTTP—вызовом без сохранения состояния, который ничего не оставляет после себя.
единый линейный маршрут.Услуги.AddRedbLlmStorage() — переключает цикл работы агента с "забывает все при перезапуске" на постоянную систему по умолчанию. Все пять поверхностей (расшифровки, утверждения, бюджеты, идемпотентность, аудит) перемещаются в redb. Ни одна строка кода маршрута не меняется — ваш существующий .To("llm://claude") начинает сохраняться сам по себе.

Теги:
Всего голосов 5: ↑3 и ↓2+3
Комментарии0

Информация

В рейтинге
216-й
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Архитектор программного обеспечения
Ведущий
C#
PostgreSQL
Базы данных
RabbitMQ
Высоконагруженные системы
Java
Apache Kafka
Микросервисная архитектура
Apache Camel
SQL