Skip to content

Add cache to /endpoints/publisher/listing#5462

Merged
codeEmpress1 merged 4 commits intocanonical:mainfrom
codeEmpress1:add-cache-to-/endpoints/publisher/listing
Nov 20, 2025
Merged

Add cache to /endpoints/publisher/listing#5462
codeEmpress1 merged 4 commits intocanonical:mainfrom
codeEmpress1:add-cache-to-/endpoints/publisher/listing

Conversation

@codeEmpress1
Copy link
Copy Markdown
Contributor

Done

  • add cache utility
  • add cache to endpoints/publisher/listing.py

How to QA

  • Go to a snap listing page and check that the page renders as expected
  • update the snap metadata and the details should be updated as expected

Testing

  • This PR has tests
  • No testing required (explain why):

Issue / Card

Fixes #

Screenshots

Copilot AI review requested due to automatic review settings November 19, 2025 08:08
@webteam-app
Copy link
Copy Markdown

Comment thread cache/README.md

## Getting Started

1. Ensure Docker is installed.
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.

every time we run snapcraft locally do we need to do this? or is it just for testing redis?

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.

This is for testing redis locally only.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 adds Redis-based caching to the publisher listing endpoint to improve performance by caching snap metadata retrieved from the dashboard API.

  • Introduces a new cache/ module with Redis utility wrapper and Docker Compose setup for local development
  • Implements caching in get_listing_data() with a 1-hour TTL, keyed by user ID and snap name
  • Adds cache invalidation when metadata or screenshots are successfully updated via post_listing_data()

Reviewed Changes

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

File Description
webapp/endpoints/publisher/listing.py Integrates Redis caching for snap info retrieval and adds cache invalidation after successful updates
cache/cache_utility.py Creates RedisCache instance with namespace and TTL configuration
cache/docker-compose.yaml Provides Docker Compose setup for local Redis development server
cache/README.md Documents how to run and interact with local Redis for development

Comment thread cache/cache_utility.py
Comment thread cache/README.md
5. To stop the container, run:
```
docker compose down
```
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

Missing information about environment variable configuration. The cache utility requires Redis connection settings (host, port, etc.), but the README doesn't explain how to configure these for local development.

Consider adding a section explaining required environment variables or configuration, such as:

## Configuration

The cache utility connects to Redis using the following environment variables:
- `REDIS_HOST` (default: localhost)
- `REDIS_PORT` (default: 6379)

