2025, Dec 05 15:02

Как исправить ошибку GSException 'Container not found' в GridDB Cloud после создания контейнера

GSException 'Container not found' в GridDB Cloud после создания: почему и как чинить — краткая пауза или повторы с экспоненциальным backoff в griddb_python.

Случайные ошибки «Container not found» сразу после создания контейнера в GridDB Cloud способны сорвать казалось бы простые скрипты загрузки. Если вы используете griddb_python, чтобы создать контейнер TIME_SERIES и тут же прочитать его обратно, время от времени может появляться GSException с сообщением, что контейнер не существует, хотя в веб‑интерфейсе он уже виден. При параллельном выполнении это заметнее.

Воспроизведение проблемы

Ниже приведена минимальная последовательность: она создаёт контейнер с именем device_metrics и затем пытается получить к нему дескриптор:

import griddb
# Предположим, grid_store — корректный экземпляр GSStore
grid_store.put_container(
    "device_metrics",
    [
        ("timestamp", griddb.Type.TIMESTAMP),
        ("device_id", griddb.Type.STRING),
        ("temperature", griddb.Type.FLOAT)
    ],
    griddb.ContainerType.TIME_SERIES,
    True
)
metrics_ts = grid_store.get_container("device_metrics")

В некоторых запусках, особенно при работе нескольких потоков, последняя строка может привести к исключению:

griddb.gsexception.GSException: [PARTIAL_EXECUTION(0x0303)] Container not found. (Container name='device_metrics')

В чём дело

Такое поведение укладывается в короткое «окно» распространения метаданных после создания контейнера. Контейнер уже существует, но последующие вызовы get_container или get_container_info могут на мгновение провалиться, пока система приходит к согласованному состоянию. Поскольку это проявляется нерегулярно и чаще при конкурентных сценариях, безопаснее явно обработать этот небольшой промежуток.

Практическое решение

Самый прямой способ избежать гонки — добавить небольшую задержку после создания перед получением дескриптора контейнера:

import time
import griddb
# Предположим, gs_handle — корректный экземпляр GSStore
gs_handle.put_container(
    "device_metrics",
    [
        ("timestamp", griddb.Type.TIMESTAMP),
        ("device_id", griddb.Type.STRING),
        ("temperature", griddb.Type.FLOAT)
    ],
    griddb.ContainerType.TIME_SERIES,
    True
)
# Короткая пауза, чтобы метаданные успели распространиться
time.sleep(1)
metrics_container = gs_handle.get_container("device_metrics")

Если требуется более надёжный подход, используйте ограниченные повторы с экспоненциальной задержкой. Это избавляет от жёстко заданной фиксированной паузы и лучше справляется с редкими всплесками времени распространения:

import time
# Предположим, db_store — корректный экземпляр GSStore
def poll_for_container(db_store, cont_name, attempts=5, base_delay=0.5):
    for idx in range(attempts):
        ref = db_store.get_container(cont_name)
        if ref is not None:
            return ref
        time.sleep(base_delay * (2 ** idx))
    raise RuntimeError(f"Container '{cont_name}' not found after retries.")
metrics_ref = poll_for_container(db_store, "device_metrics")

Почему это важно

Конвейеры данных, скрипты подготовки и тесты часто создают контейнеры на лету и сразу начинают с ними работать. Без небольшого ожидания или политики повторов вы получаете нестабильное поведение, которое трудно диагностировать, особенно при параллельном исполнении. Явная обработка короткого интервала распространения превращает хрупкий процесс в предсказуемый.

Выводы

Если вы создаёте контейнер в GridDB Cloud и хотите обратиться к нему сразу, учтите короткое окно распространения. Для простых случаев достаточно короткого sleep; для многопоточных или автоматизированных запусков по умолчанию безопаснее использовать ограниченный экспоненциальный backoff. Это подавит ложные ошибки GSException, стабилизирует ваши скрипты загрузки и сделает многопоточные нагрузки предсказуемыми.