Skip to content

Commit 02fc11a

Browse files
fix(prune): add pg prune
1 parent 5a4227f commit 02fc11a

3 files changed

Lines changed: 61 additions & 10 deletions

File tree

infra/run-artifact-refresh.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ 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 -e HF_XET_HIGH_PERFORMANCE=1 -v "${artifact_host}:/artifact" --entrypoint osmsg worker \
14+
# Cap Xet download concurrency so its reconstruction buffers stay well within the container memory limit.
15+
docker compose run --rm -e HF_XET_CLIENT_AC_MAX_DOWNLOAD_CONCURRENCY=4 -v "${artifact_host}:/artifact" --entrypoint osmsg worker \
1516
maintain refresh --artifact-dir /artifact --repo "${repo}"
1617

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

osmsg/prune.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,45 @@
1111
DEFAULT_OVERLAP = dt.timedelta(days=2)
1212

1313

14-
def prune_pg(dsn: str, cutoff: dt.datetime) -> tuple[int, int]:
15-
"""Delete changesets and their changeset_stats older than cutoff; child rows first for the FK. The
16-
DSN is interpolated into ATTACH, so it must be trusted."""
14+
def _attach(dsn: str) -> duckdb.DuckDBPyConnection:
1715
conn = duckdb.connect()
1816
conn.execute("INSTALL postgres")
1917
conn.execute("LOAD postgres")
2018
conn.execute(f"ATTACH '{dsn.replace(chr(39), chr(39) * 2)}' AS pg (TYPE postgres)")
19+
return conn
20+
21+
22+
def _pg_execute(conn: duckdb.DuckDBPyConnection, sql: str) -> None:
23+
"""Run one statement natively on the attached Postgres, so a bulk DELETE is a single indexed
24+
statement server-side instead of DuckDB's per-row ctid batches."""
25+
conn.execute(f"CALL postgres_execute('pg', $osmsg_stmt${sql}$osmsg_stmt$)")
26+
27+
28+
def prune_pg(dsn: str, cutoff: dt.datetime) -> tuple[int, int]:
29+
"""Delete changesets and their changeset_stats older than cutoff; child rows first for the FK. The
30+
DSN is interpolated into ATTACH, so it must be trusted. Counting and deleting use separate
31+
connections because a read pins the connection read-only, which would block the native deletes."""
2132
iso = cutoff.astimezone(dt.UTC).isoformat()
22-
old_cs = f"SELECT changeset_id FROM pg.changesets WHERE created_at < TIMESTAMPTZ '{iso}'"
23-
stats_row = conn.execute(f"SELECT count(*) FROM pg.changeset_stats WHERE changeset_id IN ({old_cs})").fetchone()
24-
cs_row = conn.execute(f"SELECT count(*) FROM pg.changesets WHERE created_at < TIMESTAMPTZ '{iso}'").fetchone()
33+
older = f"created_at < TIMESTAMPTZ '{iso}'"
34+
35+
reader = _attach(dsn)
36+
stats_row = reader.execute(
37+
"SELECT count(*) FROM pg.changeset_stats s "
38+
f"WHERE EXISTS (SELECT 1 FROM pg.changesets c WHERE c.changeset_id = s.changeset_id AND c.{older})"
39+
).fetchone()
40+
cs_row = reader.execute(f"SELECT count(*) FROM pg.changesets WHERE {older}").fetchone()
41+
reader.close()
2542
stats_n = stats_row[0] if stats_row else 0
2643
cs_n = cs_row[0] if cs_row else 0
44+
2745
if cs_n:
28-
conn.execute(f"DELETE FROM pg.changeset_stats WHERE changeset_id IN ({old_cs})")
29-
conn.execute(f"DELETE FROM pg.changesets WHERE created_at < TIMESTAMPTZ '{iso}'")
30-
conn.close()
46+
writer = _attach(dsn)
47+
_pg_execute(
48+
writer,
49+
f"DELETE FROM changeset_stats s USING changesets c WHERE s.changeset_id = c.changeset_id AND c.{older}",
50+
)
51+
_pg_execute(writer, f"DELETE FROM changesets WHERE {older}")
52+
writer.close()
3153
return stats_n, cs_n
3254

3355

tests/test_prune.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,31 @@ def test_prune_covered_raises_without_manifest(monkeypatch):
3232
monkeypatch.setattr(prune, "fetch_manifest", lambda url: None)
3333
with pytest.raises(OsmsgError):
3434
prune.prune_covered("dsn", "url")
35+
36+
37+
class _FakeConn:
38+
def __init__(self, calls):
39+
self._calls = calls
40+
41+
def execute(self, sql):
42+
self._calls.append(sql)
43+
return self
44+
45+
def fetchone(self):
46+
return (5,)
47+
48+
def close(self):
49+
pass
50+
51+
52+
def test_prune_pg_deletes_natively_via_postgres_execute(monkeypatch):
53+
calls = []
54+
monkeypatch.setattr(prune.duckdb, "connect", lambda *a, **k: _FakeConn(calls))
55+
stats_n, cs_n = prune.prune_pg("dsn", dt.datetime(2026, 7, 30, tzinfo=UTC))
56+
57+
deletes = [c for c in calls if "postgres_execute" in c and "DELETE" in c]
58+
assert len(deletes) == 2 # child (changeset_stats) then parent (changesets)
59+
assert "changeset_stats" in deletes[0]
60+
assert "DELETE FROM changesets WHERE" in deletes[1] and "changeset_stats" not in deletes[1]
61+
assert all("2026-07-30" in d for d in deletes)
62+
assert (stats_n, cs_n) == (5, 5)

0 commit comments

Comments
 (0)