Skip to content

Conversation

YuriZmytrakov
Copy link
Collaborator

@YuriZmytrakov YuriZmytrakov commented Oct 1, 2025

Related Issue(s):

Description:

Add Redis caching support for navigation pagination to enable proper prev/next links in STAC API responses.

PR Checklist:

  • Code is formatted and linted (run pre-commit run --all-files)
  • Tests pass (run make test)
  • Documentation has been updated to reflect changes, if applicable
  • Changes are added to the changelog

@YuriZmytrakov YuriZmytrakov changed the title Cat 1382 feat: Add Redis caching for navigation pagination Oct 1, 2025
@YuriZmytrakov YuriZmytrakov marked this pull request as draft October 1, 2025 19:10
@YuriZmytrakov YuriZmytrakov marked this pull request as ready for review October 1, 2025 19:48
Copy link
Collaborator

@jonhealy1 jonhealy1 left a comment

Choose a reason for hiding this comment

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

Great stuff here. Just a few thoughts, requests.

"pygeofilter~=0.3.1",
"jsonschema~=4.0.0",
"slowapi~=0.1.9",
"redis==6.4.0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's move this to extras so people can do this pip install stac-fastapi-core[redis] if they need redis. Some may not want to install the library if they are not using redis.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have moved redis into extras, thank you!

"elasticsearch[async]~=8.18.0",
"uvicorn~=0.23.0",
"starlette>=0.35.0,<0.36.0",
"redis==6.4.0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can remove redis from elasticsearch, opensearch and sfeos_helpers

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

"opensearch-py[async]~=2.8.0",
"uvicorn~=0.23.0",
"starlette>=0.35.0,<0.36.0",
"redis==6.4.0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can remove redis from elasticsearch, opensearch and sfeos_helpers

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.


