Double all timeout values to prevent download/upsert timeouts
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MTGJSON Data Sanity Check
|
||||
|
||||
Validates downloaded MTGJSON files for expected sizes before upserting to database.
|
||||
This prevents corrupted or incomplete data from being loaded into PostgreSQL.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Expected minimum file sizes (in bytes) for MTGJSON v5 files
|
||||
# These are approximate minimums based on typical MTGJSON data sizes
|
||||
EXPECTED_MIN_SIZES = {
|
||||
"AllPrintings.json": 500 * 1024 * 1024, # 500 MB (should be 500-600 MB)
|
||||
"AllSetFiles.json": 10 * 1024 * 1024, # 10 MB
|
||||
"AllIdentifiers.json": 100 * 1024 * 1024, # 100 MB
|
||||
"CardTypes.json": 1 * 1024 * 1024, # 1 MB
|
||||
"Keywords.json": 0.5 * 1024 * 1024, # 0.5 MB
|
||||
"MagicSets.json": 50 * 1024 * 1024, # 50 MB
|
||||
"MagicRoots.json": 1 * 1024 * 1024, # 1 MB
|
||||
"SetTranslations.json": 5 * 1024 * 1024, # 5 MB
|
||||
}
|
||||
|
||||
|
||||
def validate_file_sizes(data_dir: Path) -> dict:
|
||||
"""
|
||||
Validate downloaded MTGJSON files for expected sizes.
|
||||
|
||||
Args:
|
||||
data_dir: Path to the MTGJSON data directory
|
||||
|
||||
Returns:
|
||||
Dict with validation results
|
||||
"""
|
||||
results = {
|
||||
"valid": True,
|
||||
"files_checked": 0,
|
||||
"files_valid": 0,
|
||||
"files_invalid": 0,
|
||||
"issues": []
|
||||
}
|
||||
|
||||
if not data_dir.exists():
|
||||
results["valid"] = False
|
||||
results["issues"].append(f"Data directory does not exist: {data_dir}")
|
||||
return results
|
||||
|
||||
# Check each expected file
|
||||
for filename, min_size in EXPECTED_MIN_SIZES.items():
|
||||
filepath = data_dir / filename
|
||||
|
||||
if not filepath.exists():
|
||||
results["issues"].append(f"Missing file: {filename}")
|
||||
results["valid"] = False
|
||||
results["files_checked"] += 1
|
||||
results["files_invalid"] += 1
|
||||
continue
|
||||
|
||||
results["files_checked"] += 1
|
||||
actual_size = filepath.stat().st_size
|
||||
|
||||
if actual_size < min_size:
|
||||
results["valid"] = False
|
||||
results["files_invalid"] += 1
|
||||
results["issues"].append(
|
||||
f"{filename}: {actual_size / (1024*1024):.1f} MB (minimum: {min_size / (1024*1024):.1f} MB)"
|
||||
)
|
||||
logger.warning(
|
||||
f"File {filename} is too small: {actual_size / (1024*1024):.1f} MB "
|
||||
f"(expected minimum: {min_size / (1024*1024):.1f} MB)"
|
||||
)
|
||||
else:
|
||||
results["files_valid"] += 1
|
||||
logger.info(
|
||||
f"✓ {filename}: {actual_size / (1024*1024):.1f} MB (OK)"
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
async def validate_and_cleanup(data_dir: Path, max_retries: int = 3) -> bool:
|
||||
"""
|
||||
Validate MTGJSON files and cleanup if invalid.
|
||||
|
||||
Args:
|
||||
data_dir: Path to the MTGJSON data directory
|
||||
max_retries: Maximum number of retry attempts
|
||||
|
||||
Returns:
|
||||
True if validation passes, False otherwise
|
||||
"""
|
||||
logger.info("=" * 60)
|
||||
logger.info("MTGJSON Data Sanity Check")
|
||||
logger.info("=" * 60)
|
||||
|
||||
for attempt in range(1, max_retries + 1):
|
||||
logger.info(f"\nAttempt {attempt}/{max_retries}")
|
||||
|
||||
# Validate file sizes
|
||||
results = validate_file_sizes(data_dir)
|
||||
|
||||
if results["valid"]:
|
||||
logger.info("\n✓ All files passed validation")
|
||||
logger.info(f" Checked: {results['files_checked']} files")
|
||||
logger.info(f" Valid: {results['files_valid']} files")
|
||||
return True
|
||||
|
||||
# Validation failed
|
||||
logger.warning("\n✗ Validation failed:")
|
||||
for issue in results["issues"]:
|
||||
logger.warning(f" - {issue}")
|
||||
|
||||
if attempt < max_retries:
|
||||
logger.warning(f"\nCleanup and retry in {60 * attempt} seconds...")
|
||||
await asyncio.sleep(60 * attempt)
|
||||
|
||||
# Delete all downloaded files
|
||||
logger.warning("Deleting downloaded files...")
|
||||
for f in data_dir.glob("*"):
|
||||
if f.is_file():
|
||||
f.unlink()
|
||||
logger.warning(f" Deleted: {f.name}")
|
||||
|
||||
logger.error("\n✗✗✗ All retry attempts failed ✗✗✗")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
# Get data directory from settings or use default
|
||||
try:
|
||||
sys.path.insert(0, "/app")
|
||||
from app.config import get_settings
|
||||
settings = get_settings()
|
||||
data_dir = Path(settings.DATA_DIR)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load settings: {e}")
|
||||
data_dir = Path("/app/data/mtgjson")
|
||||
|
||||
# Run validation
|
||||
asyncio.run(validate_and_cleanup(data_dir))
|
||||
Reference in New Issue
Block a user