Skip to content

Commit 5a4227f

Browse files
fix(hf): fix download request to hf download
1 parent 4848702 commit 5a4227f

5 files changed

Lines changed: 90 additions & 94 deletions

File tree

infra/run-artifact-refresh.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ dsn="${OSMSG_PSQL_DSN:-postgresql://osmsg:osmsg@db:5432/osmsg}"
1111
repo="${OSMSG_HISTORY_REPO:-kshitijrajsharma/osmsg-history}"
1212

1313
echo "[artifact-refresh] advancing ${artifact_host} from ${repo}"
14-
docker compose run --rm -v "${artifact_host}:/artifact" --entrypoint osmsg worker \
14+
docker compose run --rm -e HF_XET_HIGH_PERFORMANCE=1 -v "${artifact_host}:/artifact" --entrypoint osmsg worker \
1515
maintain refresh --artifact-dir /artifact --repo "${repo}"
1616

1717
echo "[artifact-refresh] reloading the API onto the advanced frontier"

osmsg/maintain/refresh.py

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,27 @@
99
import shutil
1010

1111
import duckdb
12-
import requests
12+
import huggingface_hub
1313

1414
from ..exceptions import OsmsgError
1515
from ..history import fetch_manifest
16-
from ..ui import info, warn
16+
from ..ui import info
1717

1818
UTC = dt.UTC
1919
VERIFY_TOLERANCE = dt.timedelta(days=2)
20-
_HF_DATASETS = "https://huggingface.co/datasets"
21-
_DOWNLOAD_CHUNK = 1 << 20
22-
_DOWNLOAD_RETRIES = 5
23-
_INTERRUPTED = (requests.ConnectionError, requests.Timeout, requests.exceptions.ChunkedEncodingError)
2420

2521
_ROLLUP_FILES = {
2622
"hashtag_changeset.parquet": "rollup/hashtag_changeset/data.parquet",
2723
"users.parquet": "rollup/users/data.parquet",
2824
}
2925

3026

31-
def _download_file(repo: str, remote: str, dest: pathlib.Path) -> pathlib.Path:
32-
"""Stream a published file from the dataset's resolve/main URL to dest, preserving its exact bytes
33-
(and so its row-group layout). Resumes with a Range request when the connection drops mid-stream, so
34-
the multi-GB rollup survives a flaky link; raises once the retry budget is spent, before the swap."""
35-
url = f"{_HF_DATASETS}/{repo}/resolve/main/{remote}"
36-
stalls = 0
37-
while stalls < _DOWNLOAD_RETRIES:
38-
have = dest.stat().st_size if dest.exists() else 0
39-
headers = {"Range": f"bytes={have}-"} if have else {}
40-
try:
41-
with requests.get(url, headers=headers, stream=True, timeout=60) as response:
42-
response.raise_for_status()
43-
resuming = have > 0 and response.status_code == 206
44-
with open(dest, "ab" if resuming else "wb") as handle:
45-
for chunk in response.iter_content(_DOWNLOAD_CHUNK):
46-
handle.write(chunk)
47-
return dest
48-
except _INTERRUPTED as exc:
49-
now = dest.stat().st_size if dest.exists() else 0
50-
stalls = 0 if now > have else stalls + 1
51-
if stalls >= _DOWNLOAD_RETRIES:
52-
raise OsmsgError(f"{remote}: download stalled after {stalls} attempts with no progress: {exc}") from exc
53-
warn(f"{remote}: interrupted ({type(exc).__name__}); resuming from {now:,} bytes")
54-
raise OsmsgError(f"{remote}: download failed")
27+
def _download(repo: str, remote: str, into_dir: pathlib.Path) -> pathlib.Path:
28+
"""Download a published dataset file into into_dir. huggingface_hub handles resume, retry, and the
29+
parallel chunked transfer that a single stream cannot sustain over a long-haul link."""
30+
return pathlib.Path(
31+
huggingface_hub.hf_hub_download(repo_id=repo, filename=remote, repo_type="dataset", local_dir=str(into_dir))
32+
)
5533

5634

5735
def _rollup_bounds(parquet: pathlib.Path) -> tuple[int, dt.datetime | None]:
@@ -99,10 +77,8 @@ def refresh_artifact(repo: str, artifact_dir: pathlib.Path) -> bool:
9977
shutil.rmtree(scratch)
10078
scratch.mkdir()
10179

