Skip to content

Optimize read-only content queries and interval content experiment (PULP-2346, PULP-1996; merge after #1642) - #1643

Merged
dkliban merged 4 commits into
pulp:mainfrom
larrymou9:lrios/pulp-2346-mega
Oct 28, 2026
Merged

dkliban merged 4 commits into
pulp:mainfrom
larrymou9:lrios/pulp-2346-mega

Conversation

@larrymou9

@larrymou9 larrymou9 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two pulp-service patches, applied on top of pulpcore 3.122.1 when the image is built, that remove the slow repository-membership queries behind lightwell file and directory requests (PULP-2346, PULP-1996). They use the core_ca_relpath_pattern_idx index from #1642 (merged, applied on prod 2026-10-26). This PR has no migration.

  • Query patch (0080), from #8262 (Jan Hutar, Elijah DeLee): the index.html probe, directory listing and file lookup check membership per matching row (EXISTS) instead of walking the whole repository version. The probe is skipped for file paths. Directory listings are capped at 10,000 entries.
  • A/B experiment (0081), from Add always-on interval content query experiment for PULP-1996 #1579 (Jan Hutar): get_content() ("which items are in this repo version"), used by pulp_maven's directory page lookup, runs either today's unnest(content_ids) query (variant A) or an indexed join (variant B). 50% of calls each, logged.

Same changes, with unit tests, in the pulpcore draft #8270, which runs pulpcore's CI.

Review fix (d8e0f5f)

The cap sliced the query to 10,001 rows but the "…" marker counted distinct names, so later entries could be dropped with no marker. It now counts distinct entries and adds the marker only when a new entry would exceed the limit.

Results on prod

Read-only EXPLAIN ANALYZE through GABI, database time of one query, java/validated (1.6M items). "Now" is stock code with the #1642 index live. Every row uses the exact SQL the handler generates, compiled in the ORM (new shape timed twice).

Query Now New
index.html probe, missing / page exists 11.4 / 9.8 s 0.03 / 0.4 ms
File lookup, missing / existing 12.5 / 12.4 s 0.05 / 0.07 ms
Directory listing, missing folder 0.04 ms 0.04 ms (the index alone already fixes it)
Directory listing, existing folder 6.5 s 0.4 ms
Directory listing, broad folder org/apache/ (479K rows) 14.1 s 6.3 s
Directory listing, broad folder org/eclipse/ (412K rows) 14.0 s 2.0 s
pulp_maven page lookup, page exists / missing (A/B experiment, variant B) 12.8 / 12.6 s 1.6 / 0.06 ms

java/remediated (109K items): the same lookups go from 0.6–0.9 s to under 0.1 ms (page lookup 0.86 s → 1.6 ms), and the existing-folder listing from 345 ms to 0.4 ms.

Broad folders still take seconds: the handler must read every matching row (479K for org/apache/, which has only a few dozen distinct entries) to confirm it stays under the 10,000-entry limit, so they are about 2–7x faster than today, not instant.

Why it matters (CloudWatch, 6 h, 301K lightwell requests): the slowest requests on validated are directory requests (87 over 30 s) and HEAD 404s (avg 9.7 s).

Questions for reviewers

  1. The get_content() A/B experiment (the indexed query for pulp_maven's directory page lookup, patch 0081) runs for only 50% of calls. After the first production data, should we raise that share?

Not tested yet

  • Unit tests (CI on #8270).
  • End to end through a content app.
  • The remediated slow tail (53 requests over 5 s) is not explained by database timing.
  • Follow-ups: cap the publication directory listing; skip the index.html probe for extensionless paths.

Summary by Sourcery

Improve read-only content request performance and evaluate an indexed repository-membership query for interval content lookups.

Bug Fixes:

  • Optimize read-only file, directory, and index probe queries by checking repository membership only for matching content and limiting directory listings to 10,000 entries.

Enhancements:

  • Add a 50/50 A/B experiment for interval-based repository content queries, with variant dispatch logging and SQL markers for production performance comparison.

Build:

  • Apply the read-only query optimization and interval content experiment patches when building the pulp-service image.

Documentation:

  • Add a test plan for validating and evaluating the interval content query experiment in staging and production.

Tests:

  • Add database-backed tests covering query equivalence across repository versions, repository isolation, empty versions, experiment dispatch, logging, and SQL markers.

Chores:

  • Add changelog entries for the query optimization and interval content experiment.

@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The PR layers two pulpcore patches into the image: one changes read-only content handler queries to use indexed path-first membership checks, and the other experimentally selects between legacy and interval-based lazy repository-content queries with logging and SQL attribution. Review should focus on patch compatibility, deployment ordering with the external index, query correctness/laziness, and the documented production validation plan.

Sequence diagram for optimized content HEAD lookup

sequenceDiagram
    participant Client
    participant ContentHandler
    participant Database
    Client->>ContentHandler: HEAD /content/path
    ContentHandler->>Database: indexed relative_path lookup
    Database-->>ContentHandler: matching rows with EXISTS membership
    alt file path
        ContentHandler-->>Client: file response
    else directory path
        ContentHandler->>Database: directory listing capped by DIRECTORY_LISTING_LIMIT
        Database-->>ContentHandler: up to 10000 entries
        ContentHandler-->>Client: directory response
    end
Loading

Sequence diagram for interval content query experiment

sequenceDiagram
    participant RepositoryVersion
    participant run_lazy_experiment
    participant QuerySet
    participant Database
    RepositoryVersion->>run_lazy_experiment: get_content()
    run_lazy_experiment->>run_lazy_experiment: choose variant A or B
    alt variant A
        run_lazy_experiment-->>RepositoryVersion: legacy unnest QuerySet
    else variant B
        run_lazy_experiment-->>RepositoryVersion: interval-table QuerySet
    end
    RepositoryVersion->>QuerySet: apply lazy filters
    QuerySet->>Database: evaluate query with SQL marker
    Database-->>QuerySet: content results
Loading

File-Level Changes

Change Details Files
Installs and applies two hosted pulpcore patches that optimize repository content lookups and add an interval-query A/B experiment.
  • Apply path-first membership lookups using per-row EXISTS checks, skip the index.html probe for file paths, and cap directory listings at 10,000 entries.
  • Add the required Docker build steps for patches 0080 and 0081; no migration or index is created.
  • Review deployment ordering: the external relative-path index from pulp-service Add concurrent relative_path index for content query testing #1642 must exist before rollout.
Dockerfile
images/assets/patches/0080-Optimize-read-only-content-queries.patch
images/assets/patches/0081-Experiment-interval-content-query.patch
Adds lazy queryset experimentation for repository-version content membership using randomized dispatch between legacy unnest and interval-table queries.
  • Introduce run_lazy_experiment() with 50% candidate selection and JSON dispatch logging.
  • Route get_content() through equivalent content_ids_subquery() and content_pks_subquery() variants while preserving queryset laziness and chaining.
  • Use SQL comments to attribute variants in RDS Performance Insights without forcing query evaluation.
  • Add database-backed tests covering adds, removals, empty versions, repository isolation, dispatch logging, and SQL markers.
pulp_service/pulp_service/app/experiments.py
pulp_service/pulp_service/tests/unit/test_interval_content_experiment.py
images/assets/patches/0081-Experiment-interval-content-query.patch
Documents rollout validation and records the performance optimization and experiment in changelog fragments.
  • Provide a test plan for checking variant correctness, CloudWatch dispatch volume, SQL latency, and production decision criteria.
  • Add PULP-1996 and PULP-2346 changelog entries.
docs/test_plans/0005-Interval_Content_Query.md
CHANGES/1996.misc
CHANGES/2346.bugfix

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot 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.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="Dockerfile" line_range="190-191" />
<code_context>
 COPY images/assets/patches/0079-Log-Maven-path-index-fallback-reasons.patch /tmp/
 RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0079-Log-Maven-path-index-fallback-reasons.patch

+COPY images/assets/patches/0080-Optimize-read-only-content-queries.patch /tmp/
+RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0080-Optimize-read-only-content-queries.patch
+
+COPY images/assets/patches/0081-Experiment-interval-content-query.patch /tmp/
</code_context>
<issue_to_address>
**Later directory entries disappear**

When more than 10,000 matching content-artifact rows include repeated displayed names and later rows contain distinct entries, the `ContentArtifact` slice stops before those later rows, while `directory_list` counts only distinct names and never reaches the truncation threshold; the response silently omits entries without a truncation marker.

Limit based on distinct displayed entries, continuing far enough to detect and mark truncation.
</issue_to_address>

### Comment 2
<location path="Dockerfile" line_range="190-191" />
<code_context>
 COPY images/assets/patches/0079-Log-Maven-path-index-fallback-reasons.patch /tmp/
 RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0079-Log-Maven-path-index-fallback-reasons.patch

+COPY images/assets/patches/0080-Optimize-read-only-content-queries.patch /tmp/
+RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0080-Optimize-read-only-content-queries.patch
+
+COPY images/assets/patches/0081-Experiment-interval-content-query.patch /tmp/
</code_context>
<issue_to_address>
**Large publication listings stay unbounded**

When a non-pass-through publication directory contains more than 10,000 entries, the `PublishedArtifact` queryset used by the `pas` iteration is not limited, so the handler loads and renders every matching entry without a truncation marker; callers receive an unbounded listing.

Apply the directory listing limit and truncation handling to the `PublishedArtifact` iteration as well.
</issue_to_address>

### Comment 3
<location path="Dockerfile" line_range="191" />
<code_context>
 RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0079-Log-Maven-path-index-fallback-reasons.patch

+COPY images/assets/patches/0080-Optimize-read-only-content-queries.patch /tmp/
+RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0080-Optimize-read-only-content-queries.patch
+
+COPY images/assets/patches/0081-Experiment-interval-content-query.patch /tmp/
</code_context>
<issue_to_address>
**Extensionless files still pay probe**

When a file path has no trailing slash and its final segment has no dot, the `last_segment` check skips the `index.html` query only when the path contains a dot. An extensionless file path therefore still runs that repository-membership probe before the exact-file lookup, so HEAD requests for such files retain the expensive extra query.

Determine whether the requested path is a file without relying only on a dot in its name, and skip the index probe for extensionless files too.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and if the interval membership query incorrectly matches content outside the selected repository version, content lookups could serve or list artifacts that version does not contain. Reverting would stop future incorrect lookups, but it cannot undo content that was already exposed.

Blocking findings: Dockerfile:191, Dockerfile:191, Dockerfile:191


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread Dockerfile
Comment on lines +190 to +191
COPY images/assets/patches/0080-Optimize-read-only-content-queries.patch /tmp/
RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0080-Optimize-read-only-content-queries.patch

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.

🟡 Medium · Later directory entries disappear

When more than 10,000 matching content-artifact rows include repeated displayed names and later rows contain distinct entries, the ContentArtifact slice stops before those later rows, while directory_list counts only distinct names and never reaches the truncation threshold; the response silently omits entries without a truncation marker.

Limit based on distinct displayed entries, continuing far enough to detect and mark truncation.

Prompt for AI agents
In `Dockerfile` at lines 190-191:

**Later directory entries disappear**

When more than 10,000 matching content-artifact rows include repeated displayed names and later rows contain distinct entries, the `ContentArtifact` slice stops before those later rows, while `directory_list` counts only distinct names and never reaches the truncation threshold; the response silently omits entries without a truncation marker.

Limit based on distinct displayed entries, continuing far enough to detect and mark truncation.

Comment thread Dockerfile
Comment on lines +190 to +191
COPY images/assets/patches/0080-Optimize-read-only-content-queries.patch /tmp/
RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0080-Optimize-read-only-content-queries.patch

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.

🟡 Medium · Large publication listings stay unbounded

When a non-pass-through publication directory contains more than 10,000 entries, the PublishedArtifact queryset used by the pas iteration is not limited, so the handler loads and renders every matching entry without a truncation marker; callers receive an unbounded listing.

Apply the directory listing limit and truncation handling to the PublishedArtifact iteration as well.

Prompt for AI agents
In `Dockerfile` at lines 190-191:

**Large publication listings stay unbounded**

When a non-pass-through publication directory contains more than 10,000 entries, the `PublishedArtifact` queryset used by the `pas` iteration is not limited, so the handler loads and renders every matching entry without a truncation marker; callers receive an unbounded listing.

Apply the directory listing limit and truncation handling to the `PublishedArtifact` iteration as well.

Comment thread Dockerfile
RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0079-Log-Maven-path-index-fallback-reasons.patch

COPY images/assets/patches/0080-Optimize-read-only-content-queries.patch /tmp/
RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0080-Optimize-read-only-content-queries.patch

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.

🟡 Medium · Extensionless files still pay probe

When a file path has no trailing slash and its final segment has no dot, the last_segment check skips the index.html query only when the path contains a dot. An extensionless file path therefore still runs that repository-membership probe before the exact-file lookup, so HEAD requests for such files retain the expensive extra query.

Determine whether the requested path is a file without relying only on a dot in its name, and skip the index probe for extensionless files too.

Prompt for AI agents
In `Dockerfile` at line 191:

**Extensionless files still pay probe**

When a file path has no trailing slash and its final segment has no dot, the `last_segment` check skips the `index.html` query only when the path contains a dot. An extensionless file path therefore still runs that repository-membership probe before the exact-file lookup, so HEAD requests for such files retain the expensive extra query.

Determine whether the requested path is a file without relying only on a dot in its name, and skip the index probe for extensionless files too.

@kdelee kdelee 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.

I ran the production comparison through GABI and checked the experiment wiring.

Production query results

Against the 1.6M-item java/validated repository version, the optimized query shapes reduced database time as follows:

Query Before New + index
Missing index.html probe >30 s 0.09 ms
Missing file lookup 18 s 0.05 ms
Existing file lookup 13.5 s 0.11 ms
Missing directory listing 14 s 0.06 ms
Selective existing directory listing 13.7 s 0.33 ms

These are approximately 41,000x to >333,000x reductions in database time for the optimized paths.

CloudWatch content logs contained 6,076,848 requests under lightwell/java/validated in the last 30 days, of which 280 were exact broad org/apache/ directory requests (0.0046%). The broad-prefix case remains a separate limitation: it can still require a large membership join even when core_ca_relpath_pattern_idx is used. There have been no matching requests since migration 0024_contentartifact_relative_path_pattern_index was applied, so this does not yet establish post-index production behavior.

Blocking issues

Blocking correctness issue: the [:10001] slice can silently omit later distinct directory entries when the first 10,001 artifacts map to repeated displayed names. The response can be incomplete without the … truncation marker. This should be fixed before approval.

Non-Blocking issues

Non-blocking follow-up: the PublishedArtifact (pas) directory-listing path is not currently bounded by DIRECTORY_LISTING_LIMIT. This is a resource/behavior concern, but not incorrect repository membership; it can be handled in a follow-up optimization/experiment.

Non-blocking follow-up: extensionless file paths still execute the expensive index.html membership probe. This is an incomplete performance optimization, not a content-correctness issue, and can be evaluated in a subsequent experiment.

The broad-prefix org/apache/ behavior is likewise a performance concern, not a membership-correctness concern. The A/B experiment wiring is suitable for collecting real production dispatch and Performance Insights results once deployed.

Blocking only on the silent-incomplete-listing correctness issue. After that is fixed, I can approve while tracking the two performance/resource items separately.

larrymou9 added a commit to larrymou9/pulp-service that referenced this pull request Oct 27, 2026
…-2346)

The cap in patch 0080 sliced the listing query to DIRECTORY_LISTING_LIMIT + 1
rows, but the truncation marker counted distinct displayed names. When many
rows share one name (many files under one subdirectory), the slice could cut
off later subdirectories with no "…" marker, so the listing looked complete
when it was not. The marker could also be added when nothing was omitted.

Drop the row slice and stop when a new entry would exceed the limit, adding
the marker only then. The listing is now complete up to DIRECTORY_LISTING_LIMIT
distinct entries and explicitly marked beyond that.

This addresses the blocking review comment on pulp#1643. The same change is in the
pulpcore draft (pulpcore #8270), with unit tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@larrymou9

Copy link
Copy Markdown
Contributor Author

Thanks Elijah.

Blocking item: fixed in d8e0f5f. The cap now counts distinct entries, not rows, and the "…" marker only appears when a new entry would exceed the limit. The same fix, with unit tests, is in #8270 (CI waiting for approval). I agree with your two non-blocking items (cap the publication listing, and the index.html probe for extensionless paths); I'll track them as follow-ups.

Broad folders: no regression. I first reported that broad-folder listings on validated get worse. That came from my hand-written test SQL, which left out the core_artifact join that select_related adds. With the exact SQL the handler generates (compiled in the ORM, then read-only EXPLAIN ANALYZE on prod through GABI, two rounds, index live):

Folder (rows) Stock code today This PR
validated org/apache/ (479K) 14.1 s 6.3 s
validated org/eclipse/ (412K) 14.0 s 2.0 s
validated org/jboss/ (2.1K) 6.4 s 24 ms
remediated org/apache/ (65K) 0.86 s 0.85 s

So the listing rewrite is faster than today in every case I tried (or equal for remediated), and I don't need to hold it out of this PR. Sorry for the noise. The corrected cap reads all rows for a folder with few distinct names, so it is slower than the capped version (org/apache/: 6.3 s vs 1.9 s), but correct and still more than twice as fast as today.

Other numbers (read-only, prod, validated 1.6M items): the index.html probe and file lookups go from 12–13 s to under 0.2 ms; pulp_maven's directory page lookup goes from 13 s to about 1 ms with the A/B experiment's indexed variant. Those single-row lookups were timed with equivalent hand-written SQL; I'm re-timing them with the exact ORM SQL and will update.

CloudWatch (6 h, lightwell java, 301K requests): the slowest requests on validated are directory requests (87 over 30 s) and HEAD 404s (avg 9.7 s). On remediated, HEAD 404s average 0.14 s with a thin tail (6 over 30 s).

@larrymou9

larrymou9 commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Update: I re-timed the single-row lookups with the exact SQL the handler generates (compiled in the ORM, read-only EXPLAIN ANALYZE on prod, validated 1.6M items). They match the earlier numbers: index.html probe 11.4 s → 0.03 ms (9.8 s → 0.4 ms when a page exists), file lookups 12.4–12.5 s → under 0.1 ms, existing-folder listing 6.5 s → 0.4 ms, pulp_maven page lookup 12.8 s → 1.6 ms. The PR description has the full table as well .

@kdelee

kdelee commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Re-review after d8e0f5f: the directory-listing correctness fix addresses the blocking case. Removing the row slice and checking name not in directory_list before adding the truncation marker preserves later distinct entries and only emits … when a new distinct entry would exceed the limit.

I re-ran representative queries through GABI against the production read replica, using repository version 1815 (1,616,409 content IDs) with core_ca_relpath_pattern_idx live. The SQL was the current EXISTS membership shape, without the removed row LIMIT:

Directory Stock membership query Current EXISTS query
org/apache/ (466,326 artifact rows, 38 rendered names) 14,438.7 ms 4,102.4 ms
org/codehaus/ (402 rows) 7,883.7 ms 31.1 ms
Missing directory 1.28 ms 0.04 ms

The broad-folder result is expected after the correctness fix: because org/apache/ has only 38 distinct rendered entries, the handler must scan all 466,326 matching rows to establish that it has not exceeded the 10,000-entry distinct-name limit. It is still about 3.5x faster than the stock query in this run, but it should not be described as a sub-millisecond path. The returned broad-folder row count matched the membership result.

I found no remaining content-correctness issue in the revised cap logic. The two previously noted items remain non-blocking follow-ups: publication listings are still unbounded, and extensionless file paths still perform the index.html probe.

The current PR checks still need attention before merge: Ruff: New Violations fails on three unused test fixture arguments, and Red Hat Konflux is still queued. After the lint fixes and required checks pass, the code review blocker is cleared.

larrymou9 added a commit to larrymou9/pulp-service that referenced this pull request Oct 27, 2026
…iment tests

The `db` and `content_pks` arguments are requested for their fixture side
effects and are not used in the function bodies. Mark them with the repo's
usual `# noqa: ARG001`, which fixes the "Ruff: New Violations" check on pulp#1643.
@kdelee

kdelee commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

/ok-to-test

@kdelee

kdelee commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

dont think I have power to do that but worth a try :D

@dkliban

dkliban commented Oct 27, 2026

Copy link
Copy Markdown
Member

/retest

@dkliban

dkliban commented Oct 27, 2026

Copy link
Copy Markdown
Member

/ok-to-test

@kdelee

kdelee commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

I opened a stacked PR against the current #1643 branch to make the Maven custom index-page path explicitly call RepositoryVersion.get_content(), preserving the existing PULP-1996 A/B experiment and adding coverage/documentation: larrymou9#1

@kdelee

kdelee commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

For the deployed pulp-maven==0.36.0 case, I added focused fallback tests to pulp_maven maintenance PR #559 (based on branch 0.36): pulp/pulp_maven#559. It forces indexed_response() to return None and covers missing non-trailing index pages, missing trailing-slash index pages, and dotted artifact fallthrough. The tests verify Maven returns None to hand off to pulpcore and invokes version.content/get_content() for directory lookups. Focused tests passed in pr-1643-local with pulpcore at 2dcf2444e: 3 passed.

larrymou9 and others added 4 commits October 27, 2026 16:33
Patch 0080 changes the three lookups a file HEAD runs (the index.html probe,
the directory listing and the exact file lookup) from
content__in=<version content> to a correlated EXISTS on repository membership,
skips the index.html probe for file paths, and caps directory listings at
DIRECTORY_LISTING_LIMIT entries. It ports pulpcore#8262 (based on Jan Hutar's
4-readonly-speedup branch) onto the service's patched pulpcore 3.122.1; the
membership-date change in that PR is not needed because patch 0077 already
replaces that loop.

The queries rely on core_ca_relpath_pattern_idx from migration 0024 (pulp#1642).
Without the index the directory listing scans all of core_contentartifact.

Measured read-only on prod (java-validated-landing, 1.59M items, missing
path): index.html probe 13.5 s, directory listing 14.0 s, file lookup 13.0 s.
Not yet measured with this patch.

See PULP-2346.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
RepositoryVersion.get_content() derives content membership via
unnest(content_ids). That set-returning function has a hard-coded rows=10
planner estimate, so PostgreSQL picks nested-loop index probes scaling with
the full version content array instead of the result set.

Patch 0079 dispatches get_content() through the new
run_lazy_experiment() helper: variant A keeps the legacy unnest subquery,
variant B restricts core_repositorycontent through the existing
_content_relationships() interval predicate, so the planner works with real
tables, indexes, and statistics. Each call runs exactly one variant chosen
at p_candidate=0.5; the experiment is on by default with no settings, so
deploying the image enables it.

The returned queryset is lazy, so no duration is logged at dispatch time;
latency is attributed per variant in RDS Performance Insights via SQL
marker comments. The application log records the dispatch decision
(variant, correlation id) for volume and correlation analysis.

Local repro (PG16, ~10k content version): 197 ms unnest plan vs 8 ms
interval query. Equivalence, isolation, dispatch, and SQL marker tests are
included; run them through oci-env with the hosted patch stack applied.

Ref: LWLP-3268
Generated-by: GLM 5.3 (EnMaaS)
…-2346)

The cap in patch 0080 sliced the listing query to DIRECTORY_LISTING_LIMIT + 1
rows, but the truncation marker counted distinct displayed names. When many
rows share one name (many files under one subdirectory), the slice could cut
off later subdirectories with no "…" marker, so the listing looked complete
when it was not. The marker could also be added when nothing was omitted.

Drop the row slice and stop when a new entry would exceed the limit, adding
the marker only then. The listing is now complete up to DIRECTORY_LISTING_LIMIT
distinct entries and explicitly marked beyond that.

This addresses the blocking review comment on pulp#1643. The same change is in the
pulpcore draft (pulpcore #8270), with unit tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…iment tests

The `db` and `content_pks` arguments are requested for their fixture side
effects and are not used in the function bodies. Mark them with the repo's
usual `# noqa: ARG001`, which fixes the "Ruff: New Violations" check on pulp#1643.
@larrymou9
larrymou9 force-pushed the lrios/pulp-2346-mega branch from 37357bc to 9a2fdfe Compare October 27, 2026 20:33
@kdelee

kdelee commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Update after the rebase onto current main (including pulp-maven==0.37.0):

  • The branch contains only the generic pulpcore patches 0080 and 0081 for this work; there is no Maven-specific optimization patch.
  • I built the rebased pulp-service image successfully and verified that it contains pulp-maven==0.37.0 with both pulpcore patches applied.
  • In an isolated OCI agent using the rebased pulpcore source and the released pulp-maven==0.37.0, the pulp-maven unit suite passed: 235 passed, 7 skipped.
  • The changed-line Ruff check and targeted format check are clean.

The production GABI runs support the two patches independently:

  • 0080: the path-first EXISTS query reduced representative database time from ~13.8 s to 0.041 ms for a missing exact file, from ~13.0 s to 0.170 ms for an existing exact file, and from ~7.9 s to 7.4 ms for a selective directory.
  • 0081: for repository version 1815 (about 1.6M content IDs), the interval-membership candidate ran in ~5.67 s versus ~6.58 s for the legacy unnest(content_ids) variant in the captured production run.

The broad-directory caveat remains: org/apache/ still has to inspect hundreds of thousands of matching artifact rows to establish the distinct-entry cap, although the current query was still faster than stock in the production run. These are database-query timings, not end-to-end HTTP latency.

Maven 0.37.0’s direct index-page EXISTS lookup remains outside the 0081 A/B experiment. If it falls through, it uses the generic pulpcore 0080 handler path; other applicable get_content() callers remain covered by 0081.

@dkliban

dkliban commented Oct 28, 2026

Copy link
Copy Markdown
Member

/ok-to-test

@dkliban
dkliban merged commit 0035e1e into pulp:main Oct 28, 2026
6 checks passed
dkliban added a commit that referenced this pull request Oct 29, 2026
* Revert "Annotate the content membership experiment SQL with the repository (P… (#1651)"

This reverts commit 7ee62c1.

* Revert "Optimize read-only content queries and interval content experiment (PULP-2346, PULP-1996; merge after #1642) (#1643)"

This reverts commit 0035e1e.
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.

4 participants