Buckets:

glennmatlin's picture
download
raw
4.71 kB
"""Delete an R2 prefix (or sub-prefix) under the soc127-dedup bucket.
Safety:
- Defaults to --dry-run: lists what WOULD be deleted, no mutation.
- --execute additionally requires the env var R2_DELETE_OK=1 in the same
invocation. Without both, nothing is deleted.
- Re-lists the prefix from R2 live (not the stale crawl) so it deletes exactly
current reality, and re-lists afterward to confirm zero objects remain.
Deletion uses batched delete_objects (up to 1000 keys/request); Class A cost is
negligible (~$0.00027 for 60k objects).
Run via:
bash scripts/bootstrap/with_r2_credentials.sh \\
uv run python scripts/r2_sunset/delete_prefix.py --prefix soc91-stats/ --dry-run
R2_DELETE_OK=1 bash scripts/bootstrap/with_r2_credentials.sh \\
uv run python scripts/r2_sunset/delete_prefix.py --prefix soc91-stats/ --execute
"""
from __future__ import annotations
import argparse
import logging
import os
import sys
import boto3
R2_BUCKET = "soc127-dedup"
R2_ENDPOINT_URL = "https://0934ab8e84ac8f4e81decaf3eb121337.r2.cloudflarestorage.com"
logger = logging.getLogger("delete_prefix")
def list_prefix(s3, prefix: str) -> list[tuple[str, int]]:
out: list[tuple[str, int]] = []
paginator = s3.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket=R2_BUCKET, Prefix=prefix):
for obj in page.get("Contents", []):
out.append((obj["Key"], obj["Size"]))
return out
def fmt_gb(b: int) -> str:
return f"{b / 1024**3:.2f} GB"
def delete_in_batches(s3, keys: list[str]) -> int:
deleted = 0
for i in range(0, len(keys), 1000):
batch = [{"Key": k} for k in keys[i : i + 1000]]
resp = s3.delete_objects(
Bucket=R2_BUCKET, Delete={"Objects": batch, "Quiet": True}
)
errors = resp.get("Errors", [])
if errors:
for e in errors[:5]:
logger.error("delete error: %s", e)
raise SystemExit(f"delete_objects reported {len(errors)} errors; aborting")
deleted += len(batch)
logger.info(" deleted %d/%d", deleted, len(keys))
return deleted
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument(
"--prefix", required=True, help="R2 key prefix to delete (e.g. soc91-stats/)"
)
mode = ap.add_mutually_exclusive_group()
mode.add_argument("--dry-run", action="store_true", default=True)
mode.add_argument("--execute", action="store_true")
ap.add_argument("--log-level", default="INFO")
args = ap.parse_args()
logging.basicConfig(
level=args.log_level, format="%(asctime)s %(levelname)s %(message)s"
)
if not os.environ.get("R2_ACCESS_KEY_ID") or not os.environ.get(
"R2_SECRET_ACCESS_KEY"
):
logger.error(
"R2 creds missing. Run via: bash scripts/bootstrap/with_r2_credentials.sh "
"uv run python %s ...",
sys.argv[0],
)
return 2
s3 = boto3.client(
"s3",
endpoint_url=R2_ENDPOINT_URL,
aws_access_key_id=os.environ["R2_ACCESS_KEY_ID"],
aws_secret_access_key=os.environ["R2_SECRET_ACCESS_KEY"],
)
logger.info("listing r2://%s/%s ...", R2_BUCKET, args.prefix)
objs = list_prefix(s3, args.prefix)
total_bytes = sum(s for _, s in objs)
logger.info("found %d objects, %s", len(objs), fmt_gb(total_bytes))
for k, _ in objs[:5]:
logger.info(" sample: %s", k)
if not objs:
logger.info("nothing to delete under %s", args.prefix)
return 0
execute = args.execute and os.environ.get("R2_DELETE_OK") == "1"
if not args.execute:
logger.info(
"DRY-RUN: would delete %d objects (%s). No mutation.",
len(objs),
fmt_gb(total_bytes),
)
return 0
if not execute:
logger.error(
"--execute given but R2_DELETE_OK!=1. Refusing to delete. "
"Set R2_DELETE_OK=1 in the same command to confirm."
)
return 3
logger.warning(
"EXECUTING deletion of %d objects (%s) under %s",
len(objs),
fmt_gb(total_bytes),
args.prefix,
)
deleted = delete_in_batches(s3, [k for k, _ in objs])
remaining = list_prefix(s3, args.prefix)
logger.info(
"deleted %d objects; %d remain under %s", deleted, len(remaining), args.prefix
)
if remaining:
logger.warning(
"non-empty after delete (%d left); inspect manually", len(remaining)
)
return 4
logger.info("OK: prefix %s now empty. Freed %s.", args.prefix, fmt_gb(total_bytes))
return 0
if __name__ == "__main__":
sys.exit(main())

Xet Storage Details

Size:
4.71 kB
·
Xet hash:
316afbaef69284f78833ece8fad52373ca016156fd3086c8664617ba572b9695

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.