forked from Chachigo/FamilyBot
-
Notifications
You must be signed in to change notification settings - Fork 0
Fix: Price caching now expires instead of lasting forever #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
504066e
fix: change price caching from permanent to TTL-based for current prices
SuaveIV f6dd86a
feat: add check-price-cache diagnostic script and justfile recipe
SuaveIV 7554395
fix: complete cache TTL implementation - fix recovery blocks and add …
SuaveIV 53ef8dd
fix: standardize UTC formatting in cache expiration timestamps
SuaveIV 2b982d7
fix: improve transactional integrity and error auditing in populatio…
SuaveIV b8fbd82
fix: prevent API key leak in logs and add rate limiting
SuaveIV c08bbe0
fix: enhance error handling in caching functions and standardize time…
SuaveIV 0750b22
fix: improve error logging and streamline price retrieval in Steam fa…
SuaveIV File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| #!/usr/bin/env python3 | ||
| """Quick script to check price cache status in the database.""" | ||
|
|
||
| import os | ||
| import sys | ||
| from datetime import UTC, datetime | ||
|
|
||
| # Add the src directory to the Python path | ||
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) | ||
|
|
||
| from familybot.lib.database import get_db_connection | ||
|
|
||
|
|
||
| def _print_game_details_cache(cursor, now): | ||
| """Print game_details_cache table contents.""" | ||
| print("=" * 80) | ||
| print("GAME DETAILS CACHE (Steam prices)") | ||
| print("=" * 80) | ||
|
|
||
| cursor.execute( | ||
| """ | ||
| SELECT appid, name, permanent, cached_at, expires_at, | ||
| CASE | ||
| WHEN permanent = 1 THEN 'PERMANENT (never expires)' | ||
| WHEN expires_at > ? THEN 'VALID' | ||
| ELSE 'EXPIRED' | ||
| END as status | ||
| FROM game_details_cache | ||
| ORDER BY cached_at DESC | ||
| LIMIT 20 | ||
| """, | ||
| (now,), | ||
| ) | ||
|
|
||
| rows = cursor.fetchall() | ||
| if not rows: | ||
| print(" No entries found in game_details_cache") | ||
| else: | ||
| print(f" Showing {len(rows)} most recent entries:\n") | ||
| print( | ||
| f" {'AppID':<12} {'Name':<30} {'Permanent':<10} {'Status':<20} {'Expires At'}" | ||
| ) | ||
| print(" " + "-" * 95) | ||
| for row in rows: | ||
| appid = row[0] | ||
| name = (row[1] or "Unknown")[:28] | ||
| permanent = "YES" if row[2] else "NO" | ||
| status = row[5] | ||
| expires = row[4] or "Never" | ||
| if expires != "Never": | ||
| expires = expires[:19] | ||
| print(f" {appid:<12} {name:<30} {permanent:<10} {status:<20} {expires}") | ||
|
|
||
|
|
||
| def _print_itad_price_cache(cursor, now): | ||
| """Print itad_price_cache table contents.""" | ||
| print("\n" + "=" * 80) | ||
| print("ITAD PRICE CACHE (Historical lows)") | ||
| print("=" * 80) | ||
|
|
||
| cursor.execute( | ||
| """ | ||
| SELECT appid, lowest_price_formatted, permanent, cached_at, expires_at, | ||
| CASE | ||
| WHEN permanent = 1 THEN 'PERMANENT (never expires)' | ||
| WHEN expires_at > ? THEN 'VALID' | ||
| ELSE 'EXPIRED' | ||
| END as status | ||
| FROM itad_price_cache | ||
| ORDER BY cached_at DESC | ||
| LIMIT 20 | ||
| """, | ||
| (now,), | ||
| ) | ||
|
|
||
| rows = cursor.fetchall() | ||
| if not rows: | ||
| print(" No entries found in itad_price_cache") | ||
| else: | ||
| print(f" Showing {len(rows)} most recent entries:\n") | ||
| print( | ||
| f" {'AppID':<12} {'Price':<15} {'Permanent':<10} {'Status':<20} {'Expires At'}" | ||
| ) | ||
| print(" " + "-" * 80) | ||
| for row in rows: | ||
| appid = row[0] | ||
| price = row[1] or "N/A" | ||
| permanent = "YES" if row[2] else "NO" | ||
| status = row[5] | ||
| expires = row[4] or "Never" | ||
| if expires != "Never": | ||
| expires = expires[:19] | ||
| print(f" {appid:<12} {price:<15} {permanent:<10} {status:<20} {expires}") | ||
|
|
||
|
|
||
| def _print_summary(cursor): | ||
| """Print summary statistics for both cache tables.""" | ||
| print("\n" + "=" * 80) | ||
| print("SUMMARY") | ||
| print("=" * 80) | ||
|
|
||
| cursor.execute("SELECT COUNT(*) FROM game_details_cache") | ||
| total_game = cursor.fetchone()[0] | ||
|
|
||
| cursor.execute("SELECT COUNT(*) FROM game_details_cache WHERE permanent = 1") | ||
| permanent_game = cursor.fetchone()[0] | ||
|
|
||
| cursor.execute("SELECT COUNT(*) FROM game_details_cache WHERE permanent = 0") | ||
| ttl_game = cursor.fetchone()[0] | ||
|
|
||
| cursor.execute("SELECT COUNT(*) FROM itad_price_cache") | ||
| total_itad = cursor.fetchone()[0] | ||
|
|
||
| cursor.execute("SELECT COUNT(*) FROM itad_price_cache WHERE permanent = 1") | ||
| permanent_itad = cursor.fetchone()[0] | ||
|
|
||
| cursor.execute("SELECT COUNT(*) FROM itad_price_cache WHERE permanent = 0") | ||
| ttl_itad = cursor.fetchone()[0] | ||
|
|
||
| print("\n Game Details Cache:") | ||
| print(f" Total entries: {total_game}") | ||
| print(f" Permanent (never expires): {permanent_game}") | ||
| print(f" TTL-based (will expire): {ttl_game}") | ||
|
|
||
| print("\n ITAD Price Cache:") | ||
| print(f" Total entries: {total_itad}") | ||
| print(f" Permanent (never expires): {permanent_itad}") | ||
| print(f" TTL-based (will expire): {ttl_itad}") | ||
|
|
||
| if permanent_game > 0: | ||
| print( | ||
| f"\n ⚠️ WARNING: {permanent_game} game details entries are still marked as permanent!" | ||
| ) | ||
| print(" These will need to be refreshed or will show stale prices.") | ||
| else: | ||
| print("\n ✅ All game details entries use TTL-based expiration.") | ||
|
|
||
|
|
||
| def check_price_cache(): | ||
| """Check game_details_cache and itad_price_cache tables.""" | ||
| conn = get_db_connection() | ||
| cursor = conn.cursor() | ||
|
|
||
| try: | ||
| now = datetime.now(UTC).isoformat().replace("+00:00", "Z") | ||
|
|
||
| _print_game_details_cache(cursor, now) | ||
| _print_itad_price_cache(cursor, now) | ||
| _print_summary(cursor) | ||
| finally: | ||
| conn.close() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| check_price_cache() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silent exception swallowing hides salvage failures.
When individual record writes fail in the fallback path (line 189-190), the exception is caught with a bare
pass, making it impossible to audit partial cache population failures.🔧 Suggested fix
for app_id, data in games_data.items(): try: cache_game_details(app_id, data, permanent=False) written += 1 - except Exception: - pass + except Exception as e: + logger.warning("Failed to salvage record %s: %s", app_id, e)📝 Committable suggestion
🧰 Tools
🪛 Ruff (0.15.6)
[warning] 181-181: Do not catch blind exception:
Exception(BLE001)
[warning] 183-183: Use
logging.exceptioninstead oflogging.errorReplace with
exception(TRY400)
[warning] 183-183: Logging statement uses f-string
(G004)
[error] 189-190:
try-except-passdetected, consider logging the exception(S110)
[warning] 189-189: Do not catch blind exception:
Exception(BLE001)
🤖 Prompt for AI Agents