mahendra0120's picture
Upload folder using huggingface_hub (part 15)
6dde7dd verified
Raw History Blame Contribute Delete
6.56 kB
import asyncio
import os
from typing import Any, Optional
import asyncpg
DATABASE_URL = os.getenv(
"DATABASE_URL",
"postgresql://asp_user:asp_password@localhost:5432/asp",
)
PIPE_SCHEMA = """
CREATE TABLE IF NOT EXISTS pipeline_steps (
case_id TEXT NOT NULL,
step TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('partial', 'complete')),
content TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (case_id, step)
);
"""
class CheckpointDB:
def __init__(
self,
url: str = DATABASE_URL,
min_size: int = 1,
max_size: int = 10,
timeout: float = 5,
command_timeout: float = 30,
):
self.url = url
self.min_size = min_size
self.max_size = max_size
self.timeout = timeout
self.command_timeout = command_timeout
self._pool: Optional[asyncpg.Pool] = None
self._loop: Optional[asyncio.AbstractEventLoop] = None
self._lock: Optional[asyncio.Lock] = None
self._lock_loop: Optional[asyncio.AbstractEventLoop] = None
def lock_for(self, loop: asyncio.AbstractEventLoop) -> asyncio.Lock:
if self._lock is None or self._lock_loop is not loop:
self._lock, self._lock_loop = asyncio.Lock(), loop
return self._lock
async def pool(self) -> asyncpg.Pool:
loop = asyncio.get_running_loop()
if self._pool is not None and self._loop is loop:
return self._pool
async with self.lock_for(loop):
if self._pool is None or self._loop is not loop:
self.discard_pool()
pool = await asyncpg.create_pool(
self.url,
min_size=self.min_size,
max_size=self.max_size,
timeout=self.timeout,
command_timeout=self.command_timeout,
)
async with pool.acquire() as conn:
await conn.execute(PIPE_SCHEMA)
self._pool, self._loop = pool, loop
return self._pool
def discard_pool(self) -> None:
stale, self._pool, self._loop = self._pool, None, None
if stale is not None:
try:
stale.terminate()
except Exception:
pass
async def close(self) -> None:
pool, self._pool, self._loop = self._pool, None, None
if pool is not None:
await pool.close()
async def save_step(self, case_id: str, step: str, content: str, status: str) -> bool:
pool = await self.pool()
async with pool.acquire() as conn:
row = await conn.fetchval(
"""
INSERT INTO pipeline_steps (case_id, step, status, content, updated_at)
VALUES ($1, $2, $3, $4, now())
ON CONFLICT (case_id, step) DO UPDATE
SET status = EXCLUDED.status,
content = EXCLUDED.content,
updated_at = now()
WHERE NOT (pipeline_steps.status = 'complete' AND EXCLUDED.status = 'partial')
RETURNING 1
""",
case_id, step, status, content
)
return row is not None
async def load_steps(self, case_id: str) -> dict[str, dict[str, Any]]:
pool = await self.pool()
async with pool.acquire() as conn:
rows = await conn.fetch(
"SELECT step, status, content, updated_at FROM pipeline_steps WHERE case_id = $1",
case_id
)
return {
r["step"]: {
"status": r["status"],
"content": r["content"],
"updated_at": r["updated_at"].isoformat()
}
for r in rows
}
async def delete_steps(self, case_id: str, steps: list[str]) -> None:
pool = await self.pool()
async with pool.acquire() as conn:
await conn.execute(
"DELETE FROM pipeline_steps WHERE case_id = $1 AND step = ANY($2::text[])",
case_id, steps
)
async def list_cases(self, limit: int = 25) -> list[dict[str, Any]]:
pool = await self.pool()
async with pool.acquire() as conn:
rows = await conn.fetch(
"""
SELECT case_id,
max(updated_at) AS updated_at,
string_agg(step || ':' || status, ',' ORDER BY step) AS steps,
max(content) FILTER (WHERE step = 'meta') AS meta
FROM pipeline_steps
GROUP BY case_id
ORDER BY max(updated_at) DESC
LIMIT $1
""",
limit
)
return [
{
"case_id": r["case_id"],
"updated_at": r["updated_at"].isoformat(),
"steps": r["steps"],
"meta": r["meta"]
}
for r in rows
]
async def forget_case(self, case_id: str) -> int:
pool = await self.pool()
async with pool.acquire() as conn:
n = await conn.fetchval(
"WITH d AS (DELETE FROM pipeline_steps WHERE case_id = $1 RETURNING 1) SELECT count(*) FROM d",
case_id
)
return int(n or 0)
async def delete_cases_older_than(self, days: int) -> int:
pool = await self.pool()
async with pool.acquire() as conn:
ids = [
r["case_id"]
for r in await conn.fetch(
"""
DELETE FROM pipeline_steps WHERE case_id IN (
SELECT case_id FROM pipeline_steps
GROUP BY case_id
HAVING max(updated_at) < now() - make_interval(days => $1)
) RETURNING case_id
""",
int(days)
)
]
return len(set(ids))
async def delete_all_cases(self) -> int:
pool = await self.pool()
async with pool.acquire() as conn:
rows = await conn.fetch("DELETE FROM pipeline_steps RETURNING case_id")
return len({r["case_id"] for r in rows})
checkpoint_db = CheckpointDB()