Cache Interactions

How the cache integrates with repositories and adapters.

Overview

The Package Cache acts as an intermediary layer that coordinates between the CaDEngine, Repository Adapters, and external Git repositories. It provides caching, synchronization, and change notification services while maintaining a clean separation of concerns.

High-Level Architecture

┌─────────────────────────────────────────────────────────┐
│                  Package Cache Layer                    │
│                                                         │
│  ┌──────────────┐      ┌──────────────┐      ┌──────┐   │
│  │ CaDEngine    │ ───> │    Cache     │ ───> │ Repo │   │
│  │   Requests   │      │  Operations  │      │Adapt │   │
│  │              │      │              │      │ ers  │   │
│  └──────────────┘      └──────────────┘      └──────┘   │
│         ↑                      │                │       │
│         │                      ↓                ↓       │
│         │              ┌──────────────┐      ┌──────┐   │
│         └──────────────│   Watcher    │      │ Git  │   │
│                        │  Notifier    │      │Repos │   │
│                        └──────────────┘      └──────┘   │
└─────────────────────────────────────────────────────────┘

Repository Adapter Integration

The cache wraps repository adapters to provide caching and synchronization services:

Adapter Wrapping Pattern

Cache OpenRepository
        ↓
  Check if Cached
        ↓
   Not Cached? ──Yes──> Create External Repo
        │                      ↓
        │              externalrepo.CreateRepositoryImpl
        │                      ↓
        │              Repository Adapter
        │                      ↓
        │              Wrap in cachedRepository
        │                      ↓
        │              Start SyncManager
        │                      ↓
        └────────────> Store in Cache Map
                               ↓
                        Return Repository

Process:

  1. Cache receives OpenRepository request with Repository CR spec
  2. Check cache map for existing repository instance
  3. If not cached, create external repository adapter:
    • Call externalrepo.CreateRepositoryImpl with repository spec
    • Factory pattern selects Git or OCI adapter based on type
    • Adapter initialized with credentials and configuration
  4. Wrap adapter in cachedRepository (CR Cache) or dbRepository (DB Cache)
  5. Start SyncManager for background synchronization
  6. Store in cache map keyed by namespace/name
  7. Return wrapped repository to CaDEngine

Adapter delegation:

The cached repository delegates operations to the underlying adapter:

Cached Repository Method
        ↓
  Check Cache State
        ↓
  Cache Valid? ──Yes──> Return Cached Data
        │
        No
        ↓
  Call Adapter Method
        ↓
  Adapter Executes
        ↓
  Update Cache
        ↓
  Return Result

Delegated operations:

  • CreatePackageRevisionDraft: Pass-through to adapter (no caching)
  • ClosePackageRevisionDraft: Adapter closes, cache updates
  • UpdatePackageRevision: Pass-through to adapter
  • DeletePackageRevision: Adapter deletes, cache invalidates
  • ListPackageRevisions: Adapter lists, cache stores
  • Version: Adapter provides Git SHA, cache tracks
  • Refresh: Adapter re-fetches, cache rebuilds

Credential and Configuration Flow

CaDEngine
     ↓
Cache Options
     ↓
ExternalRepoOptions
     ↓
Repository Adapter
     ↓
Git/OCI Operations

Configuration passed through cache:

  • Credential resolver: Resolves authentication for Git/OCI
  • Reference resolver: Resolves upstream package references
  • User info provider: Provides authenticated user for audit
  • Metadata store: CR Cache metadata storage (CR Cache only)
  • Database handler: PostgreSQL connection (DB Cache only)

Cache doesn’t handle:

  • Authentication to external repositories
  • Git operations (clone, fetch, push)
  • OCI registry operations
  • Package content parsing

Repository Lifecycle Management

Opening repositories:

OpenRepository(spec)
        ↓
  Generate Key
        ↓
  Check Cache Map
        ↓
  Found? ──Yes──> Return Cached
        │
        No
        ↓
  Create Adapter
        ↓
  Wrap & Store
        ↓
  Return New

Closing repositories:

CloseRepository(key)
        ↓
  Stop SyncManager
        ↓
  Delete Metadata
        ↓
  Send Delete Events
        ↓
  Close Adapter
        ↓
  Remove from Map

Repository sharing:

  • Multiple Repository CRs can reference same Git repository
  • Cache checks if repository already open before closing
  • Only closes adapter when last Repository CR deleted
  • Prevents premature connection closure

CaDEngine Access

The CaDEngine accesses the cache through a clean interface:

Cache Interface Operations

CaDEngine
     ↓
