# 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