diff --git a/cache/precache.py b/cache/precache.py index 0b62072a..d3864131 100644 --- a/cache/precache.py +++ b/cache/precache.py @@ -1,7 +1,7 @@ import asyncio import traceback -from sqlalchemy import and_, join, select +from sqlalchemy import and_, func, join, select # Импорт Author, AuthorFollower отложен для избежания циклических импортов from cache.cache import cache_author, cache_topic @@ -69,29 +69,43 @@ async def precache_topics_authors(topic_id: int, session) -> None: # Предварительное кеширование подписчиков тем async def precache_topics_followers(topic_id: int, session) -> None: - followers_query = select(TopicFollower.follower).where(TopicFollower.topic == topic_id) - topic_followers = {row[0] for row in session.execute(followers_query) if row[0]} + try: + followers_query = select(TopicFollower.follower).where(TopicFollower.topic == topic_id) + topic_followers = {row[0] for row in session.execute(followers_query) if row[0]} - followers_payload = fast_json_dumps(list(topic_followers)) - await redis.execute("SET", f"topic:followers:{topic_id}", followers_payload) + followers_payload = fast_json_dumps(list(topic_followers)) + await redis.execute("SET", f"topic:followers:{topic_id}", followers_payload) + + # Добавляем логирование для отладки + logger.debug(f"Precached {len(topic_followers)} followers for topic #{topic_id}") + if len(topic_followers) == 0: + logger.warning(f"⚠️ Topic #{topic_id} has 0 followers - this might indicate a data issue") + elif len(topic_followers) == 1: + logger.info(f"ℹ️ Topic #{topic_id} has exactly 1 follower - checking if this is correct") + + except Exception as e: + logger.error(f"Error precaching followers for topic #{topic_id}: {e}") + # В случае ошибки, устанавливаем пустой список + await redis.execute("SET", f"topic:followers:{topic_id}", fast_json_dumps([])) async def precache_data() -> None: logger.info("precaching...") logger.debug("Entering precache_data") + + # Список паттернов ключей, которые нужно сохранить при FLUSHDB + preserve_patterns = [ + "migrated_views_*", # Данные миграции просмотров + "session:*", # Сессии пользователей + "env_vars:*", # Переменные окружения + "oauth_*", # OAuth токены + ] + + # Сохраняем все важные ключи перед очисткой + all_keys_to_preserve = [] + preserved_data = {} + try: - # Список паттернов ключей, которые нужно сохранить при FLUSHDB - preserve_patterns = [ - "migrated_views_*", # Данные миграции просмотров - "session:*", # Сессии пользователей - "env_vars:*", # Переменные окружения - "oauth_*", # OAuth токены - ] - - # Сохраняем все важные ключи перед очисткой - all_keys_to_preserve = [] - preserved_data = {} - for pattern in preserve_patterns: keys = await redis.execute("KEYS", pattern) if keys: @@ -153,6 +167,25 @@ async def precache_data() -> None: logger.info("Beginning topic precache phase") with local_session() as session: + # Проверяем состояние таблицы topic_followers перед кешированием + total_followers = session.execute(select(func.count(TopicFollower.topic))).scalar() + unique_topics_with_followers = session.execute( + select(func.count(func.distinct(TopicFollower.topic))) + ).scalar() + unique_followers = session.execute(select(func.count(func.distinct(TopicFollower.follower)))).scalar() + + logger.info("📊 Database state before precaching:") + logger.info(f" Total topic_followers records: {total_followers}") + logger.info(f" Unique topics with followers: {unique_topics_with_followers}") + logger.info(f" Unique followers: {unique_followers}") + + if total_followers == 0: + logger.warning( + "🚨 WARNING: topic_followers table is empty! This will cause all topics to show 0 followers." + ) + elif unique_topics_with_followers == 0: + logger.warning("🚨 WARNING: No topics have followers! This will cause all topics to show 0 followers.") + # topics q = select(Topic).where(Topic.community == 1) topics = get_with_stat(q) diff --git a/test_topic_followers_debug.py b/test_topic_followers_debug.py deleted file mode 100644 index 14f4ee76..00000000 --- a/test_topic_followers_debug.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python3 -""" -Отладочный скрипт для проверки проблемы с количеством подписчиков топиков. - -Проблема: все топики возвращают followers: 1 -""" - -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -from storage.db import local_session -from orm.topic import Topic, TopicFollower -from orm.author import Author -from sqlalchemy import text, func - -def debug_topic_followers(): - """Отладка проблемы с подписчиками топиков""" - print("🔍 Отладка проблемы с подписчиками топиков") - print("=" * 50) - - with local_session() as session: - # 1. Проверяем общее количество топиков - total_topics = session.query(func.count(Topic.id)).scalar() - print(f"📊 Всего топиков в БД: {total_topics}") - - # 2. Проверяем общее количество записей в topic_followers - total_followers = session.query(func.count(TopicFollower.topic)).scalar() - print(f"👥 Всего записей в topic_followers: {total_followers}") - - # 3. Проверяем уникальные топики с подписчиками - topics_with_followers = session.query(func.count(func.distinct(TopicFollower.topic))).scalar() - print(f"🎯 Уникальных топиков с подписчиками: {topics_with_followers}") - - # 4. Проверяем уникальных подписчиков - unique_followers = session.query(func.count(func.distinct(TopicFollower.follower))).scalar() - print(f"👤 Уникальных подписчиков: {unique_followers}") - - # 5. Проверяем распределение подписчиков по топикам - print("\n📈 Распределение подписчиков по топикам:") - followers_distribution = session.execute(text(""" - SELECT topic, COUNT(DISTINCT follower) as followers_count - FROM topic_followers - GROUP BY topic - ORDER BY followers_count DESC - LIMIT 10 - """)).fetchall() - - for topic_id, count in followers_distribution: - topic_title = session.query(Topic.title).where(Topic.id == topic_id).scalar() - print(f" Топик {topic_id} ({topic_title}): {count} подписчиков") - - # 6. Проверяем конкретные топики из вашего примера - print("\n🎯 Проверка конкретных топиков:") - test_topic_ids = [715, 82, 166] # ID из вашего примера - - for topic_id in test_topic_ids: - topic = session.query(Topic).where(Topic.id == topic_id).first() - if topic: - followers_count = session.query(func.count(TopicFollower.follower)).where( - TopicFollower.topic == topic_id - ).scalar() - - print(f" Топик {topic_id} ({topic.title}): {followers_count} подписчиков") - - # Показываем детали подписчиков - if followers_count > 0: - followers = session.query(TopicFollower.follower).where( - TopicFollower.topic == topic_id - ).all() - follower_ids = [f[0] for f in followers] - print(f" Подписчики: {follower_ids}") - else: - print(f" ❌ Топик {topic_id} не найден") - - # 7. Проверяем SQL запрос, который используется в get_topics_with_stats - print("\n🔍 Проверка SQL запроса из get_topics_with_stats:") - test_topic_ids_str = ",".join(map(str, test_topic_ids)) - - followers_stats_query = f""" - SELECT topic, COUNT(DISTINCT follower) as followers_count - FROM topic_followers tf - WHERE topic IN ({test_topic_ids_str}) - GROUP BY topic - """ - - print(f"SQL: {followers_stats_query}") - - try: - result = session.execute(text(followers_stats_query)).fetchall() - print("Результат:") - for topic_id, count in result: - print(f" Топик {topic_id}: {count} подписчиков") - except Exception as e: - print(f"❌ Ошибка выполнения SQL: {e}") - - # 8. Проверяем, есть ли проблемы с JOIN - print("\n🔗 Проверка JOIN с таблицей author:") - try: - join_query = f""" - SELECT tf.topic, tf.follower, a.name as author_name - FROM topic_followers tf - JOIN author a ON tf.follower = a.id - WHERE tf.topic IN ({test_topic_ids_str}) - ORDER BY tf.topic, tf.follower - """ - - join_result = session.execute(text(join_query)).fetchall() - print("Результат JOIN:") - for topic_id, follower_id, author_name in join_result: - print(f" Топик {topic_id} -> Подписчик {follower_id} ({author_name})") - - except Exception as e: - print(f"❌ Ошибка JOIN: {e}") - -if __name__ == "__main__": - debug_topic_followers()