Files
tf_provider/HISTORY/OPUS/2026-07-07_opus_answers_duplicate_fix.md
T

5.1 KiB

2026-07-07 — Opus answers: duplicate/exist fix strategy

Контекст

Промпт из /home/naeel/tf_provider/prompt_for_opus_duplicate.md После mutex (v5.0.63) pg_user_5 упал: «Операция вернула duplicate/exist, но объект не найден в state_out»

Корень всех проблем

Код путает два разных случая «не-found»:

  • known && !found — список в state_out ЕСТЬ, объекта нет → реальная несогласованность
  • !known — списка в state_out НЕТ вообще (PostgreSQL, Kafka) → проверить невозможно

Сейчас !known трактуется как несогласованность → ошибка.


Вопрос 1 (ПРИОРИТЕТ) — ДА, доверять API при !known

Где: templates.go → генерится во все subresource Create

Фикс: усыновлять при found || !known, ошибку только для known && !found:

if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) {
    found, known := false, false
    if targetValue != "" {
        var checkErr error
        found, known, checkErr = resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
        if checkErr != nil {
            resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error())
            return
        }
    }
    // found         → подтверждено в state_out
    // !known        → списка в state_out нет (PG/Kafka) → доверяем API
    // known&&!found → реальная несогласованность
    if found || !known {
        plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "{{.SubName}}", idParams))
        resp.Diagnostics.AddWarning("Подресурс усыновлён", "API вернул already exists; объект принят в state: "+targetValue)
        resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
        return
    }
    resp.Diagnostics.AddError("Нарушена консистентность", "API вернул duplicate/exist, но объект подтверждённо отсутствует в state_out: "+targetValue)
    return
}

Вопрос 2 — ДА, нужен явный маппинг из метаданных

Где: subresource_guard.go — SubresourceListKey (эвристика name+"s") и SubresourceIdentityKey (special-map на 2 сервиса)

Доказательство: Kafka — реальные ключи kafkaUsers/kafkaTopics, эвристика даёт users/topicsknown=false даже когда данные есть.

Фикс: генератор должен эмитить явные listKey/idKey как константы из метаданных. Для сервисов без ключа в state_out (PostgreSQL) — эмитить listKey=""FindSubresourceInStateOutknown=false → работает путь «доверять API» из Вопроса 1.


Вопрос 3 — НЕТ, list-эндпоинтов не существует

Проверено по API-метаданным (svc 90):

  • Операции: create(19), delete(20), resume(114), suspend(115), recovery(47), modify(48), restart(54)
  • Subresource: create_user(241), delete_user(244), create_database(245), delete_database(246)
  • Ни одной list_*/get_*/show_*

Перечислить подресурсы через API невозможно. Единственный источник — state_out, а у PG его нет.

Вывод: стратегия «доверять API» (Вопрос 1) — единственно возможная.


Вопрос 4 — Частично; текст ОК, нужен HTTP 409

Где: subresource_guard.go — IsSubresourceAlreadyExistsError

  • role "pg_user_5" already exists → ловится
  • already exist (без s), голый exist → НЕ ловится
  • Главная дыра: HTTP 409 без текста → НЕ ловится

Фикс:

markers := []string{"уже существует", "already exists", "already exist", "duplicate", "conflict", " exist"}

Плюс проверка HTTP 409 (требует проброса статус-кода из клиента).


Приоритет реализации

  1. Вопрос 1 — чинит pg_user_5 сразу
  2. Вопрос 2 — чинит Kafka и остальные
  3. Вопрос 4 — устойчивость к формулировкам
  4. Вопрос 3 — документировать как «невозможно, дизайн-решение»

Всё правится в шаблоне templates.go и subresource_guard.go. Сгенерированные файлы НЕ править — перегенерировать.

Дата: 2026-07-07 Статус: анализ завершён, правки НЕ внесены