fix: concurrent delete race conditions in delete_entity (#702)

Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Hernandez
2026-03-30 19:57:30 -05:00
committed by GitHub
parent 7696fca826
commit cfa70004be
2 changed files with 134 additions and 2 deletions
+21 -2
View File
@@ -735,6 +735,10 @@ class EntityService(BaseService[EntityModel]):
entity = await self.get_by_permalink(permalink_or_id)
else:
entities = await self.get_entities_by_id([permalink_or_id])
if len(entities) == 0:
# Entity already deleted (concurrent delete or race condition)
logger.info("Entity already deleted", entity_id=permalink_or_id)
return True
if len(entities) != 1: # pragma: no cover
logger.error(
"Entity lookup error", entity_id=permalink_or_id, found_count=len(entities)
@@ -746,13 +750,28 @@ class EntityService(BaseService[EntityModel]):
# Delete from search index first (if search_service is available)
if self.search_service:
await self.search_service.handle_delete(entity)
try:
await self.search_service.handle_delete(entity)
except Exception:
# Search cleanup is best-effort during concurrent deletes.
# Relationships may have been cascade-deleted by a concurrent request.
logger.warning(
"Search cleanup failed for entity (likely concurrent delete)",
permalink_or_id=permalink_or_id,
exc_info=True,
)
# Delete file
await self.file_service.delete_entity_file(entity)
# Delete from DB (this will cascade to observations/relations)
return await self.repository.delete(entity.id)
# Trigger: repository.delete returns False when entity is already gone (NoResultFound)
# Why: concurrent delete_directory requests can race to delete the same entity
# Outcome: treat as success since the entity is deleted either way
deleted = await self.repository.delete(entity.id)
if not deleted:
logger.info("Entity already removed from DB", entity_id=permalink_or_id)
return True
except EntityNotFoundError:
logger.info(f"Entity not found: {permalink_or_id}")