Cache.OpenRepository(spec)
     ↓
Repository Interface
     ↓
  ┌──────┴──────┬──────────┬─────────┐
  ↓             ↓          ↓         ↓
List        Create      Update    Delete
Packages    Draft       Draft     Package

CaDEngine workflow:

  1. Open repository through cache
  2. Receive repository interface (cached wrapper)
  3. Call repository methods (ListPackageRevisions, CreatePackageRevisionDraft, etc.)
  4. Cache handles caching, synchronization, notifications
  5. CaDEngine unaware of caching implementation details

Request Flow Examples

List package revisions:

CaDEngine
     ↓
ListPackageRevisions(filter)
     ↓
Cached Repository
     ↓
Check Cache Version
     ↓
Version Match? ──Yes──> Return Cached
     │
     No
     ↓
Fetch from Adapter
     ↓
Update Cache
     ↓
Return Results

Create package revision:

CaDEngine
     ↓
CreatePackageRevisionDraft
     ↓
Cached Repository
     ↓
Pass to Adapter
     ↓
Adapter Creates Draft
     ↓
Return Draft
     ↓
CaDEngine Modifies
     ↓
ClosePackageRevisionDraft
     ↓
Adapter Commits
     ↓
Cache Updates
     ↓
Notify Watchers

Update package revision:

CaDEngine
     ↓
UpdatePackageRevision
     ↓
Cached Repository
     ↓
Unwrap Cached PR
     ↓
Pass to Adapter
     ↓
Adapter Opens Draft
     ↓
Return Draft
     ↓
CaDEngine Modifies
     ↓
ClosePackageRevisionDraft
     ↓
Cache Updates
     ↓
Notify Watchers

Cache Transparency

The cache is transparent to the CaDEngine:

CaDEngine perspective:

  • Calls repository interface methods
  • Receives repository objects
  • Unaware of caching layer
  • Doesn’t know about cache implementation (CR vs DB)
  • Doesn’t manage synchronization

Cache responsibilities:

  • Intercepts repository operations
  • Manages caching strategy
  • Provides data storage and access interfaces
  • Sends change notifications
  • Maintains consistency with Git

Background Synchronization

Repository synchronization is orchestrated by the Repository Controller, a separate component that manages Repository custom resources using the controller-runtime framework. The Repository Controller:

  • Watches Repository resources for spec changes and reconciles them
  • Performs periodic syncs based on configured cron schedules
  • Handles one-time sync requests via spec.sync.runOnceAt
  • Detects changes (added/modified/deleted package revisions)
  • Updates the cache through standard cache interfaces
  • Updates Repository CR status conditions with sync results and package metadata

The cache provides data storage and access interfaces that the Repository Controller uses to store and retrieve package revision data. The controller drives sync operations, while the cache maintains the data layer.

Manual sync:

  • Use porchctl repo sync <repository-name> -n <namespace> for immediate sync
  • Or set spec.sync.runOnceAt in Repository CR to future timestamp

For details on the Repository Controller’s reconciliation logic and sync scheduling, see the Repository Controller documentation. For the cache’s role in synchronization, see Repository Synchronization.

Watcher Notification Integration

The cache notifies watchers of package revision changes:

Notification Flow

Cache Operation
        ↓
Package Changed
        ↓
NotifyPackageRevisionChange
        ↓
Watcher Manager
        ↓
Filter Matching
        ↓
Deliver to Watchers
        ↓
API Server Watch
        ↓
Client Receives Event

Notification triggers:

  • ClosePackageRevisionDraft: Added event for new package revision
  • UpdatePackageRevision: Modified event for updated package revision
  • DeletePackageRevision: Deleted event for removed package revision
  • Background sync: Added/Modified/Deleted events for Git changes
  • Repository close: Deleted events for all package revisions

Notification timing:

  • Sent after cache update completes
  • Sent before metadata store update (avoids race conditions)
  • Sent regardless of metadata store errors
  • Sent synchronously from cache operations

Watch Event Delivery

Event structure:

  • Event type: Added, Modified, Deleted
  • Package revision: Full object with metadata
  • Timestamp: When event occurred

Delivery guarantees:

  • At-least-once: Events may be delivered multiple times
  • Ordered: Events for same package revision ordered
  • Filtered: Only matching watchers receive events
  • Best-effort: Network failures may drop events

Client watch lifecycle:

  1. Client subscribes via API server
  2. Watcher registered with filter
  3. Cache sends notifications
  4. Watcher delivers to client
  5. Client receives watch events
  6. Client disconnects or cancels
  7. Watcher cleaned up automatically