Skip to content

Initial version#3

Open
corviday wants to merge 27 commits intomainfrom
timing
Open

Initial version#3
corviday wants to merge 27 commits intomainfrom
timing

Conversation

@corviday
Copy link
Collaborator

@corviday corviday commented Jan 17, 2026

Creates a simple translation app to sit between the PDP backend and THREDDS and handle the needs of the PDP to access netCDF files in various ways. The primary issue here is that THREDDS's streaming download has a maximum OpenDAP file size of 500 MB, but users often want to download larger files. Requests under 500MB are redirected to THREDDS; requests larger than 500MB are answered by using ncks to create the requested data subset in a new file, and forwarding the user to THREDDS' whole file server to download the new file.

  • Metadata requests (.dds and .das requests) are assumed to be under 500MB and forwarded directly to THREDDS.
  • Sometimes the PDP requests full data for a spatial dimension in ASCII format, which it uses to translate from the map to a netCDF index when users click on the map. These requests are expected to be well under 500MB and are forwarded to THREDDS, but full ASCII data for multidimensional variables like tasmax and pr, which can be larger than 500MB, are not allowed.
  • Download requests are handled by creating a file matching the requested dimensions behind the scenes with ncks and then redirecting the user's browser to THREDD's whole-file download API, which does not have a size limit.

At present, it is easy to run this app out of RAM during the file-creation step; improving RAM usage is an urgently needed next step, but I felt this PR was long enough already, so those improvements will be a separate PR.

Here is a demo PDP stack using this app. Please fool around and find requests the PDP generates that aren't covered yet!

Resolves #1

@corviday corviday force-pushed the timing branch 3 times, most recently from 1eda785 to 70abc7a Compare January 17, 2026 05:50
Copy link

@QSparks QSparks left a comment

Choose a reason for hiding this comment

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

Looks great! Builds fine locally (after updating Poetry) and tests run successfully (no local thredds).

I’ve now tested the demo, see my follow-up comment below. Leaving a few inline comments and two minor suggestions:

  • CI is currently pinned to Python 3.9. Do we have a reason to stay there, or could we move to 3.10/3.11?
  • A few workflows are still using older action versions (actions/checkout@v2, actions/setup-python@v2, docker/build-push-action@v1). It might be worth bumping these to current releases.

import pytest
import os

# WARNING: in the test suite, paths are sometimes specified absolutely and sometimes relatively.
Copy link

Choose a reason for hiding this comment

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

Possible fix using: pathlib-parent

tests_dir = Path(__file__).resolve().parent  # .../tests
os.environ["DATA_ROOT"] = str(tests_dir / "data")

# remaining filepath must end with .nc

# Flask strips the leading slash from the filepath argument, so we strip it here (and add it later)
data_root = os.getenv("DATA_ROOT".lstrip("/"), "storage/")
Copy link

Choose a reason for hiding this comment

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

I think this should be:
os.getenv("DATA_ROOT", "storage/").lstrip("/")

Choose a reason for hiding this comment

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

+1

thredds_base = os.getenv("THREDDS_HTTP_BASE")

logger.info(f"Slicing file")
subprocess.run(
Copy link

Choose a reason for hiding this comment

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

Should we add a timeout or capture stderr in case NCO fails or hangs?


def create_app(config=None):
app = Flask(__name__)
app.config.from_object(config)
Copy link

Choose a reason for hiding this comment

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

I think this is currently a no-op since we don’t pass a config. It might be worth adding a comment noting that this is intentional and keeps the app factory ready for future configuration.

Copy link

@QSparks QSparks left a comment

Choose a reason for hiding this comment

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

Testing the demo this morning. The partition call can exceed Gunicorn’s default 30s timeout, which kills the worker. We’ll need to extend the Gunicorn timeout (and possibly add subprocess timeouts).

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9

Choose a reason for hiding this comment

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

Q already mentioned it but heres a code example from pycds using the python version matrix: https://github.com/pacificclimate/pycds/blob/master/.github/workflows/python-ci.yml#L10

name: Black

on: push

Choose a reason for hiding this comment

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

we can use a concurrency block to cancel tests if this one fails. It'd look something like

concurrency:
  group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
  cancel-in-progress: true

# remaining filepath must end with .nc

# Flask strips the leading slash from the filepath argument, so we strip it here (and add it later)
data_root = os.getenv("DATA_ROOT".lstrip("/"), "storage/")

Choose a reason for hiding this comment

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

+1


def check_filepath(filepath):
"""make sure user has requested a valid file that exists"""
args = {}

Choose a reason for hiding this comment

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

use of a namedtuple or a dataclass here might give you better type checking over a raw dictionary

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.

Initial functionality

3 participants