Skip to content

Page datastore queries that fetched all applications - #7234

Open
srinivasr wants to merge 3 commits into
pipe-cd:masterfrom
srinivasr:fix/list-applications-pagination
Open

Page datastore queries that fetched all applications#7234
srinivasr wants to merge 3 commits into
pipe-cd:masterfrom
srinivasr:fix/list-applications-pagination

Conversation

@srinivasr

Copy link
Copy Markdown
Contributor

What this PR does:

Fixes unbounded datastore queries in ListApplications (piped and web) and CreateDeploymentChain. These endpoints ran one query with no limit and returned every matching row; now they fetch pages of 100 and combine them before responding. Cursor paging needs a stable sort, so these queries order by Id.

ListEvents gets the same treatment, with a default of newest-first when a request leaves the order unset — without that default, paging would error on the second page.

Adds the composite indexes these query shapes need to firestoreindexensurer.

Why we need it:

A project with thousands of applications pays for all of them on every piped sync. The TODO in ListApplications flagged this; until the reads are chunked, the query grows with the project and eventually risks datastore timeouts.

Which issue(s) this PR fixes:

Fixes #7051

Does this PR introduce a user-facing change?:

No breaking changes. Responses still contain the complete aggregated list — they're just fetched from the backend in chunks.

  • How are users affected by this change: Firestore users: firestoreindexensurer creates the new composite indexes automatically on control-plane startup, but GCP index builds take time — calls fail with FAILED_PRECONDITION until each index finishes building. MySQL users need no action.
  • Is this breaking change: No
  • How to migrate (if breaking change): N/A

was noticing the ListApplications call just pulling every row in the
collection for large projects. switched both piped_api and web_api to
cursor-based paging. same fix for ListEvents.

Co-authored-by: areebahmeddd <areebahmeddd@users.noreply.github.com>
Signed-off-by: srinivasr <sriniv4sreddy@gmail.com>
@srinivasr
srinivasr requested a review from a team as a code owner August 24, 2026 20:10
@srinivasr
srinivasr requested review from armistcxy, khanhtc1202 and t-kikuc and a lite review from Copilot August 24, 2026 20:10
@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for pipecd-site ready!

Name Link
🔨 Latest commit 201421d
🔍 Latest deploy log https://app.netlify.com/projects/pipecd-site/deploys/6a8f6cd8174df6000836be0e
😎 Deploy Preview https://deploy-preview-7234--pipecd-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI 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.

Pull request overview

This PR addresses unbounded datastore reads in PipeCD’s control-plane gRPC APIs by switching several “list everything” queries to internal cursor-based pagination, aggregating results server-side to preserve existing RPC contracts while avoiding single large queries (notably improving piped sync behavior for large projects).

Changes:

  • Page and aggregate results for ListApplications (piped + web) and CreateDeploymentChain application lookups, using stable ordering and a fixed per-page limit.
  • Page and aggregate ListEvents, including a default stable ordering when the request does not specify one.
  • Add Firestore composite indexes required by the new query shapes and update index parsing tests; add a unit test for paged ListApplications.

Reviewed changes

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

Show a summary per file
File Description
pkg/app/server/grpcapi/web_api.go Switch WebAPI ListApplications to internally page via listAllApplications with a fixed limit.
pkg/app/server/grpcapi/piped_api.go Add paging + stable ordering for piped ListApplications, ListEvents, and application listing used by CreateDeploymentChain.
pkg/app/server/grpcapi/list_applications_test.go Add unit tests verifying multi-page aggregation behavior for piped ListApplications.
pkg/app/server/grpcapi/application_lister.go Introduce a shared helper to page/aggregate application lists via datastore cursor.
pkg/app/server/grpcapi/event_lister.go Introduce a shared helper to page/aggregate event lists via datastore cursor.
pkg/app/ops/firestoreindexensurer/indexes.json Add composite indexes for the new ordered/paged Firestore query patterns.
pkg/app/ops/firestoreindexensurer/indexes_test.go Update expected parsed index list to include the new indexes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/app/server/grpcapi/piped_api.go Outdated
Comment thread pkg/app/server/grpcapi/piped_api.go
Comment thread pkg/app/server/grpcapi/piped_api.go
@srinivasr
srinivasr requested a balanced review from Copilot August 25, 2026 05:23

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@srinivasr
srinivasr requested a balanced review from Copilot August 25, 2026 05:23

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@srinivasr
srinivasr force-pushed the fix/list-applications-pagination branch from cb748e5 to 0ea89f0 Compare August 25, 2026 19:58
@srinivasr
srinivasr requested a balanced review from Copilot August 25, 2026 19:59

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@areebahmeddd

Copy link
Copy Markdown

this probably needs discussion before merge 🤔

  1. firestore index rollout

the description says firestoreindexensurer creates the indexes on control-plane startup but it actually runs in the ops component. that means the server can start serving the new queries before the indexes exist, causing ListApplications to fail with FAILED_PRECONDITION until the builds finish

