Skip to content

Commit 112e736

Browse files
fixed offset
Signed-off-by: sougata-progress <sougatab@progress.com>
1 parent 560ddc3 commit 112e736

3 files changed

Lines changed: 83 additions & 70 deletions

File tree

components/builder-api/src/server/resources/origins.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ async fn list_unique_packages(req: HttpRequest,
842842
visibility: helpers::visibility_for_optional_session(&req,
843843
opt_session_id,
844844
&origin),
845-
page: page as i64,
845+
offset: ((page as i64).saturating_sub(1)) * (per_page as i64),
846846
limit: per_page as i64, };
847847

848848
match Package::distinct_for_origin(&lpr, &mut conn) {

components/builder-api/src/server/resources/pkgs.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -852,22 +852,19 @@ pub fn postprocess_extended_package_list(_req: &HttpRequest,
852852
let start = if pagination.range < 0 {
853853
0
854854
} else {
855-
let (page, per_page) = helpers::extract_pagination_in_pages(pagination);
856-
let safe_page = page.max(1);
857-
safe_page.checked_sub(1)
858-
.and_then(|p| p.checked_mul(per_page))
859-
.unwrap_or_default()
855+
pagination.range as i64
860856
};
861-
let pkg_count = packages.len() as isize;
857+
let pkg_count = packages.len() as i64;
862858
let stop = match pkg_count {
863859
0 => count,
864-
_ => (start + pkg_count - 1) as i64,
860+
_ => start + pkg_count - 1,
865861
};
866862

867863
debug!("postprocessing extended package list, start: {}, stop: {}, total_count: {}",
868864
start, stop, count);
869865

870-
let body = helpers::package_results_json(packages, count as isize, start, stop as isize);
866+
let body =
867+
helpers::package_results_json(packages, count as isize, start as isize, stop as isize);
871868

872869
let mut response = if count as isize > (stop as isize + 1) {
873870
HttpResponse::PartialContent()
@@ -891,17 +888,23 @@ fn do_get_packages(req: &HttpRequest,
891888
Err(_) => None,
892889
};
893890

894-
let (page, per_page) = helpers::extract_pagination_in_pages(pagination);
895-
let limit = if pagination.range < 0 { -1 } else { per_page };
891+
let (offset, limit) = if pagination.range < 0 {
892+
// When range is negative, return all packages
893+
(0i64, -1i64)
894+
} else {
895+
// Use range directly as offset
896+
(pagination.range as i64, helpers::PAGINATION_RANGE_MAX as i64)
897+
};
896898

897899
let mut conn = req_state(req).db.get_conn().map_err(Error::DbError)?;
898900

899-
let lpr = ListPackages { ident: BuilderPackageIdent(ident.clone()),
900-
visibility: helpers::visibility_for_optional_session(req,
901-
opt_session_id,
902-
&ident.origin),
903-
page: page as i64,
904-
limit: limit as i64, };
901+
let lpr = ListPackages { ident: BuilderPackageIdent(ident.clone()),
902+
visibility:
903+
helpers::visibility_for_optional_session(req,
904+
opt_session_id,
905+
&ident.origin),
906+
offset,
907+
limit };
905908

906909
if pagination.distinct {
907910
match Package::list_distinct(&lpr, &mut conn).map_err(Error::DieselError) {

components/builder-db/src/models/package.rs

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ pub struct UpdatePackageVisibility {
344344
pub struct ListPackages {
345345
pub ident: BuilderPackageIdent,
346346
pub visibility: Vec<PackageVisibility>,
347-
pub page: i64,
347+
pub offset: i64,
348348
pub limit: i64,
349349
}
350350

@@ -619,72 +619,82 @@ impl Package {
619619
let name_str = pl.ident.name.clone();
620620
let parts = pl.ident.clone().parts();
621621
let visibility = pl.visibility.clone();
622-
let page = pl.page;
622+
let offset_val = pl.offset;
623623
let limit = pl.limit;
624624

625-
let mut query = packages_with_channel_platform::table
626-
.filter(packages_with_channel_platform::origin.eq(origin_str))
625+
let mut base_query = packages_with_channel_platform::table
626+
.filter(packages_with_channel_platform::origin.eq(origin_str.clone()))
627627
.into_boxed();
628+
628629
// We need the into_boxed above to be able to conditionally filter and not break the
629630
// typesystem.
630631
if !pl.ident.name.is_empty() {
631-
query = query.filter(packages_with_channel_platform::name.eq(name_str))
632+
base_query =
633+
base_query.filter(packages_with_channel_platform::name.eq(name_str.clone()))
632634
};
633635

634-
let mut pkgs = if pl.limit < 0 {
635-
let query =
636-
query.filter(packages_with_channel_platform::ident_array.contains(parts.clone()))
637-
.filter(packages_with_channel_platform::visibility.eq_any(visibility.clone()))
638-
.order(packages_with_channel_platform::ident.desc());
639-
let pkgs: std::vec::Vec<PackageWithChannelPlatform> = query.get_results(conn)?;
640-
pkgs
641-
} else {
642-
// Use window function with COUNT(*) OVER() for efficient pagination
643-
// This prevents loading all data into memory and avoids slice index panics
644-
let offset_val = (page.saturating_sub(1)) * limit;
636+
base_query = base_query
637+
.filter(packages_with_channel_platform::ident_array.contains(parts.clone()))
638+
.filter(packages_with_channel_platform::visibility.eq_any(visibility.clone()));
639+
640+
if pl.limit < 0 {
641+
// No pagination - return all records
642+
let query = base_query.order(packages_with_channel_platform::ident.desc());
643+
let mut pkgs: std::vec::Vec<PackageWithChannelPlatform> = query.get_results(conn)?;
645644

645+
// Apply deduplication
646+
pkgs = pkgs.into_iter().unique().collect();
647+
let count = pkgs.len() as i64;
648+
Ok((pkgs, count))
649+
} else {
650+
// First get the total count before pagination and deduplication
651+
// We need to rebuild the query instead of cloning because BoxedSelectStatement doesn't
652+
// implement Clone
653+
let mut count_query = packages_with_channel_platform::table
654+
.filter(packages_with_channel_platform::origin.eq(origin_str.clone()))
655+
.into_boxed();
656+
657+
if !pl.ident.name.is_empty() {
658+
count_query =
659+
count_query.filter(packages_with_channel_platform::name.eq(name_str.clone()));
660+
}
661+
662+
let total_count: i64 = count_query
663+
.filter(packages_with_channel_platform::ident_array.contains(parts.clone()))
664+
.filter(packages_with_channel_platform::visibility.eq_any(visibility.clone()))
665+
.select(count_star())
666+
.first(conn)?;
667+
668+
// Then get the paginated results using direct offset
646669
let query_with_pagination =
647-
query.filter(packages_with_channel_platform::ident_array.contains(parts.clone()))
648-
.filter(packages_with_channel_platform::visibility.eq_any(visibility.clone()))
649-
.order(packages_with_channel_platform::ident.desc())
650-
.limit(limit)
651-
.offset(offset_val);
670+
base_query.order(packages_with_channel_platform::ident.desc())
671+
.limit(limit)
672+
.offset(offset_val);
652673

653-
let paginated_rows: Vec<PackageWithChannelPlatform> = query_with_pagination.load(conn)?;
674+
let mut paginated_rows: Vec<PackageWithChannelPlatform> =
675+
query_with_pagination.load(conn)?;
654676

655677
// Apply deduplication consistently using unique_by for package identity
656-
let unique_rows: Vec<PackageWithChannelPlatform> =
657-
paginated_rows.into_iter()
658-
.unique_by(|p| (p.ident.clone(), p.origin.clone()))
659-
.collect();
678+
paginated_rows = paginated_rows.into_iter()
679+
.unique_by(|p| (p.ident.clone(), p.origin.clone()))
680+
.collect();
660681

661-
unique_rows
662-
};
682+
let duration_millis = start_time.elapsed().as_millis();
683+
Histogram::DbCallTime.set(duration_millis as f64);
684+
Histogram::PackageListCallTime.set(duration_millis as f64);
663685

664-
// helpful trick when debugging queries, this has Debug trait:
665-
// diesel::query_builder::debug_query::<diesel::pg::Pg, _>(&query)
686+
// Package list for a whole origin is still not very
687+
// performant, and we want to track that
688+
if !pl.ident.name.is_empty() {
689+
Histogram::PackageListOriginOnlyCallTime.set(duration_millis as f64);
690+
} else {
691+
Histogram::PackageListOriginNameCallTime.set(duration_millis as f64);
692+
}
666693

667-
let duration_millis = start_time.elapsed().as_millis();
668-
Histogram::DbCallTime.set(duration_millis as f64);
669-
Histogram::PackageListCallTime.set(duration_millis as f64);
694+
trace!(target: "habitat_builder_api::server::resources::pkgs::versions", "Package::list for {:?}, returned {} items out of {} total", pl.ident, paginated_rows.len(), total_count);
670695

671-
// Package list for a whole origin is still not very
672-
// performant, and we want to track that
673-
if !pl.ident.name.is_empty() {
674-
Histogram::PackageListOriginOnlyCallTime.set(duration_millis as f64);
675-
} else {
676-
Histogram::PackageListOriginNameCallTime.set(duration_millis as f64);
696+
Ok((paginated_rows, total_count))
677697
}
678-
679-
trace!(target: "habitat_builder_api::server::resources::pkgs::versions", "Package::list for {:?}, returned {} items", pl.ident, pkgs.len());
680-
681-
// TODO: Look for a performant Postgresql fix
682-
// and possibly rethink the channels design
683-
pkgs = pkgs.into_iter().unique().collect();
684-
trace!(target: "habitat_builder_api::server::resources::pkgs::versions", "Package::list for {:?} after de-dup has {} items", pl.ident, pkgs.len());
685-
686-
let new_count = pkgs.len() as i64;
687-
Ok((pkgs, new_count))
688698
}
689699

690700
pub fn list_distinct(pl: &ListPackages,
@@ -698,7 +708,7 @@ impl Package {
698708
let name_str = pl.ident.name.clone();
699709
let parts = pl.ident.clone().parts();
700710
let visibility = pl.visibility.clone();
701-
let page = pl.page;
711+
let offset_val = pl.offset;
702712
let limit = pl.limit;
703713

704714
let mut count_query =
@@ -733,7 +743,7 @@ impl Package {
733743
}
734744

735745
let limit_i64 = limit;
736-
let offset_i64 = (page.saturating_sub(1)) * limit;
746+
let offset_i64 = offset_val;
737747

738748
let rows: Vec<(String, String)> =
739749
page_query.limit(limit_i64).offset(offset_i64).load(conn)?;
@@ -771,7 +781,7 @@ impl Package {
771781
// Extract cloned copies out of pl
772782
let origin_str = pl.ident.origin.clone();
773783
let visibility = pl.visibility.clone();
774-
let page = pl.page;
784+
let offset_val = pl.offset;
775785
let limit = pl.limit;
776786

777787
let base_query = origin_package_settings::table
@@ -791,7 +801,7 @@ impl Package {
791801
.collect();
792802

793803
let total_count = unique_by_name.len() as i64;
794-
let start = ((page.saturating_sub(1)) * limit) as usize;
804+
let start = offset_val as usize;
795805
let end = (start + limit as usize).min(unique_by_name.len());
796806
let results = if limit < 0 {
797807
unique_by_name.clone()

0 commit comments

Comments
 (0)