Implement ownership model for all cached content deletion
- cache_service.delete_content: Remove user's ownership link first, only delete actual file if no other owners remain - cache_manager.discard_activity_outputs_only: Check if outputs and intermediates are used by other activities before deleting - run_service.discard_run: Now cleans up run outputs/intermediates (only if not shared by other runs) - home.py clear_user_data: Use ownership model for effects and media deletion instead of directly deleting files The ownership model ensures: 1. Multiple users can "own" the same cached content 2. Deleting removes the user's ownership link (item_types entry) 3. Actual files only deleted when no owners remain (garbage collection) 4. Shared intermediates between runs are preserved Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -132,35 +132,55 @@ async def clear_user_data(request: Request):
|
||||
except Exception as e:
|
||||
errors.append(f"Failed to list recipes: {e}")
|
||||
|
||||
# Delete all effects
|
||||
# Delete all effects (uses ownership model)
|
||||
try:
|
||||
cache_manager = get_cache_manager()
|
||||
effects_dir = Path(cache_manager.cache_dir) / "_effects"
|
||||
if effects_dir.exists():
|
||||
import shutil
|
||||
for effect_dir in effects_dir.iterdir():
|
||||
if effect_dir.is_dir():
|
||||
try:
|
||||
shutil.rmtree(effect_dir)
|
||||
deleted["effects"] += 1
|
||||
except Exception as e:
|
||||
errors.append(f"Effect {effect_dir.name}: {e}")
|
||||
# Get user's effects from item_types
|
||||
effect_items = await database.get_user_items(actor_id, item_type="effect", limit=10000)
|
||||
for item in effect_items:
|
||||
cid = item.get("cid")
|
||||
if cid:
|
||||
try:
|
||||
# Remove ownership link
|
||||
await database.delete_item_type(cid, actor_id, "effect")
|
||||
await database.delete_friendly_name(actor_id, cid)
|
||||
|
||||
# Check if orphaned
|
||||
remaining = await database.get_item_types(cid)
|
||||
if not remaining:
|
||||
# Garbage collect
|
||||
effects_dir = Path(cache_manager.cache_dir) / "_effects" / cid
|
||||
if effects_dir.exists():
|
||||
import shutil
|
||||
shutil.rmtree(effects_dir)
|
||||
import ipfs_client
|
||||
ipfs_client.unpin(cid)
|
||||
deleted["effects"] += 1
|
||||
except Exception as e:
|
||||
errors.append(f"Effect {cid[:16]}...: {e}")
|
||||
except Exception as e:
|
||||
errors.append(f"Failed to delete effects: {e}")
|
||||
|
||||
# Delete all media/cache items for user
|
||||
# Delete all media/cache items for user (uses ownership model)
|
||||
try:
|
||||
items = await database.get_user_items(actor_id, limit=10000)
|
||||
for item in items:
|
||||
try:
|
||||
from ..services.cache_service import CacheService
|
||||
cache_service = CacheService(database, cache_manager)
|
||||
|
||||
# Get user's media items (video, image, audio)
|
||||
for media_type in ["video", "image", "audio", "unknown"]:
|
||||
items = await database.get_user_items(actor_id, item_type=media_type, limit=10000)
|
||||
for item in items:
|
||||
cid = item.get("cid")
|
||||
if cid:
|
||||
await database.delete_cache_item(cid)
|
||||
deleted["media"] += 1
|
||||
except Exception as e:
|
||||
errors.append(f"Media {item.get('cid', 'unknown')}: {e}")
|
||||
try:
|
||||
success, error = await cache_service.delete_content(cid, actor_id)
|
||||
if success:
|
||||
deleted["media"] += 1
|
||||
elif error:
|
||||
errors.append(f"Media {cid[:16]}...: {error}")
|
||||
except Exception as e:
|
||||
errors.append(f"Media {cid[:16]}...: {e}")
|
||||
except Exception as e:
|
||||
errors.append(f"Failed to list media: {e}")
|
||||
errors.append(f"Failed to delete media: {e}")
|
||||
|
||||
logger.info(f"Cleared data for {actor_id}: {deleted}")
|
||||
if errors:
|
||||
|
||||
Reference in New Issue
Block a user