install_requires = [
"stac-fastapi.core==6.5.1",
"redis==6.4.0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove here too

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done.


> [!NOTE]
> Use either the Sentinel configuration (`REDIS_SENTINEL_HOSTS`, `REDIS_SENTINEL_PORTS`, `REDIS_SENTINEL_MASTER_NAME`) OR the Redis configuration (`REDIS_HOST`, `REDIS_PORT`), but not both.
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should add a REDIS_ENABLED global env var to turn on/off redis. The try/except blocks can add latency to the application if someone doesn't want to use Redis and the api keeps trying to connect.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have introduced REDIS_ENABLED env var that is used to indicate if redis has been configured and enabled.


current_url = str(request.url)
redis = None
try:
Copy link
Collaborator

@jonhealy1 jonhealy1 Oct 6, 2025

Choose a reason for hiding this comment

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

before the try we should check to see if REDIS_ENABLED is true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed, thank you!

HTTPException: If there is an error with the cql2_json filter.
"""
base_url = str(request.base_url)
try:
Copy link
Collaborator

Choose a reason for hiding this comment

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

here too, check REDIS_ENABLED

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed, thank you!

"""
base_url = str(request.base_url)
try:
redis = await connect_redis_sentinel()
Copy link
Collaborator

Choose a reason for hiding this comment

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

connect_redis here too I think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed, thank you!


result_missing = await get_prev_link(mock_redis, "dummy_token_2")
assert result_missing is None
mock_redis.get.assert_awaited_once_with("nav:self:dummy_token_2")
Copy link
Collaborator

Choose a reason for hiding this comment

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

@YuriZmytrakov Should we have integration testing with a live redis server? We would need to set up redis in gh actions too. I think it's a good idea.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you for the recommendation, I have added integration tests in this commits, and its using a live redis server as suggested. Github Actions have been configured too
e04fa57
601a8a4

RUN pip install --no-cache-dir -e ./stac_fastapi/core
RUN pip install --no-cache-dir -e ./stac_fastapi/sfeos_helpers
RUN pip install --no-cache-dir -e ./stac_fastapi/elasticsearch[dev,server]
RUN pip install --no-cache-dir redis types-redis
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need this? we should create a new compose.yml file and add redis to it for local testing I think - compose-redis.yml or something like that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here is a compose config for a redis cache

redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
volumes:
  redis_data:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was fixed, thank you @jonhealy1 . Thank you for the suggested code @jamesfisher-geo .

links = await PagingLinks(request=request, next=next_token).get_links()

collection_links = []
if (
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this needed? I believe at least the parent link is automatically created by the API with https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/blob/main/stac_fastapi/core/stac_fastapi/core/models/links.py#L125

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, unfortunately this was reported as a bug, the parent and collection relations are missing. For this reason, I’m resolving it here. This was the original issue for this ticket, which eventually evolved into the Redis cache for navigation project.

await save_self_link(redis, next_token, self_link)

prev_link = await get_prev_link(redis, token_param)
if prev_link:
Copy link
Collaborator

Choose a reason for hiding this comment

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

How do you feel about handling the previous paging link additions inside the PagingLinks class? This would be more maintainable from a code perspective.

You could also pass the previous link into get_links with the extra_links parameter?

https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/blob/main/stac_fastapi/core/stac_fastapi/core/models/links.py#L92

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, I tried this approach as well. However, I’d need to pass the Redis connection into the PagingLinks class. Also, the token for the previous link is derived from the current token, which isn’t available in PagingLinks it’s only accessible in the current link that I retrieve in the endpoint where caching is enabled. Hope this makes sense.

RUN pip install --no-cache-dir -e ./stac_fastapi/core
RUN pip install --no-cache-dir -e ./stac_fastapi/sfeos_helpers
RUN pip install --no-cache-dir -e ./stac_fastapi/elasticsearch[dev,server]
RUN pip install --no-cache-dir redis types-redis
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here is a compose config for a redis cache

redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
volumes:
  redis_data:

@YuriZmytrakov YuriZmytrakov force-pushed the CAT-1382 branch 5 times, most recently from 42d6c98 to c056704 Compare October 8, 2025 09:50
jonhealy1 and others added 6 commits October 8, 2025 12:08
**Related Issue(s):**

- stac-utils#461 

**Description:**

- GET `/collections` collection search fields extension ex.
`/collections?fields=id,title`.
[stac-utils#465](stac-utils#465)
- Improved error messages for sorting on unsortable fields in collection
search, including guidance on how to make fields sortable.
[stac-utils#465](stac-utils#465)
- Added field alias for `temporal` to enable easier sorting by temporal
extent, alongside `extent.temporal.interval`.
[stac-utils#465](stac-utils#465)

**PR Checklist:**

- [x] Code is formatted and linted (run `pre-commit run --all-files`)
- [x] Tests pass (run `make test`)
- [x] Documentation has been updated to reflect changes, if applicable
- [x] Changes are added to the changelog
**Related Issue(s):**

- stac-utils#468 

**Description:**


**PR Checklist:**

- [x] Code is formatted and linted (run `pre-commit run --all-files`)
- [x] Tests pass (run `make test`)
- [x] Documentation has been updated to reflect changes, if applicable
- [x] Changes are added to the changelog
**Related Issue(s):**

- stac-utils#460

**Description:**

ex. `/collections?q=Sentinel-2a`

**PR Checklist:**

- [x] Code is formatted and linted (run `pre-commit run --all-files`)
- [x] Tests pass (run `make test`)
- [x] Documentation has been updated to reflect changes, if applicable
- [x] Changes are added to the changelog
**Related Issue(s):**

- None

**Description:**

- Release v6.4.0

**PR Checklist:**

- [x] Code is formatted and linted (run `pre-commit run --all-files`)
- [x] Tests pass (run `make test`)
- [x] Documentation has been updated to reflect changes, if applicable
- [x] Changes are added to the changelog
@YuriZmytrakov
Copy link
Collaborator Author

YuriZmytrakov commented Oct 8, 2025

@jonhealy1 @jamesfisher-geo apologies, I have to close this PR and cherry-pick the changes into a new one. This PR has been sitting here for a while, and any change now triggers too many conflicts to resolve.
New PR: #488

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.

3 participants