Skip to content

fixed offset - #1943

Merged
mwrock merged 2 commits into
mainfrom
sougata/pagination_bug
Nov 13, 2025
Merged

fixed offset#1943
mwrock merged 2 commits into
mainfrom
sougata/pagination_bug

Conversation

@sougata-progress

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings November 12, 2025 23:50
@sougata-progress
sougata-progress requested a review from a team as a code owner November 12, 2025 23:50
Signed-off-by: sougata-progress <sougatab@progress.com>

Copilot AI left a comment

Copy link
Copy Markdown

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 refactors pagination logic to use direct offset values instead of page-based pagination. The change improves clarity by passing explicit offsets to database queries rather than calculating them from page numbers in multiple locations.

Key Changes:

  • Replaced page field with offset field in the ListPackages struct
  • Modified pagination calculation to use direct offset values instead of page-based calculations
  • Refactored database query logic to compute total counts separately from paginated results

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
components/builder-db/src/models/package.rs Updated ListPackages struct to use offset instead of page; refactored query logic to calculate total counts separately and apply pagination using direct offset values
components/builder-api/src/server/resources/pkgs.rs Modified do_get_packages to pass offset directly instead of calculating from page numbers; simplified postprocessing logic
components/builder-api/src/server/resources/origins.rs Updated list_unique_packages to calculate offset from page/per_page before passing to ListPackages

Comment on lines +678 to +680
paginated_rows = paginated_rows.into_iter()
.unique_by(|p| (p.ident.clone(), p.origin.clone()))
.collect();

Copilot AI Nov 12, 2025

Copy link

Choose a reason for hiding this comment

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

The deduplication using unique_by is performed after pagination, which may result in fewer items than the requested limit. This can lead to inconsistent pagination behavior where some pages have fewer items than expected. Consider applying deduplication before pagination, or adjusting the limit to account for potential duplicates.

Copilot uses AI. Check for mistakes.
let mut query = packages_with_channel_platform::table
.filter(packages_with_channel_platform::origin.eq(origin_str))
let mut base_query = packages_with_channel_platform::table
.filter(packages_with_channel_platform::origin.eq(origin_str.clone()))

Copilot AI Nov 12, 2025

Copy link

Choose a reason for hiding this comment

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

The origin_str is being cloned multiple times throughout the method (lines 626, 654). Since it's already a String, consider cloning it once at the beginning and reusing the reference, or avoiding the clone entirely by using a reference where possible.

Suggested change
.filter(packages_with_channel_platform::origin.eq(origin_str.clone()))
.filter(packages_with_channel_platform::origin.eq(&origin_str))

Copilot uses AI. Check for mistakes.
if !pl.ident.name.is_empty() {
query = query.filter(packages_with_channel_platform::name.eq(name_str))
base_query =
base_query.filter(packages_with_channel_platform::name.eq(name_str.clone()))

Copilot AI Nov 12, 2025

Copy link

Choose a reason for hiding this comment

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

The name_str variable is cloned twice (lines 633 and 659). Consider reusing the reference or restructuring to avoid unnecessary clones.

Copilot uses AI. Check for mistakes.
.filter(packages_with_channel_platform::ident_array.contains(parts.clone()))
.filter(packages_with_channel_platform::visibility.eq_any(visibility.clone()))
.select(count_star())
.first(conn)?;

Copilot AI Nov 12, 2025

Copy link

Choose a reason for hiding this comment

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

Using .first() with count_star() will fail if no records are found, returning an error instead of a count of 0. Use .get_result() or handle the case where no records exist to return a count of 0.

Suggested change
.first(conn)?;
.get_result(conn)?;

Copilot uses AI. Check for mistakes.
Signed-off-by: sougata-progress <sougatab@progress.com>
Copilot AI review requested due to automatic review settings November 13, 2025 00:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

if !pl.ident.name.is_empty() {
Histogram::PackageListOriginOnlyCallTime.set(duration_millis as f64);
// Apply deduplication
pkgs = pkgs.into_iter().unique().collect();

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The two branches (limit < 0 and limit >= 0) have inconsistent deduplication methods. The first branch uses .unique() while the second uses .unique_by(|p| (p.ident.clone(), p.origin.clone())). This inconsistency could lead to different results between paginated and non-paginated queries. Use the same deduplication key for both branches.

Suggested change
pkgs = pkgs.into_iter().unique().collect();
pkgs = pkgs.into_iter().unique_by(|p| (p.ident.clone(), p.origin.clone())).collect();

Copilot uses AI. Check for mistakes.
Comment on lines +650 to +653
// First get ALL records and deduplicate to get accurate counts and consistent
// pagination
let query_all = base_query.order(packages_with_channel_platform::ident.desc());
let all_rows: Vec<PackageWithChannelPlatform> = query_all.load(conn)?;

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

Loading all records into memory for pagination defeats the purpose of database-level pagination. This approach will not scale well with large datasets and could cause memory issues. Consider implementing cursor-based pagination or using SQL window functions to maintain deduplication while paginating at the database level.

Copilot uses AI. Check for mistakes.
Comment on lines +801 to +806
let start = offset_val as usize;
let end = if limit < 0 {
unique_by_name.len()
} else {
(start + limit as usize).min(unique_by_name.len())
};

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The logic for handling limit < 0 is inconsistent with the list() method. In list(), negative limits are checked at line 640, but here the negative check is embedded within the end calculation. Consider extracting this into an early return pattern like in list() for consistency.

Suggested change
let start = offset_val as usize;
let end = if limit < 0 {
unique_by_name.len()
} else {
(start + limit as usize).min(unique_by_name.len())
};
if limit < 0 {
// Early return: negative limit means return all results
let results = unique_by_name.clone();
let duration_millis = start_time.elapsed().as_millis();
trace!("DBCall package::list_distinct_for_origin time: {} ms",
duration_millis);
Histogram::DbCallTime.set(duration_millis as f64);
Histogram::PackageListDistinctForOriginCallTime.set(duration_millis as f64);
return Ok((results, total_count));
}
let start = offset_val as usize;
let end = (start + limit as usize).min(unique_by_name.len());

Copilot uses AI. Check for mistakes.
Comment on lines +866 to +867
let body =
helpers::package_results_json(packages, count as isize, start as isize, stop as isize);

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Multiple type conversions between i64, isize, and usize create confusion and potential overflow risks. The start variable is i64, then converted to isize here, while earlier conversions also occur. Consider standardizing on a single type throughout the pagination logic to reduce conversion overhead and improve clarity.

Copilot uses AI. Check for mistakes.
@sonarqube-for-infrastructure-prod

Copy link
Copy Markdown

@mwrock mwrock left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This one looks right!

@mwrock
mwrock merged commit 0b85159 into main Nov 13, 2025
34 checks passed
@mwrock
mwrock deleted the sougata/pagination_bug branch November 13, 2025 21:41
chef-expeditor Bot pushed a commit that referenced this pull request Nov 13, 2025
Obvious fix; these changes are the result of automation not creative thinking.
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.

3 participants