Files
tf_provider/HISTORY/2026-07-06_architectural_refactoring.md
T

179 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-07-06 — Архитектурный рефакторинг tf_provider
## Контекст
После анализа Claude Opus (`HISTORY/OPUS/2026-07-06_architectural_analysis.md`) выявлены архитектурные проблемы и выполнены исправления.
## Выполненные изменения
### 1. Реструктуризация проекта
```
Было: Стало:
devops/ TOOLS/scripts/ (все .sh)
devops/profiles/ TOOLS/config/ (profile.env, services_list, timeouts)
devops/ARCHITECTURE.md TOOLS/ARCHITECTURE.md
devops/config/ УДАЛЕНО (дубликат profiles)
provider/resources_yaml/ УДАЛЕНО (сгенерированное → generated/)
provider/internal/resources_gen/ УДАЛЕНО (сгенерированное → generated/)
generated/{test,prod,dev}/ (вывод пайплайна)
```
### 2. Независимость генераторов
- `yaml-generator` — требует `NUBES_OUTPUT_DIR`, без default в `provider/`
- `resource-generator` — требует `NUBES_RESOURCES_DIR` + `NUBES_RESOURCES_GEN_DIR`
- `docs-generator` — требует `--resources`, `--docs`, `--version` флаги
- Удалён `detectVersion()` — больше не читает `provider/main.go`
- Удалён `detectRoot()`, `pickPath()` — нет хардкод-путей
### 3. Общая библиотека типов
Создан `TOOLS/lib/` — единый YAML-контракт. Модуль: `tf-tools/lib`.
Подключены:
- `yaml-generator` → type alias `ParamSpec = lib.ParamSpec` и др.
- `resource-generator` → type alias `OutputParam`, `OperationSpec`, `ParamSpec`
НЕ подключён:
- `docs-generator` — будет заменён на LLM-генератор, не трогаем
При добавлении поля в YAML — править `lib/types.go`, несогласованность ловится компилятором.
### 4. Синхронизация версий
- `provider/main.go`: 5.0.75 → 5.0.60 (соответствует последней сборке)
- `profile.env`: три переменные → одна `VERSION`
- `03_build`, `04_publish`: `${PROVIDER_VERSION:-${RELEASE_VERSION:-}}``${VERSION}`
### 5. Автосборка бинарников
`01_generate_yamls.sh`: если исходники новее бинарника — пересборка.
### 6. Удалённый мусор
- `devops/config/` — дубликат profiles
- `02_generate_resources_and_docs.sh` + `_template.sh` — legacy, заменены v2
- `cloud-dashboard/`, `tools/` (root), `internal/` (root), `universal_rebuild/`
- 18 одноразовых файлов (check_ops.py, s.sh, ...)
### 7. Слияние ops-generator → docs-generator
`--ops` флаг. Три генератора вместо четырёх.
### 8. Документация LLM
`docs/LLM_DOCS_GENERATION.md` — подход, промпт, тест на Postgres (gpt-oss-120b, 9/10).
## Текущее состояние
```
tf_provider/
├── TOOLS/ — всё для генерации (код + скрипты + настройки)
│ ├── yaml-generator/
│ ├── resource-generator/
│ ├── docs-generator/
│ ├── lib/ — общие YAML-типы
│ ├── scripts/
│ ├── config/{test,prod,dev}/
│ └── ARCHITECTURE.md
├── provider/ — только исходники
├── generated/ — вывод пайплайна (gitignored)
└── docs/ — документация
```
## Осталось
- [x] yaml-generator → lib
- [ ] resource-generator → lib
- [ ] docs-generator → lib
---
## ⚠️ BUG: modify падает с 500 — GET ?fields=cfsParams вызывает getResourceRealmConfig
**Симптом:** `terraform apply` при modify возвращает:
```
не удалось получить детали операции: ошибка API 500: Invalid call of the function [getResourceRealmConfig], first Argument [resourceRealm] is of invalid type, Cannot cast Object type [Struct] to a value of type [string]
```
**Причина:** `RunInstanceOperationUniversalWithDefaults` (client.go:384) делает GET `/instanceOperations/{opUid}?fields=cfsParams`. Бэкенд (ColdFusion) при вычислении cfsParams вызывает `getResourceRealmConfig` через `expression_parser.cfc:184`. Если `resourceRealm` в контексте инстанса — Struct (объект), а не string, функция падает.
**Где проявляется:**
- `client.go:420``RunInstanceOperationUniversalWithDefaults` (modify/create/delete с дефолтами)
- `client.go:1399``RunInstanceOperationUniversalByCode` (операции по code)
- `client.go:224``CreateGenericInstanceUniversalV6` (CREATE flow) — работает на новых инстансах
**Почему на одних инстансах падает, на других нет:**
- Новые инстансы: `resourceRealm` = string → OK
- Старые/модифицированные: `resourceRealm` = Struct → 500
**Исправление (v5.0.62):**
- `crud.go:58`: `UpdateResourceWithTimeout``RunInstanceOperationUniversal` (без GET)
- GET не нужен для modify — все параметры уже известны из конфига
**Статус:** ✅ FIXED v5.0.62
**Файлы:**
- `/home/naeel/tf_provider/provider/internal/resources_core/crud.go:58`
- `/home/naeel/tf_provider/provider/internal/core/client.go:384` (WithDefaults — не используется в modify)
**Тесты (v5.0.62):**
- ✅ CREATE postgres — OK
- ✅ MODIFY postgres (replicas, backup_config, access_config) — OK
- ✅ CREATE/DELETE user — OK
- ✅ CREATE/DELETE database — OK
- ✅ Invalid role → 400 c понятным сообщением — OK
- ✅ No-change plan (идемпотентность) — OK
**Остаётся риск:**
- `RunInstanceOperationUniversalByCode` (client.go:1399) всё ещё делает GET — может упасть для service_operation_resource на проблемных инстансах
- `CreateGenericInstanceUniversalV6` (client.go:224) — CREATE на инстансах где resourceRealm = Struct может упасть
---
## ⚠️ BUG: Параллельные операции на одном инстансе ломаются
**Симптом:** При создании нескольких subresource'ов (user, database) на одном postgres-инстансе:
- `ошибка API 500: Cannot connect to the orchestrator` — бэкенд не справляется с параллельными запросами
- `операция завершилась успешно, но объект не найден в state_out` — user создался, но RefreshResourceState не видит его
- `экземпляр не готов: операция в ожидании` — инстанс занят предыдущей операцией
**Причина:** Terraform по умолчанию параллелит до 10 ресурсов (`-parallelism=10`). Все операции на одном инстансе (5 user'ов + 7 database'ов) стартуют одновременно → гонка на бэкенде.
**Пример:** 2026-07-07 на инстансе `5111108D`:
- 5 user'ов создавались параллельно: user_2 ok, user_1+user_3 — orchestrator error, user_4+user_5 — state_out не найден
- 7 database'ов упали: инстанс не готов
**План исправления:** mutex map в провайдере (стандартный подход как в AWS/GCP провайдерах):
```go
// client.go — глобальная карта мьютексов
var instanceMutexes sync.Map // key: instanceUid
func (c *UniversalClient) lockInstance(instanceUid string) func() {
mu, _ := c.instanceMutexes.LoadOrStore(instanceUid, &sync.Mutex{})
mu.(*sync.Mutex).Lock()
return func() { mu.(*sync.Mutex).Unlock() }
}
```
Вызывать в каждом CRUD:
```go
unlock := client.lockInstance(instanceID)
defer unlock()
```
**Альтернативы (хуже):**
- `depends_on` цепочкой в конфиге — неудобно, требует ручной правки .tf
- `-parallelism=1` — замедляет ВСЕ ресурсы, не только subresource'ы
**Затрагивает:**
- Все subresource-операции (postgres_user, postgres_database, mariadb_user, clickhouse_user, etc.)
- Modify + delete на одном инстансе тоже могут столкнуться
**Статус:** ⚠️ OPEN — не исправлено
**Файлы для правки:**
- `/home/naeel/tf_provider/provider/internal/core/client.go` — добавить mutex map + lockInstance
- `/home/naeel/tf_provider/provider/internal/resources_core/crud.go` — добавить lock/unlock в Create/Update/Delete
- `/home/naeel/tf_provider/provider/internal/resources_core/service_operation_resource.go` — добавить lock/unlock