Make sure Redis is running before starting the application.
Suggested change
```

Configuration

The cache utility connects to Redis using the following environment variables:

  • REDIS_HOST (default: localhost)
  • REDIS_PORT (default: 6379)

Make sure Redis is running before starting the application.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +40
snap_details = redis_cache.get(snap_info_key, expected_type=dict)

if not snap_details:
snap_details = dashboard.get_snap_info(flask.session, snap_name)
redis_cache.set(snap_info_key, snap_details, ttl=3600)
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

Missing error handling for cache operations. If Redis is unavailable or encounters an error, the redis_cache.get() call could raise an exception, causing the entire request to fail. The application should gracefully fall back to fetching data directly from the API when the cache is unavailable.

Consider wrapping cache operations in try-except blocks:

snap_info_key = get_snap_info_key(snap_name)
snap_details = None

try:
    snap_details = redis_cache.get(snap_info_key, expected_type=dict)
except Exception:
    # Log the error and continue without cache
    pass

if not snap_details:
    snap_details = dashboard.get_snap_info(flask.session, snap_name)
    try:
        redis_cache.set(snap_info_key, snap_details, ttl=3600)
    except Exception:
        # Log the error but don't fail the request
        pass
Suggested change
snap_details = redis_cache.get(snap_info_key, expected_type=dict)
if not snap_details:
snap_details = dashboard.get_snap_info(flask.session, snap_name)
redis_cache.set(snap_info_key, snap_details, ttl=3600)
snap_details = None
try:
snap_details = redis_cache.get(snap_info_key, expected_type=dict)
except Exception:
# Optionally log the error, but continue without cache
pass
if not snap_details:
snap_details = dashboard.get_snap_info(flask.session, snap_name)
try:
redis_cache.set(snap_info_key, snap_details, ttl=3600)
except Exception:
# Optionally log the error, but don't fail the request
pass

Copilot uses AI. Check for mistakes.
Comment on lines +216 to +218
# Invalidate cache after successful metadata update
snap_info_key = get_snap_info_key(snap_name)
redis_cache.delete(snap_info_key)
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

Missing error handling for cache invalidation. If Redis is unavailable, the redis_cache.delete() call could raise an exception, potentially causing a successful metadata update to appear as failed to the user.

Consider wrapping the cache delete operation in a try-except block:

# Invalidate cache after successful metadata update
snap_info_key = get_snap_info_key(snap_name)
try:
    redis_cache.delete(snap_info_key)
except Exception:
    # Log the error but don't fail the request
    pass

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +201
# Invalidate cache after successful screenshot update
snap_info_key = get_snap_info_key(snap_name)
redis_cache.delete(snap_info_key)
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

Missing error handling for cache invalidation. If Redis is unavailable, the redis_cache.delete() call could raise an exception, potentially causing a successful screenshot update to appear as failed to the user.

Consider wrapping the cache delete operation in a try-except block:

# Invalidate cache after successful screenshot update
snap_info_key = get_snap_info_key(snap_name)
try:
    redis_cache.delete(snap_info_key)
except Exception:
    # Log the error but don't fail the request
    pass

Copilot uses AI. Check for mistakes.
Comment thread webapp/endpoints/publisher/listing.py Outdated
Comment on lines +27 to +30
user_id = flask.session.get(
"publisher", {}
).get("identity_url", "").split("+id")[1]

Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The split("+id")[1] operation will raise an IndexError if the identity_url doesn't contain "+id" or if it ends with "+id". This can occur if the session data is malformed or if the identity URL format changes.

Consider adding error handling or validation:

def get_snap_info_key(snap_name):
    identity_url = flask.session.get("publisher", {}).get("identity_url", "")
    
    if "+id" not in identity_url:
        raise ValueError("Invalid identity_url format")
    
    user_id = identity_url.split("+id")[1]
    
    if not user_id:
        raise ValueError("User ID not found in identity_url")
    
    return f"{user_id}:snap_info:{snap_name}"
Suggested change
user_id = flask.session.get(
"publisher", {}
).get("identity_url", "").split("+id")[1]
identity_url = flask.session.get("publisher", {}).get("identity_url", "")
if "+id" not in identity_url:
raise ValueError("Invalid identity_url format: '+id' not found")
parts = identity_url.split("+id", 1)
if len(parts) < 2 or not parts[1]:
raise ValueError("User ID not found in identity_url")
user_id = parts[1]

Copilot uses AI. Check for mistakes.
Comment thread cache/README.md
Copy link
Copy Markdown
Contributor

@alvaromateo alvaromateo left a comment

Choose a reason for hiding this comment

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

LGTM

The only thing that I would like to add if possible are a bit of logging for when we set/delete info in Redis, to be able to track the flow of our program properly. If I remember correctly there were some logs already in the RedisCache utility? Otherwise we should add them.

@codeEmpress1
Copy link
Copy Markdown
Contributor Author

LGTM

The only thing that I would like to add if possible are a bit of logging for when we set/delete info in Redis, to be able to track the flow of our program properly. If I remember correctly there were some logs already in the RedisCache utility? Otherwise we should add them.

@alvaromateo we have logs already in the RedisCache utility in storeAPI module

@codeEmpress1
Copy link
Copy Markdown
Contributor Author

LGTM
The only thing that I would like to add if possible are a bit of logging for when we set/delete info in Redis, to be able to track the flow of our program properly. If I remember correctly there were some logs already in the RedisCache utility? Otherwise we should add them.

@alvaromateo we have logs already in the RedisCache utility in storeAPI module

I would update the failing tests and ping you for re-approval

@edisile edisile self-requested a review November 19, 2025 08:44
Comment thread webapp/endpoints/publisher/listing.py Outdated
@codeEmpress1 codeEmpress1 force-pushed the add-cache-to-/endpoints/publisher/listing branch from 81aec92 to e72fec8 Compare November 19, 2025 10:40
@codeEmpress1 codeEmpress1 force-pushed the add-cache-to-/endpoints/publisher/listing branch from 95e174f to f1ae166 Compare November 20, 2025 07:41
@codeEmpress1 codeEmpress1 merged commit ac12365 into canonical:main Nov 20, 2025
9 checks passed
@codeEmpress1 codeEmpress1 deleted the add-cache-to-/endpoints/publisher/listing branch November 20, 2025 08:04
@codecov
Copy link
Copy Markdown

codecov Bot commented Nov 20, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (b8b6b55) to head (f1ae166).
⚠️ Report is 559 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #5462       +/-   ##
==========================================
- Coverage   66.80%       0   -66.81%     
==========================================
  Files         113       0      -113     
  Lines        3714       0     -3714     
  Branches      965       0      -965     
==========================================
- Hits         2481       0     -2481     
+ Misses       1098       0     -1098     
+ Partials      135       0      -135     

see 62 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants