Skip to content

fix#189

Merged
Salah3060 merged 1 commit intodevfrom
Fix/timeline-performance
Dec 15, 2025
Merged

fix#189
Salah3060 merged 1 commit intodevfrom
Fix/timeline-performance

Conversation

@Salah3060
Copy link
Contributor

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the personalized "For You" posts query by introducing a dual-path strategy: a high-performance path for fresh users with minimal interaction history, and an enhanced normal path with improved query optimization through pre-filtering, reduced dataset sizes, and conditional logic based on user engagement patterns.

  • Adds a fast-track query path for fresh users (< 5 follows, < 10 likes) that skips complex personalization
  • Refactors the normal path with pre-calculation optimizations, earlier LIMIT clauses, and conditional query generation
  • Reduces intermediate result sets through strategic filtering and hard limits on candidate posts

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

WHERE p."is_deleted" = false
AND p."type" IN ('POST', 'QUOTE')
AND p."created_at" > NOW() - INTERVAL '7 days' -- Shorter window for fresh users
AND p."interest_id" IN (${interestIdsString})
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vulnerability: The interestIdsString variable is directly interpolated into the SQL query using string interpolation. This creates a security risk even if the values are expected to be integers, as malicious input could potentially manipulate the query structure.

Copilot uses AI. Check for mistakes.
FROM "Like" l
JOIN "posts" p ON l."post_id" = p."id"
WHERE l."user_id" = ${userId}
AND l."created_at" > '${dateStr}' -- Optimization: Only recent likes matter for weighting
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vulnerability: The dateStr variable is directly interpolated into the SQL query. Although it's derived from a Date object, this approach bypasses Prisma's parameterization. Use proper parameterized queries to prevent potential SQL injection.

Copilot uses AI. Check for mistakes.
ROW_NUMBER() OVER (PARTITION BY p."interest_id" ORDER BY p."created_at" DESC) as rn
FROM "posts" p
WHERE p."is_deleted" = false
WHERE p."created_at" > '${dateStr}'
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vulnerability: The dateStr variable is directly interpolated into the SQL query without parameterization. This creates a security risk as it bypasses Prisma's built-in SQL injection protection mechanisms.

Copilot uses AI. Check for mistakes.
Comment on lines +1769 to +1773
CASE WHEN ap."user_id" = ${userId} THEN 3
WHEN uf.following_id IS NOT NULL THEN 2
WHEN la.author_id IS NOT NULL THEN 1
ELSE 0
END as pre_score
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pre_score values (3, 2, 1, 0) are magic numbers without clear documentation. These scoring weights should be defined as named constants alongside the other personalization weights to maintain consistency and improve code maintainability.

Copilot uses AI. Check for mistakes.
FROM "posts" p
WHERE p."is_deleted" = false
AND p."type" IN ('POST', 'QUOTE')
AND p."created_at" > NOW() - INTERVAL '7 days' -- Shorter window for fresh users
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The time window filter uses 7 days for fresh users, but the comment at line 1671-1674 and the dateStr calculation sets up a 14-day window for normal users. However, the fresh user query comment on line 1607 says "7 days" but this inconsistency between fresh and normal user time windows should be intentional. The problem is that the fresh user path will miss posts from days 8-14 that the normal path would include, potentially creating an inconsistent experience. Consider documenting why this difference exists or using a consistent time window.

Suggested change
AND p."created_at" > NOW() - INTERVAL '7 days' -- Shorter window for fresh users
-- NOTE: We intentionally use a 7-day window for fresh users (vs. 14 days for normal users)
-- to ensure new users see only the most recent and relevant content, and to avoid overwhelming
-- them with too much information. This difference is by design.
AND p."created_at" > NOW() - INTERVAL '7 days'

Copilot uses AI. Check for mistakes.
AND p."created_at" > NOW() - INTERVAL '7 days' -- Shorter window for fresh users
AND p."interest_id" IN (${interestIdsString})
ORDER BY p."created_at" DESC
LIMIT ${limit} OFFSET ${offset}
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vulnerability: The limit and offset parameters are directly interpolated into the SQL query without validation. These should be properly validated as positive integers and parameterized to prevent SQL injection attacks.

Copilot uses AI. Check for mistakes.
CASE WHEN ap."user_id" = ${userId} THEN ${personalizationWeights.ownPost} ELSE 0 END +
CASE WHEN uf.following_id IS NOT NULL THEN ${personalizationWeights.following} ELSE 0 END +
CASE WHEN la.author_id IS NOT NULL THEN ${personalizationWeights.directLike} ELSE 0 END +
tc.pre_score * 5.0 + -- Reuse pre-calculated score
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multiplication factor of 5.0 for the pre_score is a magic number without documentation. This should be defined as a named constant (e.g., PRE_SCORE_MULTIPLIER) alongside the other personalization weights to maintain consistency and make the scoring logic more transparent.

Copilot uses AI. Check for mistakes.
// ---------------------------------------------------------
if (isFreshUser && hasInterests) {
// Just stringify IDs for the IN clause (safe for integers)
const interestIdsString = interestIds.join(',');
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vulnerability: The interestIdsString is constructed by directly joining user interest IDs without proper parameterization. While the IDs are expected to be integers, this approach is unsafe. Use Prisma's parameterized queries or properly validate and sanitize the input to prevent potential SQL injection attacks.

Copilot uses AI. Check for mistakes.
raw_reposts AS (
SELECT r."post_id", r."user_id", r."created_at"
FROM "Repost" r
WHERE r."created_at" > '${dateStr}'
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL injection vulnerability: The dateStr variable is directly interpolated into the SQL query without parameterization. This approach is unsafe and should use Prisma's parameterized query functionality.

Copilot uses AI. Check for mistakes.
),
-- Get posts from user's interests with time window (no per-interest limit)
-- Optimization: Limit original posts EARLIER.
-- The partition by interest is heavy, but necessary for diversity.
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions "The partition by interest is heavy, but necessary for diversity" but doesn't explain what diversity means in this context or why partitioning by interest achieves it. Consider expanding this comment to explain that it ensures a balanced representation of posts across different interest categories rather than overwhelming the feed with posts from the most active interest.

Suggested change
-- The partition by interest is heavy, but necessary for diversity.
-- The partition by interest is computationally heavy, but necessary to ensure diversity in the feed.
-- Specifically, partitioning by interest_id ensures a balanced representation of posts from different interest categories,
-- preventing the feed from being dominated by posts from the most active interests and promoting a varied user experience.

Copilot uses AI. Check for mistakes.
@Salah3060 Salah3060 merged commit cf0042f into dev Dec 15, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants