Shoraky commited on
Commit
6809da8
·
1 Parent(s): a80fddd

Return session deletion before storage cleanup

Browse files
Files changed (1) hide show
  1. app.py +38 -18
app.py CHANGED
@@ -26,11 +26,11 @@ import cv2
26
  from email.message import Message
27
  from email.utils import collapse_rfc2231_value
28
  from dotenv import load_dotenv
29
- from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
30
  from fastapi.responses import FileResponse, JSONResponse
31
  from huggingface_hub import HfApi, hf_hub_download, snapshot_download
32
  from huggingface_hub.errors import RemoteEntryNotFoundError
33
- from storage_listing import session_files, session_folder_exists
34
 
35
  load_dotenv(Path(__file__).with_name(".env"))
36
  jobs = {}
@@ -731,15 +731,38 @@ def cancel_session_job(session_id: UUID):
731
  job["done"].wait(timeout=300)
732
  if not job["done"].is_set():
733
  raise HTTPException(504, "The analysis upload did not stop in time; deletion was not started")
734
- work_path = job.get("workPath")
 
 
 
 
735
  if work_path:
736
  shutil.rmtree(work_path, ignore_errors=True)
737
- try:
738
- for path in (f"colab-staging/{client_id}", f"colab-results/{client_id}"):
739
- hub().delete_folder(repo_id=os.environ["STORAGE_REPO_ID"], repo_type="dataset", path_in_repo=path,
740
- commit_message=f"Clean up cancelled analysis {client_id}")
741
- except Exception:
742
- logger.exception("Unable to clean cancelled analysis staging files: %s", client_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
743
 
744
 
745
  def session_row(session_id, user_id, is_admin=False):
@@ -858,22 +881,19 @@ def update_action(session_id: UUID, action_id: UUID, payload: dict, request: Req
858
 
859
 
860
  @app.delete("/api/sessions/{session_id}")
861
- def delete_session(session_id: UUID, request: Request):
862
- cancel_session_job(session_id)
863
  row = owned_session_row(session_id, request.state.user_id, request.state.is_admin)
864
  expected_path = f"sessions/{session_id}"
865
  storage_path = row.get("path")
866
  if storage_path is not None and storage_path != expected_path:
867
  raise HTTPException(409, "Session uses a different storage layout")
868
 
869
- if storage_path == expected_path:
870
- if session_folder_exists(hub(), os.environ["STORAGE_REPO_ID"], storage_path):
871
- hub().delete_folder(
872
- repo_id=os.environ["STORAGE_REPO_ID"], repo_type="dataset",
873
- path_in_repo=storage_path, commit_message=f"Delete session {session_id}",
874
- )
875
-
876
  backend(f"/{session_id}", request.state.user_id, "DELETE", is_admin=request.state.is_admin)
 
 
 
 
877
  return {"ok": True}
878
 
879
 
 
26
  from email.message import Message
27
  from email.utils import collapse_rfc2231_value
28
  from dotenv import load_dotenv
29
+ from fastapi import BackgroundTasks, FastAPI, File, Form, HTTPException, Request, UploadFile
30
  from fastapi.responses import FileResponse, JSONResponse
31
  from huggingface_hub import HfApi, hf_hub_download, snapshot_download
32
  from huggingface_hub.errors import RemoteEntryNotFoundError
33
+ from storage_listing import session_files
34
 
35
  load_dotenv(Path(__file__).with_name(".env"))
36
  jobs = {}
 
731
  job["done"].wait(timeout=300)
732
  if not job["done"].is_set():
733
  raise HTTPException(504, "The analysis upload did not stop in time; deletion was not started")
734
+ return [(client_id, job.get("workPath")) for client_id, job in matching]
735
+
736
+
737
+ def cleanup_deleted_session_files(session_id, storage_path, job_paths):
738
+ for _, work_path in job_paths:
739
  if work_path:
740
  shutil.rmtree(work_path, ignore_errors=True)
741
+ folders = ([storage_path] if storage_path else []) + [
742
+ path for client_id, _ in job_paths
743
+ for path in (f"colab-staging/{client_id}", f"colab-results/{client_id}")
744
+ ]
745
+ api = hub()
746
+ repo_id = os.environ["STORAGE_REPO_ID"]
747
+ failed_folders = []
748
+ for folder in folders:
749
+ for attempt in range(3):
750
+ try:
751
+ api.delete_folder(repo_id=repo_id, repo_type="dataset", path_in_repo=folder,
752
+ commit_message=f"Delete session {session_id} files")
753
+ break
754
+ except RemoteEntryNotFoundError:
755
+ break
756
+ except Exception:
757
+ if attempt == 2:
758
+ logger.exception("Unable to delete session storage folder %s", folder)
759
+ failed_folders.append(folder)
760
+ else:
761
+ time.sleep(2 ** attempt)
762
+ if failed_folders:
763
+ logger.error("Background storage cleanup incomplete for session %s: %s", session_id, failed_folders)
764
+ else:
765
+ logger.info("Finished background storage cleanup for session %s", session_id)
766
 
767
 
768
  def session_row(session_id, user_id, is_admin=False):
 
881
 
882
 
883
  @app.delete("/api/sessions/{session_id}")
884
+ def delete_session(session_id: UUID, request: Request, background_tasks: BackgroundTasks):
 
885
  row = owned_session_row(session_id, request.state.user_id, request.state.is_admin)
886
  expected_path = f"sessions/{session_id}"
887
  storage_path = row.get("path")
888
  if storage_path is not None and storage_path != expected_path:
889
  raise HTTPException(409, "Session uses a different storage layout")
890
 
891
+ job_paths = cancel_session_job(session_id)
 
 
 
 
 
 
892
  backend(f"/{session_id}", request.state.user_id, "DELETE", is_admin=request.state.is_admin)
893
+ for client_id, _ in job_paths:
894
+ jobs.pop(client_id, None)
895
+ # The database deletion makes the session disappear before Hugging Face finishes deleting its files.
896
+ background_tasks.add_task(cleanup_deleted_session_files, session_id, storage_path, job_paths)
897
  return {"ok": True}
898
 
899