gwdatafind-server 2.1.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/.gitlab-ci.yml +1 -0
  2. gwdatafind_server-2.3.0/AGENTS.md +94 -0
  3. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/CHANGELOG.md +18 -0
  4. gwdatafind_server-2.3.0/CLAUDE.md +94 -0
  5. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/Dockerfile +9 -9
  6. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/PKG-INFO +1 -1
  7. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/gunicorn.conf.py +7 -9
  8. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/gwdatafind-server.toml +1 -0
  9. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docker-compose.yml +12 -5
  10. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/changelog.md +18 -0
  11. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/configuration.md +1 -3
  12. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/auth/docker-compose.yml +9 -2
  13. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/auth/gwdatafind-server.toml +4 -0
  14. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/diskcache/docker-compose.yml +10 -1
  15. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/diskcache/gwdatafind-server.toml +1 -0
  16. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/__init__.py +14 -8
  17. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/_version.py +3 -3
  18. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/app.py +28 -22
  19. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/diskcache.py +1 -2
  20. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/pelican.py +192 -69
  21. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/PKG-INFO +1 -1
  22. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/SOURCES.txt +4 -0
  23. gwdatafind_server-2.3.0/gwdatafind_server.egg-info/scm_file_list.json +82 -0
  24. gwdatafind_server-2.3.0/gwdatafind_server.egg-info/scm_version.json +8 -0
  25. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/pyproject.toml +13 -4
  26. gwdatafind_server-2.3.0/tests/test_app.py +122 -0
  27. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_source_pelican.py +145 -13
  28. gwdatafind_server-2.1.0/tests/test_app.py +0 -6
  29. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/.gitignore +0 -0
  30. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/.markdownlint.yml +0 -0
  31. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/LICENSE +0 -0
  32. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/MANIFEST.in +0 -0
  33. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/README.md +0 -0
  34. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/docker-compose.yml +0 -0
  35. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/gwdatafind-server.service +0 -0
  36. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/diskcache/Makefile +0 -0
  37. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/diskcache/create_testdata.py +0 -0
  38. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/diskcache/diskcache.rsc +0 -0
  39. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/api/index.md +0 -0
  40. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/api/ldr.md +0 -0
  41. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/api/v1.md +0 -0
  42. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/auth.md +0 -0
  43. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/diskcache.md +0 -0
  44. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/gwosc.md +0 -0
  45. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/index.md +0 -0
  46. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/index.md +0 -0
  47. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/index.md +0 -0
  48. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/overview.md +0 -0
  49. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/requirements.txt +0 -0
  50. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/__init__.py +0 -0
  51. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/base.py +0 -0
  52. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/ldr.py +0 -0
  53. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/utils.py +0 -0
  54. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/v1.py +0 -0
  55. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/auth/__init__.py +0 -0
  56. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/auth/core.py +0 -0
  57. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/auth/scitoken.py +0 -0
  58. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/cache.py +0 -0
  59. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/config.py +0 -0
  60. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/credential/__init__.py +0 -0
  61. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/credential/credential.py +0 -0
  62. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/credential/scitoken.py +0 -0
  63. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/gwdatafind_server.wsgi +0 -0
  64. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/__init__.py +0 -0
  65. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/source.py +0 -0
  66. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/urls.py +0 -0
  67. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/dependency_links.txt +0 -0
  68. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/requires.txt +0 -0
  69. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/top_level.txt +0 -0
  70. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/mkdocs.yml +0 -0
  71. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/setup.cfg +0 -0
  72. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/__init__.py +0 -0
  73. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/cache-0x00ff.dat +0 -0
  74. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/cache-0x0101.dat +0 -0
  75. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/conftest.py +0 -0
  76. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/grid-mapfile +0 -0
  77. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/gwdatafind-server.ini +0 -0
  78. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/gwdatafind-server.toml +0 -0
  79. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_api_v1.py +0 -0
  80. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_auth_override.py +0 -0
  81. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_auth_scitoken.py +0 -0
  82. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_cache.py +0 -0
  83. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_config.py +0 -0
  84. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_credential.py +0 -0
  85. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_credential_scitoken.py +0 -0
  86. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_source.py +0 -0
  87. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_source_diskcache.py +0 -0
  88. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_urls.py +0 -0
  89. {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/utils.py +0 -0
@@ -53,6 +53,7 @@ include:
53
53
  --bind=0.0.0.0:8080
54
54
  --config=/etc/gwdatafind/gunicorn.conf.py
55
55
  --forwarded-allow-ips="*"
56
+ --workers=1
56
57
  "gwdatafind_server:create_app()"
57
58
  # deploy options
58
59
  deploy_tag: $CI_COMMIT_REF_NAME # use verbatim tag name
@@ -0,0 +1,94 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents (and humans) working in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ GWDataFind Server is a [Flask](https://flask.palletsprojects.com/) application that
8
+ implements the HTTP API used to discover URLs for gravitational-wave observatory data
9
+ files.
10
+ It is developed by the LIGO Scientific Collaboration / IGWN and used in production
11
+ to serve data discovery requests for LIGO/Virgo/KAGRA data.
12
+
13
+ The accompanying Python client library (`gwdatafind`, not in this repo) is what most
14
+ end users interact with; this repo is the server side of that API.
15
+
16
+ Canonical docs live in `docs/` (built with MkDocs, published at
17
+ <https://computing.docs.ligo.org/gwdatafind/server/>).
18
+ Read `docs/overview.md` first for architecture,
19
+ and `docs/configuration.md` for the full config schema —
20
+ both are kept up to date and should be treated as source of truth over any summary here.
21
+
22
+ ## Architecture (brief)
23
+
24
+ - The app (`gwdatafind_server/app.py`, `GWDataFindApp`) runs one or more **sources**
25
+ (`gwdatafind_server/source/`: `diskcache`, `pelican`) on independent async schedules
26
+ (via `flask-apscheduler`). Each source discovers fully-qualified URLs
27
+ (`file://`, `pelican://`, etc.) plus metadata (observatory, dataset, format, GPS
28
+ segment) and feeds it into an in-memory `Cache` (`gwdatafind_server/cache.py`).
29
+ - **URL transforms** (`gwdatafind_server/urls.py`, configured under `[url.transform]`)
30
+ rewrite URLs between schemes (e.g. `pelican://` → `https://`) at query time.
31
+ - **Auth** (`gwdatafind_server/auth/`) supports SciToken bearer-token authorization and
32
+ per-IP/subnet overrides.
33
+ - **Credentials** (`gwdatafind_server/credential/`) are refreshed on a schedule and
34
+ attached to sources that need them (e.g. SciTokens obtained via Kerberos keytab for
35
+ Pelican access).
36
+ - **API** (`gwdatafind_server/api/`): versioned HTTP endpoints (`ldr` legacy, `v1`
37
+ current) built on the shared cache; see `docs/api/`.
38
+ - Configuration is TOML (`gwdatafind_server/config.py`), one config file drives the
39
+ whole app. `python3 -m gwdatafind_server.config <file>` parses and dumps it as JSON
40
+ for validation.
41
+
42
+ ## Repo layout
43
+
44
+ - `gwdatafind_server/` — the application package (source of truth for behavior).
45
+ - `tests/` — pytest suite, one `test_*.py` per module roughly mirroring the package.
46
+ - `docs/` — MkDocs site (nav defined in `mkdocs.yml`); includes API reference and
47
+ deployment examples (`docs/deploy/examples/`).
48
+ - `examples/` — runnable docker-compose deployment examples (diskcache, auth/SciToken).
49
+ - `config/` — example/reference config files (TOML, gunicorn, systemd unit) referenced
50
+ by the docs via `include-markdown`; treat as documentation fixtures, not scratch space.
51
+ - `Dockerfile`, `docker-compose.yml` — container build/quick-start.
52
+ - `.gitlab-ci.yml` — CI pipeline (GitLab, not GitHub). Runs lint/type-check/tests across
53
+ Python 3.11–3.14, builds the Docker image, and builds the MkDocs site.
54
+
55
+ ## Development practices
56
+
57
+ - **Dependency management**: [`uv`](https://docs.astral.sh/uv/).
58
+ Use `uv run …` / `uv sync --group dev` rather than raw `pip`.
59
+ - **Tests**: `pytest` (`uv run pytest`).
60
+ Config in `pyproject.toml` (`[tool.pytest.ini_options]`);
61
+ warnings are errors (`filterwarnings = ["error", ...]`),
62
+ so new code must not raise unexpected warnings.
63
+ Coverage is measured against `gwdatafind_server`.
64
+ - **Linting**: `ruff`, with `select = ["ALL"]` and an explicit ignore list in
65
+ `pyproject.toml`.
66
+ Run `uv run ruff check` before considering work done.
67
+ Per-directory ignores exist for `tests/`, `config/`, `docs/`.
68
+ - **Type checking**: [`ty`](https://github.com/astral-sh/ty) (not mypy) —
69
+ see `[tool.ty.src]` in `pyproject.toml`.
70
+ Note the codebase mostly ignores annotation lint rules (`ANN` is ruff-ignored)
71
+ but does use `from __future__ import annotations` and `TYPE_CHECKING` blocks.
72
+ - **Docs**: MkDocs + `mkdocs-material`-based theme, built via a shared CI component.
73
+ Markdown is linted with `markdownlint` (`.markdownlint.yml`); config docs pull live
74
+ examples from `config/` via the `include-markdown` plugin, so keep those files valid.
75
+ - **Commit style**: Conventional-commit-like scoped messages are the norm, e.g.
76
+ `feat(config): add support for source update jitter`,
77
+ `fix(pelican): don't skip recursion using cache`,
78
+ `build(docker-compose): add volume for pelican caches`.
79
+ Follow this pattern.
80
+ - **Workflow**: Development happens on GitLab (`git.ligo.org/computing/gwdatafind/server`)
81
+ via merge requests into `main`;
82
+ releases are tagged and generate `CHANGELOG.md` entries per version.
83
+ Licensed GPL-3.0-or-later — keep the copyright/license header convention
84
+ (`# Copyright (c) 2019-2025 Cardiff University` /
85
+ `# Licensed under GPLv3+ - see LICENSE`) on new source files.
86
+
87
+ ## Sensitive areas
88
+
89
+ - **Auth/credential code** (`gwdatafind_server/auth/`, `gwdatafind_server/credential/`)
90
+ gates access to (sometimes proprietary/embargoed) LIGO/Virgo/KAGRA data —
91
+ change carefully and preserve existing test coverage.
92
+ - **URL transform logic** (`gwdatafind_server/urls.py`) is order-sensitive and affects
93
+ which URLs get served for which scheme; see the "Transform behavior" warning in
94
+ `docs/configuration.md` before changing matching/precedence semantics.
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.3.0 - 2025-07-06 { #2.3.0 }
4
+
5
+ - Load source cache upfront during app startup, use concurrent gunicorn workers
6
+ ([!133](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/133))
7
+ - Improve Pelican sqlite db transaction locking
8
+ ([!134](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/134))
9
+
10
+ [Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.3.0)
11
+
12
+ ## 2.2.0 - 2025-04-09 { #2.2.0 }
13
+
14
+ - Fix directory recursion with Pelican memory cache
15
+ ([!129](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/129))
16
+ - Add Pelican cache versioning and compatibility checks
17
+ ([!130](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/130))
18
+
19
+ [Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.2.0)
20
+
3
21
  ## 2.1.0 - 2025-03-26 { #2.1.0 }
4
22
 
5
23
  - Add Pelican database cache file
@@ -0,0 +1,94 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents (and humans) working in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ GWDataFind Server is a [Flask](https://flask.palletsprojects.com/) application that
8
+ implements the HTTP API used to discover URLs for gravitational-wave observatory data
9
+ files.
10
+ It is developed by the LIGO Scientific Collaboration / IGWN and used in production
11
+ to serve data discovery requests for LIGO/Virgo/KAGRA data.
12
+
13
+ The accompanying Python client library (`gwdatafind`, not in this repo) is what most
14
+ end users interact with; this repo is the server side of that API.
15
+
16
+ Canonical docs live in `docs/` (built with MkDocs, published at
17
+ <https://computing.docs.ligo.org/gwdatafind/server/>).
18
+ Read `docs/overview.md` first for architecture,
19
+ and `docs/configuration.md` for the full config schema —
20
+ both are kept up to date and should be treated as source of truth over any summary here.
21
+
22
+ ## Architecture (brief)
23
+
24
+ - The app (`gwdatafind_server/app.py`, `GWDataFindApp`) runs one or more **sources**
25
+ (`gwdatafind_server/source/`: `diskcache`, `pelican`) on independent async schedules
26
+ (via `flask-apscheduler`). Each source discovers fully-qualified URLs
27
+ (`file://`, `pelican://`, etc.) plus metadata (observatory, dataset, format, GPS
28
+ segment) and feeds it into an in-memory `Cache` (`gwdatafind_server/cache.py`).
29
+ - **URL transforms** (`gwdatafind_server/urls.py`, configured under `[url.transform]`)
30
+ rewrite URLs between schemes (e.g. `pelican://` → `https://`) at query time.
31
+ - **Auth** (`gwdatafind_server/auth/`) supports SciToken bearer-token authorization and
32
+ per-IP/subnet overrides.
33
+ - **Credentials** (`gwdatafind_server/credential/`) are refreshed on a schedule and
34
+ attached to sources that need them (e.g. SciTokens obtained via Kerberos keytab for
35
+ Pelican access).
36
+ - **API** (`gwdatafind_server/api/`): versioned HTTP endpoints (`ldr` legacy, `v1`
37
+ current) built on the shared cache; see `docs/api/`.
38
+ - Configuration is TOML (`gwdatafind_server/config.py`), one config file drives the
39
+ whole app. `python3 -m gwdatafind_server.config <file>` parses and dumps it as JSON
40
+ for validation.
41
+
42
+ ## Repo layout
43
+
44
+ - `gwdatafind_server/` — the application package (source of truth for behavior).
45
+ - `tests/` — pytest suite, one `test_*.py` per module roughly mirroring the package.
46
+ - `docs/` — MkDocs site (nav defined in `mkdocs.yml`); includes API reference and
47
+ deployment examples (`docs/deploy/examples/`).
48
+ - `examples/` — runnable docker-compose deployment examples (diskcache, auth/SciToken).
49
+ - `config/` — example/reference config files (TOML, gunicorn, systemd unit) referenced
50
+ by the docs via `include-markdown`; treat as documentation fixtures, not scratch space.
51
+ - `Dockerfile`, `docker-compose.yml` — container build/quick-start.
52
+ - `.gitlab-ci.yml` — CI pipeline (GitLab, not GitHub). Runs lint/type-check/tests across
53
+ Python 3.11–3.14, builds the Docker image, and builds the MkDocs site.
54
+
55
+ ## Development practices
56
+
57
+ - **Dependency management**: [`uv`](https://docs.astral.sh/uv/).
58
+ Use `uv run …` / `uv sync --group dev` rather than raw `pip`.
59
+ - **Tests**: `pytest` (`uv run pytest`).
60
+ Config in `pyproject.toml` (`[tool.pytest.ini_options]`);
61
+ warnings are errors (`filterwarnings = ["error", ...]`),
62
+ so new code must not raise unexpected warnings.
63
+ Coverage is measured against `gwdatafind_server`.
64
+ - **Linting**: `ruff`, with `select = ["ALL"]` and an explicit ignore list in
65
+ `pyproject.toml`.
66
+ Run `uv run ruff check` before considering work done.
67
+ Per-directory ignores exist for `tests/`, `config/`, `docs/`.
68
+ - **Type checking**: [`ty`](https://github.com/astral-sh/ty) (not mypy) —
69
+ see `[tool.ty.src]` in `pyproject.toml`.
70
+ Note the codebase mostly ignores annotation lint rules (`ANN` is ruff-ignored)
71
+ but does use `from __future__ import annotations` and `TYPE_CHECKING` blocks.
72
+ - **Docs**: MkDocs + `mkdocs-material`-based theme, built via a shared CI component.
73
+ Markdown is linted with `markdownlint` (`.markdownlint.yml`); config docs pull live
74
+ examples from `config/` via the `include-markdown` plugin, so keep those files valid.
75
+ - **Commit style**: Conventional-commit-like scoped messages are the norm, e.g.
76
+ `feat(config): add support for source update jitter`,
77
+ `fix(pelican): don't skip recursion using cache`,
78
+ `build(docker-compose): add volume for pelican caches`.
79
+ Follow this pattern.
80
+ - **Workflow**: Development happens on GitLab (`git.ligo.org/computing/gwdatafind/server`)
81
+ via merge requests into `main`;
82
+ releases are tagged and generate `CHANGELOG.md` entries per version.
83
+ Licensed GPL-3.0-or-later — keep the copyright/license header convention
84
+ (`# Copyright (c) 2019-2025 Cardiff University` /
85
+ `# Licensed under GPLv3+ - see LICENSE`) on new source files.
86
+
87
+ ## Sensitive areas
88
+
89
+ - **Auth/credential code** (`gwdatafind_server/auth/`, `gwdatafind_server/credential/`)
90
+ gates access to (sometimes proprietary/embargoed) LIGO/Virgo/KAGRA data —
91
+ change carefully and preserve existing test coverage.
92
+ - **URL transform logic** (`gwdatafind_server/urls.py`) is order-sensitive and affects
93
+ which URLs get served for which scheme; see the "Transform behavior" warning in
94
+ `docs/configuration.md` before changing matching/precedence semantics.
@@ -2,18 +2,14 @@
2
2
 
3
3
  FROM ghcr.io/astral-sh/uv:python3.14-bookworm AS build
4
4
 
5
- ARG SETUPTOOLS_SCM_PRETEND_VERSION
6
-
7
5
  WORKDIR /code
8
6
 
9
- COPY .git .git
10
7
  COPY . ./
11
8
 
12
9
  # Generate a wheel
13
- RUN if [ -n "$SETUPTOOLS_SCM_PRETEND_VERSION" ]; then \
14
- export SETUPTOOLS_SCM_PRETEND_VERSION="$SETUPTOOLS_SCM_PRETEND_VERSION"; \
15
- fi \
16
- && uv build --wheel
10
+ RUN git config --global safe.directory $(pwd) \
11
+ && rm -rf docker_dist/*.whl \
12
+ && uv build --wheel --out-dir docker_dist/
17
13
 
18
14
  # -- install
19
15
 
@@ -28,10 +24,14 @@ RUN uv venv ${VIRTUAL_ENV}
28
24
 
29
25
  # Install the requirements
30
26
  COPY pyproject.toml .
31
- RUN uv pip install -r pyproject.toml
27
+ RUN uv pip install \
28
+ -r pyproject.toml \
29
+ --extra credentials \
30
+ --extra gunicorn \
31
+ --extra pelican
32
32
 
33
33
  # Install the app
34
- COPY --from=build /code/dist/*.whl /code
34
+ COPY --from=build /code/docker_dist/*.whl /code
35
35
  RUN WHEEL=$(ls /code/*.whl) \
36
36
  && uv pip install $WHEEL[credentials,gunicorn,pelican]
37
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gwdatafind-server
3
- Version: 2.1.0
3
+ Version: 2.3.0
4
4
  Summary: The GWDataFind data discover server application
5
5
  Author-email: Duncan Macleod <duncan.macleod@ligo.org>, Duncan Meacher <duncan.meacher@ligo.org>
6
6
  License: GPL-3.0-or-later
@@ -11,20 +11,17 @@ bind = "0.0.0.0:8080"
11
11
  backlog = 2048
12
12
 
13
13
  # Worker processes
14
- workers = 1
14
+ workers = 2
15
15
  worker_class = "gthread"
16
16
  worker_connections = 1000
17
- threads = 8
17
+ threads = 4
18
18
  timeout = 30
19
19
  keepalive = 2
20
20
 
21
- # Restart workers after this many requests, with up to this much jitter
22
- max_requests = 1000
23
- max_requests_jitter = 100
24
-
25
- # Worker recycling
26
- max_worker_memory = 134217728 # 128MB in bytes
27
- worker_tmp_dir = "/dev/shm"
21
+ # Restart workers after this many requests, with up to this much jitter.
22
+ # Keep this high enough to avoid frequent cache rebuilds.
23
+ max_requests = 10000
24
+ max_requests_jitter = 1000
28
25
 
29
26
  # Security
30
27
  limit_request_line = 4094
@@ -104,6 +101,7 @@ if os.environ.get("GWDATAFIND_SERVER_DEBUG"):
104
101
  loglevel = "debug"
105
102
  reload = True
106
103
  workers = 1
104
+ threads = 4
107
105
 
108
106
  if gwdfworkers := os.environ.get("GWDATAFIND_WORKERS"):
109
107
  workers = int(gwdfworkers)
@@ -36,6 +36,7 @@ type = "pelican"
36
36
  federation_url = "osg-htc.org"
37
37
  path = "/gwdata"
38
38
  interval = 10800
39
+ jitter = 600
39
40
  cachefile = "/var/lib/gwdatafind/pelican-gwosc.db"
40
41
 
41
42
  # -- credentials ------------
@@ -10,7 +10,7 @@
10
10
  services:
11
11
  gwdatafind:
12
12
  container_name: gwdatafind-server
13
- image: gwdatafind-server:latest
13
+ image: ${GWDATAFIND_SERVER_IMAGE-containers.ligo.org/computing/gwdatafind/server:latest}
14
14
  ports:
15
15
  - "127.0.0.1:8080:8080" # Bind only to localhost
16
16
  restart: unless-stopped
@@ -26,13 +26,16 @@ services:
26
26
  # Volumes
27
27
  volumes:
28
28
  - type: bind
29
- source: config/gwdatafind-server.toml
29
+ source: ${GWDATAFIND_SERVER_CONFIG-config/gwdatafind-server.toml}
30
30
  target: /etc/gwdatafind/gwdatafind-server.toml
31
31
  read_only: true
32
32
  - type: tmpfs
33
33
  target: /tmp
34
34
  tmpfs:
35
35
  size: 100m
36
+ - type: volume
37
+ source: gwdatafind-data
38
+ target: /var/lib/gwdatafind
36
39
 
37
40
  # Environment
38
41
  environment:
@@ -41,11 +44,11 @@ services:
41
44
 
42
45
  # Health check
43
46
  healthcheck:
44
- test: ["CMD", "curl", "-f", "http://localhost:8080/api/version", "--max-time", "5"]
47
+ test: ["CMD-SHELL", "pgrep -f 'gunicorn.*gwdatafind_server:create_app\\(\\)' >/dev/null"]
45
48
  interval: 30s
46
- timeout: 10s
49
+ timeout: 5s
47
50
  retries: 3
48
- start_period: 40s
51
+ start_period: 30s
49
52
 
50
53
  # Logging
51
54
  logging:
@@ -58,3 +61,7 @@ services:
58
61
  labels:
59
62
  - "com.example.service=gwdatafind-server"
60
63
  - "com.example.version=latest"
64
+
65
+ volumes:
66
+ gwdatafind-data:
67
+ driver: local
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.3.0 - 2025-07-06 { #2.3.0 }
4
+
5
+ - Load source cache upfront during app startup, use concurrent gunicorn workers
6
+ ([!133](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/133))
7
+ - Improve Pelican sqlite db transaction locking
8
+ ([!134](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/134))
9
+
10
+ [Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.3.0)
11
+
12
+ ## 2.2.0 - 2025-04-09 { #2.2.0 }
13
+
14
+ - Fix directory recursion with Pelican memory cache
15
+ ([!129](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/129))
16
+ - Add Pelican cache versioning and compatibility checks
17
+ ([!130](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/130))
18
+
19
+ [Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.2.0)
20
+
3
21
  ## 2.1.0 - 2025-03-26 { #2.1.0 }
4
22
 
5
23
  - Add Pelican database cache file
@@ -257,6 +257,7 @@ All source types accept the following options:
257
257
  | `name` | [string](https://toml.io/en/v1.0.0#string) | [optional] A unique name for this source (defaults to "{type}_source_{index}") |
258
258
  | `interval` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] How often (in seconds) to query for new data |
259
259
  | `initial_delay` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] Delay (in seconds) before the first data collection |
260
+ | `jitter` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] Maximum random jitter (in seconds) to add to the interval between data collections (default: 0) |
260
261
  | `credential` | [string](https://toml.io/en/v1.0.0#string) | [optional] Name of the credential to use for authentication |
261
262
  | `split_sites` | [boolean](https://toml.io/en/v1.0.0#boolean) | [optional] If `true`, multi-site files will be indexed for individual sites, as well as the combination (default: `false`) |
262
263
 
@@ -270,7 +271,6 @@ other keys:
270
271
  | `host` | [string](https://toml.io/en/v1.0.0#string) | The address of the diskcache instance |
271
272
  | `port` | [integer](https://toml.io/en/v1.0.0#integer) | The port to talk to on the diskcache host |
272
273
  | `path` | [string](https://toml.io/en/v1.0.0#string) | Alternatively, the path of a Diskcache ASCII output file to read |
273
- | `interval` | [integer](https://toml.io/en/v1.0.0#integer) | How often (in seconds) to request information from the diskcache host, or to reread the diskcache file |
274
274
 
275
275
  !!! example "Diskcache input configuration"
276
276
 
@@ -306,7 +306,6 @@ other keys:
306
306
  | `token` | [boolean](https://toml.io/en/v1.0.0#boolean) | If `true`, a token will be included in requests to the Pelican host (default `false`) |
307
307
  | `credential` | [string](https://toml.io/en/v1.0.0#string) | [optional] Name of the credential to use for authentication (alternative to `token`) |
308
308
  | `cachefile` | [string](https://toml.io/en/v1.0.0#string) | [optional] Path to SQLite cache file for persistent storage of directory listings (improves startup performance) |
309
- | `interval` | [integer](https://toml.io/en/v1.0.0#integer) | How often (in seconds) to query for new data from the Pelican host |
310
309
 
311
310
  !!! note "Pelican directory listings are cached"
312
311
 
@@ -353,7 +352,6 @@ other keys:
353
352
  | `principal` | [string](https://toml.io/en/v1.0.0#string) | [optional] The Kerberos principal to authenticate as |
354
353
  | `audience` | [string](https://toml.io/en/v1.0.0#string) | The intended audience for the SciToken |
355
354
  | `scopes` | [array](https://toml.io/en/v1.0.0#array) of [string](https://toml.io/en/v1.0.0#string) | [optional] List of scopes to request for the SciToken |
356
- | `interval` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] How often (in seconds) to refresh the credential (default: 3600 = 1 hour) |
357
355
 
358
356
  !!! example "SciToken credential configuration"
359
357
 
@@ -10,7 +10,7 @@ services:
10
10
  # Rebuild image with Kerberos keytab embedded securely
11
11
  context: .
12
12
  dockerfile_inline: |
13
- FROM containers.ligo.org/computing/gwdatafind/server:latest
13
+ FROM ${GWDATAFIND_SERVER_IMAGE-containers.ligo.org/computing/gwdatafind/server:latest}
14
14
  COPY --chown=gwdatafind:gwdatafind --chmod=400 pelican.keytab /pelican.keytab
15
15
  restart: unless-stopped
16
16
  ports:
@@ -26,9 +26,12 @@ services:
26
26
  # Volumes
27
27
  volumes:
28
28
  - type: bind
29
- source: ./gwdatafind-server.toml
29
+ source: ${GWDATAFIND_SERVER_CONFIG-./gwdatafind-server.toml}
30
30
  target: /etc/gwdatafind/gwdatafind-server.toml
31
31
  read_only: true
32
+ - type: volume
33
+ source: gwdatafind-data
34
+ target: /var/lib/gwdatafind
32
35
  read_only: true # Read-only root filesystem
33
36
  tmpfs:
34
37
  - /tmp:noexec,nosuid,size=100m
@@ -64,3 +67,7 @@ services:
64
67
  options:
65
68
  max-size: "10m"
66
69
  max-file: "3"
70
+
71
+ volumes:
72
+ gwdatafind-data:
73
+ driver: local
@@ -38,6 +38,7 @@ type = "pelican"
38
38
  federation_url = "osg-htc.org"
39
39
  path = "/gwdata"
40
40
  interval = 10800
41
+ jitter = 600
41
42
  cachefile = "/var/lib/gwdatafind/pelican-gwosc.db"
42
43
 
43
44
  # Private LIGO data in Pelican
@@ -47,6 +48,7 @@ type = "pelican"
47
48
  federation_url = "osg-htc.org"
48
49
  path = "/igwn/ligo"
49
50
  interval = 600
51
+ jitter = 60
50
52
  credential = "pelican_token"
51
53
  cachefile = "/var/lib/gwdatafind/pelican-ligo.db"
52
54
 
@@ -57,6 +59,7 @@ type = "pelican"
57
59
  federation_url = "osg-htc.org"
58
60
  path = "/igwn/virgo"
59
61
  interval = 600
62
+ jitter = 60
60
63
  credential = "pelican_token"
61
64
  cachefile = "/var/lib/gwdatafind/pelican-virgo.db"
62
65
 
@@ -67,6 +70,7 @@ type = "pelican"
67
70
  federation_url = "osg-htc.org"
68
71
  path = "/igwn/kagra"
69
72
  interval = 600
73
+ jitter = 60
70
74
  credential = "pelican_token"
71
75
  cachefile = "/var/lib/gwdatafind/pelican-kagra.db"
72
76
 
@@ -30,6 +30,7 @@ services:
30
30
  gwdatafind:
31
31
  container_name: gwdatafind-server
32
32
  image: containers.ligo.org/computing/gwdatafind/server:latest
33
+ image: ${GWDATAFIND_SERVER_IMAGE-containers.ligo.org/computing/gwdatafind/server:latest}
33
34
  ports:
34
35
  - "8080:8080"
35
36
  restart: unless-stopped
@@ -45,9 +46,12 @@ services:
45
46
  # Volumes
46
47
  volumes:
47
48
  - type: bind
48
- source: gwdatafind-server.toml
49
+ source: ${GWDATAFIND_SERVER_CONFIG-./gwdatafind-server.toml}
49
50
  target: /etc/gwdatafind/gwdatafind-server.toml
50
51
  read_only: true
52
+ - type: volume
53
+ source: gwdatafind-data
54
+ target: /var/lib/gwdatafind
51
55
  tmpfs:
52
56
  - /tmp:noexec,nosuid,size=100m
53
57
  - /var/tmp:noexec,nosuid,size=10m
@@ -69,3 +73,8 @@ services:
69
73
  networks:
70
74
  gwdatafind_internal:
71
75
  driver: bridge
76
+
77
+ # Volumes
78
+ volumes:
79
+ gwdatafind-data:
80
+ driver: local
@@ -37,6 +37,7 @@ type = "pelican"
37
37
  federation_url = "osg-htc.org"
38
38
  path = "/gwdata"
39
39
  interval = 10800
40
+ jitter = 600
40
41
  cachefile = "/var/lib/gwdatafind/pelican-gwosc.db"
41
42
 
42
43
  # Local diskcache for file system scanning
@@ -1,13 +1,11 @@
1
1
  # Copyright (c) 2019-2025 Cardiff University
2
2
  # Licensed under GPLv3+ - see LICENSE
3
3
 
4
- """The GWDataFind Service provides a metadata-based
5
- data discover mechanism for data files.
4
+ """A metadata-based discovery mechanism for GW data files."""
6
5
 
7
- Users provide a few key details as part of an HTTP(S)
8
- query and receive URLs that indicate the locations
9
- of the relevant data.
10
- """
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING
11
9
 
12
10
  try:
13
11
  from ._version import version as __version__
@@ -17,9 +15,17 @@ except ModuleNotFoundError: # development mode
17
15
  from .app import GWDataFindApp
18
16
  from .config import get_config_path
19
17
 
18
+ if TYPE_CHECKING:
19
+ from pathlib import Path
20
+
20
21
 
21
- def create_app(config=None):
22
+ def create_app(
23
+ name: str = __name__,
24
+ config: Path | None = None,
25
+ app_class: type[GWDataFindApp] = GWDataFindApp,
26
+ **app_kwargs,
27
+ ):
22
28
  """Create an instance of the application."""
23
29
  if config is None:
24
30
  config = get_config_path()
25
- return GWDataFindApp(__name__, config)
31
+ return app_class(name, config, **app_kwargs)
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.1.0'
22
- __version_tuple__ = version_tuple = (2, 1, 0)
21
+ __version__ = version = '2.3.0'
22
+ __version_tuple__ = version_tuple = (2, 3, 0)
23
23
 
24
- __commit_id__ = commit_id = 'gca848aa98'
24
+ __commit_id__ = commit_id = 'gffb5a6c3c'