Add reference-counted garbage collection

This commit is contained in:
2026-05-17 00:26:19 +02:00
parent 1dae855700
commit a60d24f814
7 changed files with 631 additions and 3 deletions

View File

@@ -254,6 +254,75 @@ def test_cli_retention_sweep_marks_expired_package(
assert payload == {"marked_package_ids": [package_id], "marked_count": 1}
def test_cli_retention_gc_collects_eligible_package(
runner: CliRunner,
env_db: Path,
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
sync_engine = create_engine(f"sqlite:///{env_db}", future=True)
metadata.create_all(sync_engine)
with sync_engine.begin() as conn:
conn.execute(insert(retention_classes), [dict(s) for s in RETENTION_CLASS_SEEDS])
sync_engine.dispose()
retention_config = tmp_path / "retention.toml"
retention_config.write_text(
'[retention_classes.transient]\ndefault_duration_seconds = 0\n',
encoding="utf-8",
)
monkeypatch.setenv("ARTIFACTSTORE_RETENTION_CONFIG_PATH", str(retention_config))
async def create_expired_file() -> None:
from collections.abc import AsyncIterator
from datetime import UTC, datetime, timedelta
from artifactstore.app import build_registry
from artifactstore.config import get_settings
async def stream() -> AsyncIterator[bytes]:
yield b"collect-me"
registry = build_registry(get_settings())
try:
package_id = await registry.create_package(
name="collect",
producer="tests",
subject="cli-gc",
retention_class="transient",
actor="ops",
)
await registry.ingest_file(
package_id,
relative_path="collect.bin",
media_type="application/octet-stream",
stream=stream(),
actor="ops",
)
await registry.finalize_package(package_id, actor="ops")
await registry.sweep_deletion_eligibility(
now=datetime.now(UTC) + timedelta(seconds=1)
)
finally:
await registry.dispose()
asyncio.run(create_expired_file())
result = runner.invoke(cli_app, ["retention", "gc"])
assert result.exit_code == 0, result.output
payload = json.loads(result.output)
assert payload["released_location_count"] == 1
assert payload["delete_attempted_object_count"] == 1
assert payload["deleted_object_count"] == 1
assert payload["results"][0]["object_deleted"] is True
sync_engine = create_engine(f"sqlite:///{env_db}", future=True)
with sync_engine.connect() as conn:
status = conn.execute(select(storage_locations.c.status)).scalar_one()
sync_engine.dispose()
assert status == "deleted"
def test_cli_storage_verify_marks_local_location_verified(
runner: CliRunner,
env_db: Path,