Merge pull request #123 from aradhyacp/fix/sync
Fix crash on upload due to Database and Disk mismatch
This commit is contained in:
+28
-21
@@ -6,7 +6,6 @@ import os
|
|||||||
import time
|
import time
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import rmtree
|
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
@@ -152,33 +151,32 @@ def upload_image() -> tuple[Response, int]:
|
|||||||
submission_hash = hashlib.md5(submission_data).hexdigest()
|
submission_hash = hashlib.md5(submission_data).hexdigest()
|
||||||
submission_path = RESULT_FOLDER / img_hash / submission_hash
|
submission_path = RESULT_FOLDER / img_hash / submission_hash
|
||||||
|
|
||||||
# If submission already exists, return submission hash
|
# Check if hash is present on the DB
|
||||||
if submission_path.exists():
|
|
||||||
submission: Submission = Submission.query.filter_by(
|
submission: Submission = Submission.query.filter_by(
|
||||||
hash=submission_hash
|
hash=submission_hash
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if submission is not None: # File exist but not in DB (should not happen),
|
# Only return if BOTH the file exists AND the DB entry exists
|
||||||
rmtree(submission_path) # Removing file and continue processing as new
|
if submission_path.exists() and submission is not None:
|
||||||
db.session.delete(submission) # pylint: disable=no-member
|
|
||||||
db.session.commit() # pylint: disable=no-member
|
|
||||||
else:
|
|
||||||
return jsonify({"submission_hash": submission.hash}), 200
|
return jsonify({"submission_hash": submission.hash}), 200
|
||||||
|
|
||||||
# If image is new, create a new Image entry
|
# Self-Healing Logic
|
||||||
new_img_path = RESULT_FOLDER / img_hash / image_name
|
new_img_path = RESULT_FOLDER / img_hash / image_name
|
||||||
sub_img = Image.query.filter_by(hash=img_hash).first() # type: ignore
|
|
||||||
|
|
||||||
if new_img_path.parent.exists() and sub_img is None:
|
# This fixes the "Ghost File" issue (DB exists, but file is missing)
|
||||||
# Inconsistent state: image file exists but no DB entry
|
if not new_img_path.exists():
|
||||||
rmtree(new_img_path.parent) # Remove the existing files and continue
|
|
||||||
|
|
||||||
if not new_img_path.parent.exists():
|
|
||||||
new_img_path.parent.mkdir(parents=True, exist_ok=True)
|
new_img_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(new_img_path, "wb") as f: # Write file to disk
|
with open(new_img_path, "wb") as f:
|
||||||
f.write(image_data)
|
f.write(image_data)
|
||||||
|
|
||||||
new_img = Image(
|
# Try to find image in DB by hash
|
||||||
|
sub_img = Image.query.filter_by(
|
||||||
|
hash=img_hash
|
||||||
|
).first()
|
||||||
|
|
||||||
|
# If DB entry is missing (even if file exists on disk), create it!
|
||||||
|
if sub_img is None:
|
||||||
|
sub_img = Image(
|
||||||
file=str(new_img_path),
|
file=str(new_img_path),
|
||||||
hash=img_hash,
|
hash=img_hash,
|
||||||
size=len(image_data),
|
size=len(image_data),
|
||||||
@@ -186,15 +184,19 @@ def upload_image() -> tuple[Response, int]:
|
|||||||
first_submission_date=datetime.now(timezone.utc),
|
first_submission_date=datetime.now(timezone.utc),
|
||||||
last_submission_date=datetime.now(timezone.utc),
|
last_submission_date=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
db.session.add(new_img) # pylint: disable=no-member
|
db.session.add(sub_img) # pylint: disable=no-member
|
||||||
db.session.commit() # pylint: disable=no-member
|
db.session.commit() # pylint: disable=no-member
|
||||||
|
|
||||||
sub_img = Image.query.filter_by(hash=img_hash).first() # type: ignore
|
# Increment upload count for the image
|
||||||
sub_img.upload_count += 1
|
sub_img.upload_count += 1
|
||||||
db.session.commit() # pylint: disable=no-member
|
db.session.commit() # pylint: disable=no-member
|
||||||
|
|
||||||
# Create new Submission entry
|
# Create new Submission entry
|
||||||
submission_path.mkdir(parents=True, exist_ok=True)
|
submission_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Only create a new submission if it does not already exist
|
||||||
|
# New submission for new image
|
||||||
|
if not submission:
|
||||||
submission = Submission(
|
submission = Submission(
|
||||||
filename=image.filename,
|
filename=image.filename,
|
||||||
password=password,
|
password=password,
|
||||||
@@ -202,14 +204,19 @@ def upload_image() -> tuple[Response, int]:
|
|||||||
hash=submission_hash,
|
hash=submission_hash,
|
||||||
status="pending",
|
status="pending",
|
||||||
date=time.time(),
|
date=time.time(),
|
||||||
image_hash=sub_img.hash, # type: ignore
|
image_hash=sub_img.hash,
|
||||||
)
|
)
|
||||||
db.session.add(submission) # pylint: disable=no-member
|
db.session.add(submission) # pylint: disable=no-member
|
||||||
|
|
||||||
db.session.commit() # pylint: disable=no-member
|
db.session.commit() # pylint: disable=no-member
|
||||||
|
|
||||||
|
# Re-analysis case to prevent early return
|
||||||
|
submission.status = "pending" # type: ignore
|
||||||
|
db.session.commit() # pylint: disable=no-member
|
||||||
|
|
||||||
# Start the analysis jobs
|
# Start the analysis jobs
|
||||||
queue.enqueue("aperisolve.workers.analyze_image", submission.hash, job_timeout=300)
|
queue.enqueue("aperisolve.workers.analyze_image", submission.hash, job_timeout=300)
|
||||||
|
|
||||||
|
# Return submission identifier
|
||||||
return jsonify({"submission_hash": submission.hash}), 200
|
return jsonify({"submission_hash": submission.hash}), 200
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ def analyze_image(submission_hash: str) -> None:
|
|||||||
try:
|
try:
|
||||||
analyzer_func(*args)
|
analyzer_func(*args)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
sentry_sdk.capture_exception(e)
|
||||||
print(f"Error in {analyzer_func.__name__}: {e}")
|
print(f"Error in {analyzer_func.__name__}: {e}")
|
||||||
sentry_sdk.capture_exception(e)
|
sentry_sdk.capture_exception(e)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user