mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 16:36:21 +02:00
781 lines
42 KiB
Markdown
781 lines
42 KiB
Markdown
|
|
---
|
|||
|
|
layout: default
|
|||
|
|
title: "Техническое описание: Поддержка многопользовательской среды"
|
|||
|
|
parent: "Russian (Beta)"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Техническое описание: Поддержка многопользовательской среды
|
|||
|
|
|
|||
|
|
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
|
|||
|
|
|
|||
|
|
## Обзор
|
|||
|
|
|
|||
|
|
Обеспечьте поддержку многопользовательской среды, устранив несоответствия в именах параметров, которые препятствуют настройке очередей, и добавив параметризацию пространства ключей Cassandra.
|
|||
|
|
|
|||
|
|
## Контекст архитектуры
|
|||
|
|
|
|||
|
|
### Разрешение очередей на основе потоков
|
|||
|
|
|
|||
|
|
Система TrustGraph использует **архитектуру на основе потоков** для динамического разрешения очередей, что изначально поддерживает многопользовательскую среду:
|
|||
|
|
|
|||
|
|
**Определения потоков** хранятся в Cassandra и указывают имена очередей через определения интерфейсов.
|
|||
|
|
**Имена очередей используют шаблоны** с переменными `{id}`, которые заменяются идентификаторами экземпляров потоков.
|
|||
|
|
**Сервисы динамически разрешают очереди**, получая конфигурации потоков в момент запроса.
|
|||
|
|
**Каждый пользователь может иметь уникальные потоки** с разными именами очередей, что обеспечивает изоляцию.
|
|||
|
|
|
|||
|
|
Пример определения интерфейса потока:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"interfaces": {
|
|||
|
|
"triples-store": "persistent://tg/flow/triples-store:{id}",
|
|||
|
|
"graph-embeddings-store": "persistent://tg/flow/graph-embeddings-store:{id}"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Когда арендатор A запускает поток `tenant-a-prod`, а арендатор B запускает поток `tenant-b-prod`, они автоматически получают изолированные очереди:
|
|||
|
|
`persistent://tg/flow/triples-store:tenant-a-prod`
|
|||
|
|
`persistent://tg/flow/triples-store:tenant-b-prod`
|
|||
|
|
|
|||
|
|
**Сервисы, правильно разработанные для многоарендности:**
|
|||
|
|
✅ **Knowledge Management (основные компоненты)** - Динамически определяет очереди из конфигурации потока, передаваемой в запросах
|
|||
|
|
|
|||
|
|
**Сервисы, требующие исправления:**
|
|||
|
|
🔴 **Config Service** - Несоответствие имен параметров препятствует настройке очереди
|
|||
|
|
🔴 **Librarian Service** - Жестко заданные темы управления хранилищем (описано ниже)
|
|||
|
|
🔴 **Все сервисы** - Невозможно настроить пространство ключей Cassandra
|
|||
|
|
|
|||
|
|
## Описание проблемы
|
|||
|
|
|
|||
|
|
### Проблема #1: Несоответствие имен параметров в AsyncProcessor
|
|||
|
|
**CLI определяет:** `--config-queue` (неясное наименование)
|
|||
|
|
**Argparse преобразует в:** `config_queue` (в словаре параметров)
|
|||
|
|
**Код ищет:** `config_push_queue`
|
|||
|
|
**Результат:** Параметр игнорируется, используется значение по умолчанию `persistent://tg/config/config`
|
|||
|
|
**Влияние:** Затрагивает все 32+ сервиса, наследующие от AsyncProcessor
|
|||
|
|
**Препятствует:** Многоарендные развертывания не могут использовать очереди, специфичные для арендатора
|
|||
|
|
**Решение:** Переименовать параметр CLI в `--config-push-queue` для ясности (разрыв обратной совместимости допустим, так как функция в настоящее время не работает)
|
|||
|
|
|
|||
|
|
### Проблема #2: Несоответствие имен параметров в Config Service
|
|||
|
|
**CLI определяет:** `--push-queue` (двусмысленное наименование)
|
|||
|
|
**Argparse преобразует в:** `push_queue` (в словаре параметров)
|
|||
|
|
**Код ищет:** `config_push_queue`
|
|||
|
|
**Результат:** Параметр игнорируется
|
|||
|
|
**Влияние:** Сервис Config не может использовать пользовательскую очередь отправки
|
|||
|
|
**Решение:** Переименовать параметр CLI в `--config-push-queue` для согласованности и ясности (разрыв обратной совместимости допустим)
|
|||
|
|
|
|||
|
|
### Проблема #3: Жестко заданное пространство ключей Cassandra
|
|||
|
|
**Текущее состояние:** Пространство ключей жестко задано как `"config"`, `"knowledge"`, `"librarian"` в различных сервисах
|
|||
|
|
**Результат:** Невозможно настроить пространство ключей для многоарендных развертываний
|
|||
|
|
**Влияние:** Сервисы Config, основные компоненты и Librarian
|
|||
|
|
**Препятствует:** Несколько арендаторов не могут использовать отдельные пространства ключей Cassandra
|
|||
|
|
|
|||
|
|
### Проблема #4: Архитектура управления коллекциями ✅ ВЫПОЛНЕНО
|
|||
|
|
**Предыдущее состояние:** Коллекции хранились в пространстве ключей Cassandra Librarian через отдельную таблицу коллекций
|
|||
|
|
**Предыдущее состояние:** Librarian использовал 4 жестко заданные темы управления хранилищем для координации создания/удаления коллекций:
|
|||
|
|
`vector_storage_management_topic`
|
|||
|
|
`object_storage_management_topic`
|
|||
|
|
`triples_storage_management_topic`
|
|||
|
|
`storage_management_response_topic`
|
|||
|
|
**Проблемы (решены):**
|
|||
|
|
Жестко заданные темы не могли быть настроены для многоарендных развертываний
|
|||
|
|
Сложная асинхронная координация между Librarian и 4+ сервисами хранения
|
|||
|
|
Отдельная таблица Cassandra и инфраструктура управления
|
|||
|
|
Непостоянные очереди запросов/ответов для критических операций
|
|||
|
|
**Решение реализовано:** Коллекции были перенесены в хранилище сервиса Config, используется отправка Config для распространения
|
|||
|
|
**Статус:** Все хранилища были перенесены на шаблон `CollectionConfigHandler`
|
|||
|
|
|
|||
|
|
## Решение
|
|||
|
|
|
|||
|
|
Эта спецификация решает проблемы #1, #2, #3 и #4.
|
|||
|
|
|
|||
|
|
### Часть 1: Исправление несоответствий имен параметров
|
|||
|
|
|
|||
|
|
#### Изменение 1: Базовый класс AsyncProcessor - Переименование параметра CLI
|
|||
|
|
**Файл:** `trustgraph-base/trustgraph/base/async_processor.py`
|
|||
|
|
**Строка:** 260-264
|
|||
|
|
|
|||
|
|
**Текущее состояние:**
|
|||
|
|
```python
|
|||
|
|
parser.add_argument(
|
|||
|
|
'--config-queue',
|
|||
|
|
default=default_config_queue,
|
|||
|
|
help=f'Config push queue {default_config_queue}',
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Исправлено:**
|
|||
|
|
```python
|
|||
|
|
parser.add_argument(
|
|||
|
|
'--config-push-queue',
|
|||
|
|
default=default_config_queue,
|
|||
|
|
help=f'Config push queue (default: {default_config_queue})',
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
Более понятное и явное наименование
|
|||
|
|
Соответствует внутреннему имени переменной `config_push_queue`
|
|||
|
|
Изменение, нарушающее обратную совместимость, допустимо, поскольку функция в настоящее время не функционирует
|
|||
|
|
Не требуется изменение кода в params.get() - он уже ищет правильное имя
|
|||
|
|
|
|||
|
|
#### Изменение 2: Сервис конфигурации - Переименование параметра командной строки
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/config/service/service.py`
|
|||
|
|
**Строка:** 276-279
|
|||
|
|
|
|||
|
|
**Текущее:**
|
|||
|
|
```python
|
|||
|
|
parser.add_argument(
|
|||
|
|
'--push-queue',
|
|||
|
|
default=default_config_push_queue,
|
|||
|
|
help=f'Config push queue (default: {default_config_push_queue})'
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Исправлено:**
|
|||
|
|
```python
|
|||
|
|
parser.add_argument(
|
|||
|
|
'--config-push-queue',
|
|||
|
|
default=default_config_push_queue,
|
|||
|
|
help=f'Config push queue (default: {default_config_push_queue})'
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
Более понятное наименование - "config-push-queue" более явно, чем просто "push-queue".
|
|||
|
|
Соответствует внутреннему имени переменной `config_push_queue`.
|
|||
|
|
Соответствует параметру `--config-push-queue` класса AsyncProcessor.
|
|||
|
|
Изменение, нарушающее обратную совместимость, допустимо, поскольку функция в настоящее время не работает.
|
|||
|
|
Не требуется изменение кода в params.get() - он уже ищет правильное имя.
|
|||
|
|
|
|||
|
|
### Часть 2: Добавление параметризации пространства ключей Cassandra
|
|||
|
|
|
|||
|
|
#### Изменение 3: Добавление параметра пространства ключей в модуль cassandra_config
|
|||
|
|
**Файл:** `trustgraph-base/trustgraph/base/cassandra_config.py`
|
|||
|
|
|
|||
|
|
**Добавление аргумента командной строки** (в функции `add_cassandra_args()`):
|
|||
|
|
```python
|
|||
|
|
parser.add_argument(
|
|||
|
|
'--cassandra-keyspace',
|
|||
|
|
default=None,
|
|||
|
|
help='Cassandra keyspace (default: service-specific)'
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Добавить поддержку переменных окружения** (в функции `resolve_cassandra_config()`):
|
|||
|
|
```python
|
|||
|
|
keyspace = params.get(
|
|||
|
|
"cassandra_keyspace",
|
|||
|
|
os.environ.get("CASSANDRA_KEYSPACE")
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновить возвращаемое значение** `resolve_cassandra_config()`:
|
|||
|
|
В настоящее время возвращает: `(hosts, username, password)`
|
|||
|
|
Изменить, чтобы возвращалось: `(hosts, username, password, keyspace)`
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
Соответствует существующей схеме конфигурации Cassandra
|
|||
|
|
Доступно всем сервисам через `add_cassandra_args()`
|
|||
|
|
Поддерживает конфигурацию как через командную строку, так и через переменные окружения
|
|||
|
|
|
|||
|
|
#### Изменение 4: Сервис конфигурации - Использование параметризованных пространств ключей
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/config/service/service.py`
|
|||
|
|
|
|||
|
|
**Строка 30** - Удалить жестко закодированное пространство ключей:
|
|||
|
|
```python
|
|||
|
|
# DELETE THIS LINE:
|
|||
|
|
keyspace = "config"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Строки 69-73** - Обновление разрешения конфигурации Cassandra:
|
|||
|
|
|
|||
|
|
**Текущая версия:**
|
|||
|
|
```python
|
|||
|
|
cassandra_host, cassandra_username, cassandra_password = \
|
|||
|
|
resolve_cassandra_config(params)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Исправлено:**
|
|||
|
|
```python
|
|||
|
|
cassandra_host, cassandra_username, cassandra_password, keyspace = \
|
|||
|
|
resolve_cassandra_config(params, default_keyspace="config")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
Обеспечивает обратную совместимость с "config" в качестве значения по умолчанию.
|
|||
|
|
Позволяет переопределить значение с помощью `--cassandra-keyspace` или `CASSANDRA_KEYSPACE`.
|
|||
|
|
|
|||
|
|
#### Изменение 5: Основные компоненты/Сервис знаний - Использование параметризованных пространств ключей.
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/cores/service.py`
|
|||
|
|
|
|||
|
|
**Строка 37** - Удалить жестко закодированное пространство ключей:
|
|||
|
|
```python
|
|||
|
|
# DELETE THIS LINE:
|
|||
|
|
keyspace = "knowledge"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновление разрешения конфигурации Cassandra** (в том же месте, что и служба конфигурации):
|
|||
|
|
```python
|
|||
|
|
cassandra_host, cassandra_username, cassandra_password, keyspace = \
|
|||
|
|
resolve_cassandra_config(params, default_keyspace="knowledge")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Изменение 6: Сервис библиотекаря - Использование параметризованных пространств ключей.
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/librarian/service.py`
|
|||
|
|
|
|||
|
|
**Строка 51** - Удалить жестко закодированное пространство ключей:
|
|||
|
|
```python
|
|||
|
|
# DELETE THIS LINE:
|
|||
|
|
keyspace = "librarian"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновление разрешения конфигурации Cassandra** (в том же месте, что и служба конфигурации):
|
|||
|
|
```python
|
|||
|
|
cassandra_host, cassandra_username, cassandra_password, keyspace = \
|
|||
|
|
resolve_cassandra_config(params, default_keyspace="librarian")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Часть 3: Перенос управления коллекциями в службу конфигураций
|
|||
|
|
|
|||
|
|
#### Обзор
|
|||
|
|
Перенесите коллекции из пространства ключей Cassandra librarian в хранилище службы конфигураций. Это устраняет жестко закодированные темы управления хранилищем и упрощает архитектуру, используя существующий механизм распространения конфигураций.
|
|||
|
|
|
|||
|
|
#### Текущая архитектура
|
|||
|
|
```
|
|||
|
|
API Request → Gateway → Librarian Service
|
|||
|
|
↓
|
|||
|
|
CollectionManager
|
|||
|
|
↓
|
|||
|
|
Cassandra Collections Table (librarian keyspace)
|
|||
|
|
↓
|
|||
|
|
Broadcast to 4 Storage Management Topics (hardcoded)
|
|||
|
|
↓
|
|||
|
|
Wait for 4+ Storage Service Responses
|
|||
|
|
↓
|
|||
|
|
Response to Gateway
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Новая архитектура
|
|||
|
|
```
|
|||
|
|
API Request → Gateway → Librarian Service
|
|||
|
|
↓
|
|||
|
|
CollectionManager
|
|||
|
|
↓
|
|||
|
|
Config Service API (put/delete/getvalues)
|
|||
|
|
↓
|
|||
|
|
Cassandra Config Table (class='collections', key='user:collection')
|
|||
|
|
↓
|
|||
|
|
Config Push (to all subscribers on config-push-queue)
|
|||
|
|
↓
|
|||
|
|
All Storage Services receive config update independently
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Изменение 7: Менеджер коллекций - Использование API сервиса конфигурации
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/librarian/collection_manager.py`
|
|||
|
|
|
|||
|
|
**Удалить:**
|
|||
|
|
Использование `LibraryTableStore` (строки 33, 40-41)
|
|||
|
|
Инициализация производителей управления хранилищем (строки 86-140)
|
|||
|
|
Метод `on_storage_response` (строки 400-430)
|
|||
|
|
Отслеживание `pending_deletions` (строки 57, 90-96 и использование во всем коде)
|
|||
|
|
|
|||
|
|
**Добавить:**
|
|||
|
|
Клиент сервиса конфигурации для вызовов API (шаблон запрос/ответ)
|
|||
|
|
|
|||
|
|
**Настройка клиента конфигурации:**
|
|||
|
|
```python
|
|||
|
|
# In __init__, add config request/response producers/consumers
|
|||
|
|
from trustgraph.schema.services.config import ConfigRequest, ConfigResponse
|
|||
|
|
|
|||
|
|
# Producer for config requests
|
|||
|
|
self.config_request_producer = Producer(
|
|||
|
|
client=pulsar_client,
|
|||
|
|
topic=config_request_queue,
|
|||
|
|
schema=ConfigRequest,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Consumer for config responses (with correlation ID)
|
|||
|
|
self.config_response_consumer = Consumer(
|
|||
|
|
taskgroup=taskgroup,
|
|||
|
|
client=pulsar_client,
|
|||
|
|
flow=None,
|
|||
|
|
topic=config_response_queue,
|
|||
|
|
subscriber=f"{id}-config",
|
|||
|
|
schema=ConfigResponse,
|
|||
|
|
handler=self.on_config_response,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Tracking for pending config requests
|
|||
|
|
self.pending_config_requests = {} # request_id -> asyncio.Event
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменить `list_collections` (строки 145-180):**
|
|||
|
|
```python
|
|||
|
|
async def list_collections(self, user, tag_filter=None, limit=None):
|
|||
|
|
"""List collections from config service"""
|
|||
|
|
# Send getvalues request to config service
|
|||
|
|
request = ConfigRequest(
|
|||
|
|
id=str(uuid.uuid4()),
|
|||
|
|
operation='getvalues',
|
|||
|
|
type='collections',
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Send request and wait for response
|
|||
|
|
response = await self.send_config_request(request)
|
|||
|
|
|
|||
|
|
# Parse collections from response
|
|||
|
|
collections = []
|
|||
|
|
for key, value_json in response.values.items():
|
|||
|
|
if ":" in key:
|
|||
|
|
coll_user, collection = key.split(":", 1)
|
|||
|
|
if coll_user == user:
|
|||
|
|
metadata = json.loads(value_json)
|
|||
|
|
collections.append(CollectionMetadata(**metadata))
|
|||
|
|
|
|||
|
|
# Apply tag filtering in-memory (as before)
|
|||
|
|
if tag_filter:
|
|||
|
|
collections = [c for c in collections if any(tag in c.tags for tag in tag_filter)]
|
|||
|
|
|
|||
|
|
# Apply limit
|
|||
|
|
if limit:
|
|||
|
|
collections = collections[:limit]
|
|||
|
|
|
|||
|
|
return collections
|
|||
|
|
|
|||
|
|
async def send_config_request(self, request):
|
|||
|
|
"""Send config request and wait for response"""
|
|||
|
|
event = asyncio.Event()
|
|||
|
|
self.pending_config_requests[request.id] = event
|
|||
|
|
|
|||
|
|
await self.config_request_producer.send(request)
|
|||
|
|
await event.wait()
|
|||
|
|
|
|||
|
|
return self.pending_config_requests.pop(request.id + "_response")
|
|||
|
|
|
|||
|
|
async def on_config_response(self, message, consumer, flow):
|
|||
|
|
"""Handle config response"""
|
|||
|
|
response = message.value()
|
|||
|
|
if response.id in self.pending_config_requests:
|
|||
|
|
self.pending_config_requests[response.id + "_response"] = response
|
|||
|
|
self.pending_config_requests[response.id].set()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменить `update_collection` (строки 182-312):**
|
|||
|
|
```python
|
|||
|
|
async def update_collection(self, user, collection, name, description, tags):
|
|||
|
|
"""Update collection via config service"""
|
|||
|
|
# Create metadata
|
|||
|
|
metadata = CollectionMetadata(
|
|||
|
|
user=user,
|
|||
|
|
collection=collection,
|
|||
|
|
name=name,
|
|||
|
|
description=description,
|
|||
|
|
tags=tags,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Send put request to config service
|
|||
|
|
request = ConfigRequest(
|
|||
|
|
id=str(uuid.uuid4()),
|
|||
|
|
operation='put',
|
|||
|
|
type='collections',
|
|||
|
|
key=f'{user}:{collection}',
|
|||
|
|
value=json.dumps(metadata.to_dict()),
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
response = await self.send_config_request(request)
|
|||
|
|
|
|||
|
|
if response.error:
|
|||
|
|
raise RuntimeError(f"Config update failed: {response.error.message}")
|
|||
|
|
|
|||
|
|
# Config service will trigger config push automatically
|
|||
|
|
# Storage services will receive update and create collections
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменить `delete_collection` (строки 314-398):**
|
|||
|
|
```python
|
|||
|
|
async def delete_collection(self, user, collection):
|
|||
|
|
"""Delete collection via config service"""
|
|||
|
|
# Send delete request to config service
|
|||
|
|
request = ConfigRequest(
|
|||
|
|
id=str(uuid.uuid4()),
|
|||
|
|
operation='delete',
|
|||
|
|
type='collections',
|
|||
|
|
key=f'{user}:{collection}',
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
response = await self.send_config_request(request)
|
|||
|
|
|
|||
|
|
if response.error:
|
|||
|
|
raise RuntimeError(f"Config delete failed: {response.error.message}")
|
|||
|
|
|
|||
|
|
# Config service will trigger config push automatically
|
|||
|
|
# Storage services will receive update and delete collections
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Формат метаданных коллекции:**
|
|||
|
|
Хранится в таблице конфигурации как: `class='collections', key='user:collection'`
|
|||
|
|
Значение является JSON-сериализованным объектом CollectionMetadata (без полей времени).
|
|||
|
|
Поля: `user`, `collection`, `name`, `description`, `tags`
|
|||
|
|
Пример: `class='collections', key='alice:my-docs', value='{"user":"alice","collection":"my-docs","name":"My Documents","description":"...","tags":["work"]}'`
|
|||
|
|
|
|||
|
|
#### Изменение 8: Сервис Librarian - Удаление инфраструктуры управления хранилищем
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/librarian/service.py`
|
|||
|
|
|
|||
|
|
**Удалить:**
|
|||
|
|
Модули-производители управления хранилищем (строки 173-190):
|
|||
|
|
`vector_storage_management_producer`
|
|||
|
|
`object_storage_management_producer`
|
|||
|
|
`triples_storage_management_producer`
|
|||
|
|
Потребитель ответов хранилища (строки 192-201)
|
|||
|
|
Обработчик `on_storage_response` (строки 467-473)
|
|||
|
|
|
|||
|
|
**Изменить:**
|
|||
|
|
Инициализация CollectionManager (строки 215-224) - удалить параметры модуля-производителя хранилища
|
|||
|
|
|
|||
|
|
**Примечание:** Внешний API для коллекций остается неизменным:
|
|||
|
|
`list-collections`
|
|||
|
|
`update-collection`
|
|||
|
|
`delete-collection`
|
|||
|
|
|
|||
|
|
#### Изменение 9: Удаление таблицы Collections из LibraryTableStore
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/tables/library.py`
|
|||
|
|
|
|||
|
|
**Удалить:**
|
|||
|
|
Оператор CREATE для таблицы Collections (строки 114-127)
|
|||
|
|
Подготовленные операторы для Collections (строки 205-240)
|
|||
|
|
Все методы для работы с коллекциями (строки 578-717):
|
|||
|
|
`ensure_collection_exists`
|
|||
|
|
`list_collections`
|
|||
|
|
`update_collection`
|
|||
|
|
`delete_collection`
|
|||
|
|
`get_collection`
|
|||
|
|
`create_collection`
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
Коллекции теперь хранятся в таблице конфигурации.
|
|||
|
|
Изменение, требующее обновления, приемлемо - миграция данных не требуется.
|
|||
|
|
Значительно упрощает сервис Librarian.
|
|||
|
|
|
|||
|
|
#### Изменение 10: Сервисы хранения - Управление коллекциями на основе конфигурации ✅ ВЫПОЛНЕНО
|
|||
|
|
|
|||
|
|
**Статус:** Все 11 хранилищ были перенесены на использование `CollectionConfigHandler`.
|
|||
|
|
|
|||
|
|
**Затронутые сервисы (всего 11):**
|
|||
|
|
Векторные представления документов: milvus, pinecone, qdrant
|
|||
|
|
Векторные представления графов: milvus, pinecone, qdrant
|
|||
|
|
Объектное хранилище: cassandra
|
|||
|
|
Хранилище троек: cassandra, falkordb, memgraph, neo4j
|
|||
|
|
|
|||
|
|
**Файлы:**
|
|||
|
|
`trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/graph_embeddings/milvus/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/graph_embeddings/pinecone/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/triples/falkordb/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/triples/memgraph/write.py`
|
|||
|
|
`trustgraph-flow/trustgraph/storage/triples/neo4j/write.py`
|
|||
|
|
|
|||
|
|
**Шаблон реализации (для всех сервисов):**
|
|||
|
|
|
|||
|
|
1. **Зарегистрировать обработчик конфигурации в `__init__`:**
|
|||
|
|
```python
|
|||
|
|
# Add after AsyncProcessor initialization
|
|||
|
|
self.register_config_handler(self.on_collection_config)
|
|||
|
|
self.known_collections = set() # Track (user, collection) tuples
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **Реализовать обработчик конфигурации:**
|
|||
|
|
```python
|
|||
|
|
async def on_collection_config(self, config, version):
|
|||
|
|
"""Handle collection configuration updates"""
|
|||
|
|
logger.info(f"Collection config version: {version}")
|
|||
|
|
|
|||
|
|
if "collections" not in config:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# Parse collections from config
|
|||
|
|
# Key format: "user:collection" in config["collections"]
|
|||
|
|
config_collections = set()
|
|||
|
|
for key in config["collections"].keys():
|
|||
|
|
if ":" in key:
|
|||
|
|
user, collection = key.split(":", 1)
|
|||
|
|
config_collections.add((user, collection))
|
|||
|
|
|
|||
|
|
# Determine changes
|
|||
|
|
to_create = config_collections - self.known_collections
|
|||
|
|
to_delete = self.known_collections - config_collections
|
|||
|
|
|
|||
|
|
# Create new collections (idempotent)
|
|||
|
|
for user, collection in to_create:
|
|||
|
|
try:
|
|||
|
|
await self.create_collection_internal(user, collection)
|
|||
|
|
self.known_collections.add((user, collection))
|
|||
|
|
logger.info(f"Created collection: {user}/{collection}")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Failed to create {user}/{collection}: {e}")
|
|||
|
|
|
|||
|
|
# Delete removed collections (idempotent)
|
|||
|
|
for user, collection in to_delete:
|
|||
|
|
try:
|
|||
|
|
await self.delete_collection_internal(user, collection)
|
|||
|
|
self.known_collections.discard((user, collection))
|
|||
|
|
logger.info(f"Deleted collection: {user}/{collection}")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Failed to delete {user}/{collection}: {e}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **Инициализация известных коллекций при запуске:**
|
|||
|
|
```python
|
|||
|
|
async def start(self):
|
|||
|
|
"""Start the processor"""
|
|||
|
|
await super().start()
|
|||
|
|
await self.sync_known_collections()
|
|||
|
|
|
|||
|
|
async def sync_known_collections(self):
|
|||
|
|
"""Query backend to populate known_collections set"""
|
|||
|
|
# Backend-specific implementation:
|
|||
|
|
# - Milvus/Pinecone/Qdrant: List collections/indexes matching naming pattern
|
|||
|
|
# - Cassandra: Query keyspaces or collection metadata
|
|||
|
|
# - Neo4j/Memgraph/FalkorDB: Query CollectionMetadata nodes
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **Рефакторинг существующих методов обработчиков:**
|
|||
|
|
```python
|
|||
|
|
# Rename and remove response sending:
|
|||
|
|
# handle_create_collection → create_collection_internal
|
|||
|
|
# handle_delete_collection → delete_collection_internal
|
|||
|
|
|
|||
|
|
async def create_collection_internal(self, user, collection):
|
|||
|
|
"""Create collection (idempotent)"""
|
|||
|
|
# Same logic as current handle_create_collection
|
|||
|
|
# But remove response producer calls
|
|||
|
|
# Handle "already exists" gracefully
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
async def delete_collection_internal(self, user, collection):
|
|||
|
|
"""Delete collection (idempotent)"""
|
|||
|
|
# Same logic as current handle_delete_collection
|
|||
|
|
# But remove response producer calls
|
|||
|
|
# Handle "not found" gracefully
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
5. **Удаление инфраструктуры управления хранилищем:**
|
|||
|
|
Удалить настройку и запуск `self.storage_request_consumer`
|
|||
|
|
Удалить настройку `self.storage_response_producer`
|
|||
|
|
Удалить метод диспетчера `on_storage_management`
|
|||
|
|
Удалить метрики для управления хранилищем
|
|||
|
|
Удалить импорты: `StorageManagementRequest`, `StorageManagementResponse`
|
|||
|
|
|
|||
|
|
**Особенности, специфичные для бэкенда:**
|
|||
|
|
|
|||
|
|
**Векторные хранилища (Milvus, Pinecone, Qdrant):** Отслеживать логическую `(user, collection)` в `known_collections`, но может создавать несколько бэкенд-коллекций для каждой размерности. Продолжить использование ленивой схемы создания. Операции удаления должны удалять все варианты размерностей.
|
|||
|
|
|
|||
|
|
**Объекты Cassandra:** Коллекции являются свойствами строк, а не структурами. Отслеживать информацию на уровне пространства ключей.
|
|||
|
|
|
|||
|
|
**Графовые хранилища (Neo4j, Memgraph, FalkorDB):** Запрашивать узлы `CollectionMetadata` при запуске. Создавать/удалять метаданные узлов при синхронизации.
|
|||
|
|
|
|||
|
|
**Тройки Cassandra:** Использовать API `KnowledgeGraph` для операций с коллекциями.
|
|||
|
|
|
|||
|
|
**Основные принципы проектирования:**
|
|||
|
|
|
|||
|
|
**Итоговая согласованность:** Отсутствует механизм запрос/ответ, конфигурация распространяется широковещанием.
|
|||
|
|
**Идемпотентность:** Все операции создания/удаления должны быть безопасными для повторной попытки.
|
|||
|
|
**Обработка ошибок:** Регистрировать ошибки, но не блокировать обновления конфигурации.
|
|||
|
|
**Самовосстановление:** Неудачные операции будут повторены при следующем распространении конфигурации.
|
|||
|
|
**Формат ключа коллекции:** `"user:collection"` в `config["collections"]`
|
|||
|
|
|
|||
|
|
#### Изменение 11: Обновление схемы коллекции - Удаление временных меток
|
|||
|
|
**Файл:** `trustgraph-base/trustgraph/schema/services/collection.py`
|
|||
|
|
|
|||
|
|
**Изменить CollectionMetadata (строки 13-21):**
|
|||
|
|
Удалить поля `created_at` и `updated_at`:
|
|||
|
|
```python
|
|||
|
|
class CollectionMetadata(Record):
|
|||
|
|
user = String()
|
|||
|
|
collection = String()
|
|||
|
|
name = String()
|
|||
|
|
description = String()
|
|||
|
|
tags = Array(String())
|
|||
|
|
# Remove: created_at = String()
|
|||
|
|
# Remove: updated_at = String()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменить класс CollectionManagementRequest (строки 25-47):**
|
|||
|
|
Удалить поля временной метки:
|
|||
|
|
```python
|
|||
|
|
class CollectionManagementRequest(Record):
|
|||
|
|
operation = String()
|
|||
|
|
user = String()
|
|||
|
|
collection = String()
|
|||
|
|
timestamp = String()
|
|||
|
|
name = String()
|
|||
|
|
description = String()
|
|||
|
|
tags = Array(String())
|
|||
|
|
# Remove: created_at = String()
|
|||
|
|
# Remove: updated_at = String()
|
|||
|
|
tag_filter = Array(String())
|
|||
|
|
limit = Integer()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
Временные метки не добавляют ценности для коллекций.
|
|||
|
|
Сервис конфигурации самостоятельно отслеживает версии.
|
|||
|
|
Упрощает схему и уменьшает объем хранения.
|
|||
|
|
|
|||
|
|
#### Преимущества миграции на сервис конфигурации
|
|||
|
|
|
|||
|
|
1. ✅ **Устраняет жестко закодированные темы управления хранилищем** - Решает проблему многопользовательской среды.
|
|||
|
|
2. ✅ **Более простая координация** - Нет сложного асинхронного ожидания ответов от 4+ хранилищ.
|
|||
|
|
3. ✅ **В конечном итоге согласованность** - Сервисы хранения обновляются независимо с помощью push-конфигурации.
|
|||
|
|
4. ✅ **Более высокая надежность** - Постоянная push-конфигурация против неперсистентного запроса/ответа.
|
|||
|
|
5. ✅ **Унифицированная модель конфигурации** - Коллекции рассматриваются как конфигурация.
|
|||
|
|
6. ✅ **Уменьшает сложность** - Удаляет около 300 строк кода для координации.
|
|||
|
|
7. ✅ **Готовность к многопользовательской среде** - Конфигурация уже поддерживает изоляцию пользователей с помощью пространств ключей.
|
|||
|
|
8. ✅ **Отслеживание версий** - Механизм версионирования сервиса конфигурации обеспечивает аудит.
|
|||
|
|
|
|||
|
|
## Замечания по реализации
|
|||
|
|
|
|||
|
|
### Обратная совместимость
|
|||
|
|
|
|||
|
|
**Изменения параметров:**
|
|||
|
|
Переименование параметров командной строки является серьезным изменением, но допустимо (функция в настоящее время не работает).
|
|||
|
|
Сервисы работают без параметров (используются значения по умолчанию).
|
|||
|
|
Значения по умолчанию для пространств ключей сохранены: "config", "knowledge", "librarian".
|
|||
|
|
Значение по умолчанию для очереди: `persistent://tg/config/config`
|
|||
|
|
|
|||
|
|
**Управление коллекциями:**
|
|||
|
|
**Серьезное изменение:** Таблица коллекций удалена из пространства ключей librarian.
|
|||
|
|
**Миграция данных не предусмотрена** - приемлемо для этой фазы.
|
|||
|
|
Внешний API для коллекций не изменен (операции list/update/delete).
|
|||
|
|
Формат метаданных коллекций упрощен (временные метки удалены).
|
|||
|
|
|
|||
|
|
### Требования к тестированию
|
|||
|
|
|
|||
|
|
**Тестирование параметров:**
|
|||
|
|
1. Проверить, работает ли параметр `--config-push-queue` для сервиса graph-embeddings.
|
|||
|
|
2. Проверить, работает ли параметр `--config-push-queue` для сервиса text-completion.
|
|||
|
|
3. Проверить, работает ли параметр `--config-push-queue` для сервиса конфигурации.
|
|||
|
|
4. Проверить, работает ли параметр `--cassandra-keyspace` для сервиса конфигурации.
|
|||
|
|
5. Проверить, работает ли параметр `--cassandra-keyspace` для сервиса cores.
|
|||
|
|
6. Проверить, работает ли параметр `--cassandra-keyspace` для сервиса librarian.
|
|||
|
|
7. Проверить, работают ли сервисы без параметров (используются значения по умолчанию).
|
|||
|
|
8. Проверить развертывание в многопользовательской среде с использованием пользовательских имен очередей и пространств ключей.
|
|||
|
|
|
|||
|
|
**Тестирование управления коллекциями:**
|
|||
|
|
9. Проверить операцию `list-collections` через сервис конфигурации.
|
|||
|
|
10. Проверить, создает ли `update-collection` записи/обновления в таблице конфигурации.
|
|||
|
|
11. Проверить, удаляет ли `delete-collection` записи из таблицы конфигурации.
|
|||
|
|
12. Проверить, запускается ли push-конфигурации при обновлениях коллекций.
|
|||
|
|
13. Проверить, работает ли фильтрация по тегам с использованием хранилища на основе конфигурации.
|
|||
|
|
14. Проверить, работают ли операции с коллекциями без полей временных меток.
|
|||
|
|
|
|||
|
|
### Пример развертывания в многопользовательской среде
|
|||
|
|
```bash
|
|||
|
|
# Tenant: tg-dev
|
|||
|
|
graph-embeddings \
|
|||
|
|
-p pulsar+ssl://broker:6651 \
|
|||
|
|
--pulsar-api-key <KEY> \
|
|||
|
|
--config-push-queue persistent://tg-dev/config/config
|
|||
|
|
|
|||
|
|
config-service \
|
|||
|
|
-p pulsar+ssl://broker:6651 \
|
|||
|
|
--pulsar-api-key <KEY> \
|
|||
|
|
--config-push-queue persistent://tg-dev/config/config \
|
|||
|
|
--cassandra-keyspace tg_dev_config
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Анализ влияния
|
|||
|
|
|
|||
|
|
### Сервисы, затронутые изменением 1-2 (Переименование параметра CLI)
|
|||
|
|
Все сервисы, наследующие AsyncProcessor или FlowProcessor:
|
|||
|
|
config-service
|
|||
|
|
cores-service
|
|||
|
|
librarian-service
|
|||
|
|
graph-embeddings
|
|||
|
|
document-embeddings
|
|||
|
|
text-completion-* (все провайдеры)
|
|||
|
|
extract-* (все экстракторы)
|
|||
|
|
query-* (все сервисы запросов)
|
|||
|
|
retrieval-* (все сервисы RAG)
|
|||
|
|
storage-* (все сервисы хранения)
|
|||
|
|
И еще 20+ сервисов
|
|||
|
|
|
|||
|
|
### Сервисы, затронутые изменениями 3-6 (Keyspace Cassandra)
|
|||
|
|
config-service
|
|||
|
|
cores-service
|
|||
|
|
librarian-service
|
|||
|
|
|
|||
|
|
### Сервисы, затронутые изменениями 7-11 (Управление коллекциями)
|
|||
|
|
|
|||
|
|
**Немедленные изменения:**
|
|||
|
|
librarian-service (collection_manager.py, service.py)
|
|||
|
|
tables/library.py (удаление таблицы collections)
|
|||
|
|
schema/services/collection.py (удаление метки времени)
|
|||
|
|
|
|||
|
|
**Выполненные изменения (изменение 10):** ✅
|
|||
|
|
Все сервисы хранения (всего 11) - перенесены на push-конфигурацию для обновлений коллекций через `CollectionConfigHandler`
|
|||
|
|
Схема управления хранилищем удалена из `storage.py`
|
|||
|
|
|
|||
|
|
## Будущие соображения
|
|||
|
|
|
|||
|
|
### Модель keyspace на уровне пользователя
|
|||
|
|
|
|||
|
|
Некоторые сервисы динамически используют **keyspace на уровне пользователя**, когда каждый пользователь получает свой собственный keyspace Cassandra:
|
|||
|
|
|
|||
|
|
**Сервисы с keyspace на уровне пользователя:**
|
|||
|
|
1. **Сервис запросов Triple** (`trustgraph-flow/trustgraph/query/triples/cassandra/service.py:65`)
|
|||
|
|
Использует `keyspace=query.user`
|
|||
|
|
2. **Сервис запросов объектов** (`trustgraph-flow/trustgraph/query/objects/cassandra/service.py:479`)
|
|||
|
|
Использует `keyspace=self.sanitize_name(user)`
|
|||
|
|
3. **Прямой доступ к графу знаний** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py:18`)
|
|||
|
|
Параметр по умолчанию `keyspace="trustgraph"`
|
|||
|
|
|
|||
|
|
**Статус:** Эти параметры **не изменяются** в этой спецификации.
|
|||
|
|
|
|||
|
|
**Требуется дальнейший анализ:**
|
|||
|
|
Оценить, создает ли модель keyspace на уровне пользователя проблемы изоляции арендаторов
|
|||
|
|
Рассмотреть, нужны ли многопользовательским развертываниям префиксы keyspace (например, `tenant_a_user1`)
|
|||
|
|
Проверить на потенциальные конфликты идентификаторов пользователей между арендаторами
|
|||
|
|
Оценить, является ли единый общий keyspace на арендатора с изоляцией строк на уровне пользователя более предпочтительным
|
|||
|
|
|
|||
|
|
**Примечание:** Это не блокирует текущую многопользовательскую реализацию, но должно быть рассмотрено перед производственными многопользовательскими развертываниями.
|
|||
|
|
|
|||
|
|
## Этапы реализации
|
|||
|
|
|
|||
|
|
### Этап 1: Исправление параметров (изменения 1-6)
|
|||
|
|
Исправить именование параметра `--config-push-queue`
|
|||
|
|
Добавить поддержку параметра `--cassandra-keyspace`
|
|||
|
|
**Результат:** Многопользовательская очередь и конфигурация keyspace включены
|
|||
|
|
|
|||
|
|
### Этап 2: Миграция управления коллекциями (изменения 7-9, 11)
|
|||
|
|
Перенести хранение коллекций в сервис конфигурации
|
|||
|
|
Удалить таблицу collections из librarian
|
|||
|
|
Обновить схему коллекций (удалить метки времени)
|
|||
|
|
**Результат:** Устраняет жестко закодированные темы управления хранилищем, упрощает librarian
|
|||
|
|
|
|||
|
|
### Этап 3: Обновления сервисов хранения (изменение 10) ✅ ВЫПОЛНЕНО
|
|||
|
|
Обновлены все сервисы хранения для использования push-конфигурации для коллекций через `CollectionConfigHandler`
|
|||
|
|
Удалена инфраструктура запросов/ответов управления хранилищем
|
|||
|
|
Удалены устаревшие определения схемы
|
|||
|
|
**Результат:** Достигнуто полное управление коллекциями на основе конфигурации
|
|||
|
|
|
|||
|
|
## Ссылки
|
|||
|
|
GitHub Issue: https://github.com/trustgraph-ai/trustgraph/issues/582
|
|||
|
|
Связанные файлы:
|
|||
|
|
`trustgraph-base/trustgraph/base/async_processor.py`
|
|||
|
|
`trustgraph-base/trustgraph/base/cassandra_config.py`
|
|||
|
|
`trustgraph-base/trustgraph/schema/core/topic.py`
|
|||
|
|
`trustgraph-base/trustgraph/schema/services/collection.py`
|
|||
|
|
`trustgraph-flow/trustgraph/config/service/service.py`
|
|||
|
|
`trustgraph-flow/trustgraph/cores/service.py`
|
|||
|
|
`trustgraph-flow/trustgraph/librarian/service.py`
|
|||
|
|
`trustgraph-flow/trustgraph/librarian/collection_manager.py`
|
|||
|
|
`trustgraph-flow/trustgraph/tables/library.py`
|