Skip to content

PULP-2385: Route pulp-content reads to RDS replica - #1468

Merged
decko merged 2 commits into
pulp:mainfrom
decko:pulp-2385-content-replica
Sep 16, 2026
Merged

decko merged 2 commits into
pulp:mainfrom
decko:pulp-2385-content-replica

Conversation

@decko

@decko decko commented Sep 11, 2026

Copy link
Copy Markdown
Member

Summary

Route pulp-content reads to the stage RDS read replica while keeping writes and pull-through operations on the primary database.

Changes

  • Add the model-based content database router with safe no-replica fallback.
  • Add ContextVar protection for pull-through writes and failed-download updates.
  • Configure the stage replica endpoint and content-only router setting.
  • Add router tests and the pulpcore image patch.

Testing

  • Targeted Ruff checks passed.
  • Ruff format check passed.
  • Pulpcore 3.117.1 patch chain dry-run passed through patch 0064.
  • Full OCI test execution was unavailable because the isolated environment failed to boot with a missing SECRET_KEY.

Jira

PULP-2385


Labels: ai-assisted
Assisted-by: OpenAI

Summary by Sourcery

Route pulp-content reads through the stage RDS replica while preserving primary-database behavior for writes and content pull-through operations.

New Features:

  • Route pulp-content database reads to the stage RDS replica when available while keeping writes and migrations on the primary database.

Bug Fixes:

  • Ensure pull-through caching and failed-download updates continue using the primary database despite replica read routing.

Enhancements:

  • Add safe fallback to the primary database when no replica is configured and isolate primary-database routing to the current execution context.

Build:

  • Apply the pulpcore patch needed to keep content pull-through writes on the primary database.

Deployment:

  • Configure the optional stage database replica endpoint and enable the content database router in deployed environments.

Tests:

  • Add database router tests covering replica fallback, primary routing, migration behavior, error cleanup, and task-local context isolation.

@sourcery-ai

sourcery-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Routes pulp-content database reads to an optionally configured stage RDS replica through a Django database router, while keeping writes, migrations, app-status reads, and pull-through-related updates on the primary via ContextVar protection and a pulpcore image patch.

Sequence diagram for primary protection during pull-through

sequenceDiagram
    participant Content as pulp-content
    participant Router as ContentReplicaRouter
    participant Primary as RDS primary
    participant Replica as RDS read replica

    Content->>Router: db_for_read(model)
    Router->>Replica: return replica
    Replica-->>Content: content read

    Content->>Router: use_primary_database()
    Content->>Router: db_for_read(model)
    Router->>Primary: return default
    Primary-->>Content: pull-through write-related read
    Content->>Router: db_for_write(model)
    Router->>Primary: return default
    Primary-->>Content: write completed
    Content->>Router: ContextVar reset
Loading

File-Level Changes

Change Details Files
Introduces model-aware database routing that sends content reads to a replica when available while preserving primary routing for writes, migrations, app status reads, and pull-through contexts.
  • Added a ContextVar-backed context manager for temporarily forcing primary reads.
  • Added safe fallback to the default database when no replica is configured.
  • Configured the content router in development and stage deployments.
  • Added coverage for fallback, routing, context isolation/reset, writes, and migrations.
pulp_service/pulp_service/app/database_router.py
pulp_service/pulp_service/tests/unit/test_database_router.py
dev-container/settings.py
deploy/clowdapp.yaml
Ensures pulpcore pull-through caching and failed-download database updates bypass the replica.
  • Added the pulpcore patch to wrap pull-through operations and failed-download updates in primary-database routing context.
  • Applied the patch during image construction.
  • Documented the patch in the image patch guide.
images/assets/patches/0064-Route-content-pull-through-writes-to-primary.patch
Dockerfile
images/assets/patches/CLAUDE.md
Adds optional stage read-replica configuration and enables the router for pulp-content.
  • Reads the optional replica host from the stage database secret.
  • Creates a replica database configuration only when the endpoint is present.
  • Sets the content-only database router in the deployed service configuration.
deploy/clowdapp.yaml
Documents the new replica-read behavior.
  • Added the PULP-2385 feature entry.
CHANGES/2385.feature

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 reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. If the replica endpoint is unavailable, misconfigured, or too far behind, content reads could fail or return stale data across the service, and pull-through behavior could be disrupted. Reverting restores primary reads, but requests that failed or served stale content during the rollout cannot be undone.


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

@@ -0,0 +1,33 @@
"""Database routing for the content service."""

@YasenT YasenT Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I am wondering if it's not worth it to add a fallback to the primary, in case the replica is unavailable?
Which considering we are on aws, etc etc.. might be over-engineering it
Something like
`import time
from django.db.utils import Error as DjangoDBError

REPLICA_HEALTH_CHECK_TTL = 5 # seconds to trust a health probe before re-checking
_replica_down_until = 0.0

def _replica_is_healthy():
global _replica_down_until
now = time.monotonic()
if now < _replica_down_until:
return False
try:
connections["replica"].ensure_connection()
return True
except DjangoDBError:
_replica_down_until = now + REPLICA_HEALTH_CHECK_TTL
return False

class ContentReplicaRouter:
def db_for_read(self, model, **hints):
if (
"replica" not in settings.DATABASES
or _use_primary.get()
or model._meta.model_name == "appstatus"
):
return "default"
if not _replica_is_healthy():
return "default"
return "replica"`

@decko

decko commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

I recommend keeping the current fail-fast behavior when a configured replica is unavailable. Router-level health probing is not reliable for async ORM connections, and transparent retries are unsafe after response streaming or pull-through mutations begin. Silent fallback could overload the primary during a replica incident. Let’s validate stage behavior first and track an observable, bounded circuit-breaker design separately if primary fallback becomes an availability requirement.

Assisted-by: OpenAI

Keep upstream ETag patch 0064 and renumber PULP-2385 routing patch to 0065.\n\nAssisted-by: OpenAI
@decko
decko merged commit 8f349d3 into pulp:main Sep 16, 2026
6 checks passed
@decko
decko deleted the pulp-2385-content-replica branch September 16, 2026 11:32
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