could the index changes land in a separate, earlier pr so operators can let them build before the query changes ship? that would avoid a hard upgrade ordering constraint

  1. application index shapes

CreateDeploymentChain always filters on ProjectId and Disabled, with Name/Kind being optional. but four of the six new application indexes omit Disabled:

ProjectId | Id
ProjectId | Kind | Id
ProjectId | Name | Id
ProjectId | Name | Kind | Id

the indexes that seem to match the actual query shapes would be :

ProjectId | Disabled | Name | Id
ProjectId | Disabled | Kind | Id
ProjectId | Disabled | Name | Kind | Id

could you walk through the query-shape to index mapping again? the two event indexes look correct, so this may just be a slip on the application side

// Page through the datastore so a large project cannot be served by one
// unbounded query. The RPC response has no cursor field, so all pages are
// aggregated here before returning.
apps, err := listAllApplications(ctx, a.applicationStore, opts)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ListApplications is called by every piped on each app-store sync and again by the event watcher. with 3,000 apps, this change turns one query into 30 sequential round-trips per sync, while the response is still fully aggregated in memory.

the description also says paging avoids paying for all applications on every sync, but the total read is still the same, just split across queries.

could listApplicationsPageSize be increased substantially, say to 1000, to keep the bounded-query benefit without adding 10x the round-trips? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

makes sense, bumped listApplicationsPageSize to 1000 to keep the round-trips low while still keeping each individual query bounded.

srinivasr added a commit to srinivasr/pipecd that referenced this pull request Aug 26, 2026
data-only and test-only change. pulling the firestore composite indexes out from pipe-cd#7234 into a prerequisite PR based on reviewer feedback.

CreateIndexes in the ops component fires off gcloud commands but doesn't wait for the READY state. landing this first lets the indexes build in the background across environments before we ship the actual query changes. avoids the FAILED_PRECONDITION downtime window for ListApplications and CreateDeploymentChain.

Which issue(s) this PR fixes:
split from pipe-cd#7234; enables pipe-cd#7051

Co-authored-by: areebahmeddd <areebahmeddd@users.noreply.github.com>
Signed-off-by: srinivasr <sriniv4sreddy@gmail.com>
@srinivasr
srinivasr force-pushed the fix/list-applications-pagination branch from 0ea89f0 to 9749c8a Compare August 26, 2026 17:17
reviewer caught it: CreateDeploymentChain always filters on Disabled so
the composite indexes from the previous commit won't actually serve those
queries. swapped out the four bad entries for the three shapes that match:
  ProjectId | Disabled | Name | Id
  ProjectId | Disabled | Kind | Id
  ProjectId | Disabled | Name | Kind | Id

the standalone ProjectId | Id one is redundant with ProjectId | Disabled | Id
so dropped it too.

Co-authored-by: areebahmeddd <areebahmeddd@users.noreply.github.com>
Signed-off-by: srinivasr <sriniv4sreddy@gmail.com>
@srinivasr
srinivasr force-pushed the fix/list-applications-pagination branch from 9749c8a to c7dbb1a Compare August 26, 2026 17:23
@srinivasr

Copy link
Copy Markdown
Contributor Author

this probably needs discussion before merge 🤔

1. firestore index rollout

the description says firestoreindexensurer creates the indexes on control-plane startup but it actually runs in the ops component. that means the server can start serving the new queries before the indexes exist, causing ListApplications to fail with FAILED_PRECONDITION until the builds finish

could the index changes land in a separate, earlier pr so operators can let them build before the query changes ship? that would avoid a hard upgrade ordering constraint

2. application index shapes

CreateDeploymentChain always filters on ProjectId and Disabled, with Name/Kind being optional. but four of the six new application indexes omit Disabled:

ProjectId | Id
ProjectId | Kind | Id
ProjectId | Name | Id
ProjectId | Name | Kind | Id

the indexes that seem to match the actual query shapes would be :

ProjectId | Disabled | Name | Id
ProjectId | Disabled | Kind | Id
ProjectId | Disabled | Name | Kind | Id

could you walk through the query-shape to index mapping again? the two event indexes look correct, so this may just be a slip on the application side

makes sense, split the indexes out into #7274 so operators can let them build first without hitting FAILED_PRECONDITION.

also fixed the app index shapes to include Disabled across the CreateDeploymentChain and ListApplications queries (ProjectId | Disabled | Name | Id, ProjectId | Disabled | Kind | Id, ProjectId | Disabled | Name | Kind | Id), and dropped the redundant ProjectId | Id entry. updated this branch with the same fixes so both stay in sync.

@srinivasr
srinivasr requested a review from areebahmeddd August 26, 2026 18:12
Signed-off-by: srinivasr <sriniv4sreddy@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: piped ListApplications silently truncates paginated results

3 participants