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, стабилизирует ваши скрипты загрузки и сделает многопоточные нагрузки предсказуемыми.