102-
downloaded = {
103-
name: _download_file(repo, remote_path, scratch / name) for name, remote_path in _ROLLUP_FILES.items()
104-
}
105-
manifest = _download_file(repo, "manifest.json", scratch / "manifest.json")
80+
downloaded = {name: _download(repo, remote_path, scratch) for name, remote_path in _ROLLUP_FILES.items()}
81+
manifest = _download(repo, "manifest.json", scratch)
10682
_verify(downloaded["hashtag_changeset.parquet"], remote.frontier, artifact_dir / "hashtag_changeset.parquet")
10783
for name, path in downloaded.items():
10884
os.replace(path, artifact_dir / name)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ license-files = ["LICENSE"]
1111
requires-python = ">=3.11"
1212
dependencies = [
1313
"duckdb>=1.5.2",
14+
"huggingface-hub>=1.0",
1415
"osmium>=4.3.1",
1516
"platformdirs>=4.5.1",
1617
"pyarrow>=24.0.0",

tests/test_maintain_refresh.py

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import duckdb
88
import pytest
9-
import requests
109

1110
from osmsg.exceptions import OsmsgError
1211
from osmsg.history import Manifest
@@ -15,55 +14,6 @@
1514
UTC = dt.UTC
1615

1716

18-
class _FakeResponse:
19-
def __init__(self, status_code, chunks, break_after=None):
20-
self.status_code = status_code
21-
self._chunks = chunks
22-
self._break_after = break_after
23-
24-
def __enter__(self):
25-
return self
26-
27-
def __exit__(self, *args):
28-
return False
29-
30-
def raise_for_status(self):
31-
pass
32-
33-
def iter_content(self, chunk_size):
34-
for index, chunk in enumerate(self._chunks):
35-
if self._break_after is not None and index == self._break_after:
36-
raise requests.exceptions.ChunkedEncodingError("connection broken")
37-
yield chunk
38-
39-
40-
def test_download_resumes_after_interruption(tmp_path, monkeypatch):
41-
dest = tmp_path / "rollup.parquet"
42-
calls = []
43-
44-
def fake_get(url, headers=None, stream=True, timeout=60):
45-
calls.append(headers or {})
46-
if not headers:
47-
return _FakeResponse(200, [b"aa", b"bb", b"cc"], break_after=2)
48-
return _FakeResponse(206, [b"cc", b"dd"])
49-
50-
monkeypatch.setattr(refresh.requests, "get", fake_get)
51-
refresh._download_file("test/repo", "rollup/x/data.parquet", dest)
52-
assert dest.read_bytes() == b"aabbccdd"
53-
assert calls[1]["Range"] == "bytes=4-"
54-
55-
56-
def test_download_raises_after_retry_budget(tmp_path, monkeypatch):
57-
dest = tmp_path / "rollup.parquet"
58-
59-
def always_break(url, headers=None, stream=True, timeout=60):
60-
return _FakeResponse(200, [b"aa", b"bb"], break_after=0)
61-
62-
monkeypatch.setattr(refresh.requests, "get", always_break)
63-
with pytest.raises(OsmsgError, match="stalled after"):
64-
refresh._download_file("test/repo", "rollup/x/data.parquet", dest)
65-
66-
6717
def _month_start(year, month):
6818
return dt.datetime(year, month, 1, tzinfo=UTC)
6919

@@ -77,9 +27,11 @@ def _write_rollup(path, latest, rows):
7727

7828

7929
def _fake_download(latest, manifest_dict):
80-
"""Stand in for the HTTP download: materialize each requested file at its scratch destination."""
30+
"""Stand in for huggingface_hub.hf_hub_download: materialize each requested file under into_dir."""
8131

82-
def _download(repo, remote, dest):
32+
def _download(repo, remote, into_dir):
33+
dest = into_dir / remote
34+
dest.parent.mkdir(parents=True, exist_ok=True)
8335
if remote.endswith("manifest.json"):
8436
dest.write_text(json.dumps(manifest_dict))
8537
elif "hashtag_changeset" in remote:
@@ -108,7 +60,7 @@ def test_noop_when_already_current(tmp_path, monkeypatch):
10860
def _boom(*args, **kwargs):
10961
raise AssertionError("must not download when already current")
11062

111-
monkeypatch.setattr(refresh, "_download_file", _boom)
63+
monkeypatch.setattr(refresh, "_download", _boom)
11264
assert refresh.refresh_artifact("test/repo", artifact) is False
11365

11466

@@ -122,7 +74,7 @@ def test_happy_path_advances_and_swaps(tmp_path, monkeypatch):
12274
new_manifest = {"schema_version": 1, "min_month": "2005-04", "max_month": "2026-07"}
12375
_patch_manifests(monkeypatch, artifact, remote_frontier=_month_start(2026, 8), local_frontier=_month_start(2026, 7))
12476
monkeypatch.setattr(
125-
refresh, "_download_file", _fake_download(dt.datetime(2026, 7, 31, 23, 59, tzinfo=UTC), new_manifest)
77+
refresh, "_download", _fake_download(dt.datetime(2026, 7, 31, 23, 59, tzinfo=UTC), new_manifest)
12678
)
12779

12880
assert refresh.refresh_artifact("test/repo", artifact) is True
@@ -140,7 +92,7 @@ def test_short_rollup_is_rejected_and_live_files_untouched(tmp_path, monkeypatch
14092
_write_rollup(artifact / "hashtag_changeset.parquet", _month_start(2026, 6), rows=5)
14193
_patch_manifests(monkeypatch, artifact, remote_frontier=_month_start(2026, 8), local_frontier=_month_start(2026, 7))
14294
monkeypatch.setattr(
143-
refresh, "_download_file", _fake_download(dt.datetime(2026, 6, 15, tzinfo=UTC), {"max_month": "2026-07"})
95+
refresh, "_download", _fake_download(dt.datetime(2026, 6, 15, tzinfo=UTC), {"max_month": "2026-07"})
14496
)
14597

14698
with pytest.raises(OsmsgError, match="short of frontier"):
@@ -154,7 +106,7 @@ def test_shrunk_rollup_is_rejected(tmp_path, monkeypatch):
154106
_write_rollup(artifact / "hashtag_changeset.parquet", _month_start(2026, 6), rows=50)
155107
_patch_manifests(monkeypatch, artifact, remote_frontier=_month_start(2026, 8), local_frontier=_month_start(2026, 7))
156108
monkeypatch.setattr(
157-
refresh, "_download_file", _fake_download(dt.datetime(2026, 7, 31, tzinfo=UTC), {"max_month": "2026-07"})
109+
refresh, "_download", _fake_download(dt.datetime(2026, 7, 31, tzinfo=UTC), {"max_month": "2026-07"})
158110
)
159111

160112
with pytest.raises(OsmsgError, match="fewer rows"):

0 commit comments

Comments
 (0)