fix: use SAVEPOINT instead of full rollback in sync user creation

Full db.rollback() in _get_or_create_bot_user_from_panel expires ALL
ORM objects in the session, causing MissingGreenlet errors when
subsequent sync iterations access user attributes from synchronous code.

Replace with begin_nested() (SAVEPOINT) so only the failed INSERT is
rolled back while the parent transaction and all cached objects remain
valid.
This commit is contained in:
Fringg
2026-02-27 21:32:10 +03:00
parent b47678cfb0
commit 2a90f871b9
+8 -11
View File
@@ -502,19 +502,18 @@ class RemnaWaveService:
language='ru',
)
db_user = await create_user_no_commit(**create_kwargs)
# Используем SAVEPOINT чтобы при IntegrityError откатить только
# вложенную транзакцию, а не всю сессию. Полный rollback помечает
# ВСЕ объекты сессии как expired, что вызывает MissingGreenlet
# при последующем sync-доступе к атрибутам ORM-объектов.
async with db.begin_nested():
db_user = await create_user_no_commit(**create_kwargs)
return db_user, True
except IntegrityError as create_error:
logger.info(
'♻️ Пользователь с telegram_id уже существует. Используем существующую запись.', telegram_id=telegram_id
)
try:
await db.rollback()
except Exception:
# create_user_no_commit уже выполняет rollback при необходимости
pass
try:
existing_user = await get_user_by_telegram_id(db, telegram_id)
if existing_user is None:
@@ -535,13 +534,11 @@ class RemnaWaveService:
)
return None, False
except Exception as general_error:
# SAVEPOINT (begin_nested) уже откатил частичную работу.
# Полный rollback не нужен — он бы пометил все объекты сессии expired.
logger.error(
'❌ Общая ошибка создания/загрузки пользователя', telegram_id=telegram_id, general_error=general_error
)
try:
await db.rollback()
except:
pass
return None, False
async def get_system_statistics(self) -> dict[str, Any]: