Approach: parse CSV lines, keep the most recent successful backup timestamp per database, compare to an RPO threshold (timedelta). We'll stream lines (memory-efficient), parse timestamps robustly, and report/return flagged DBs.python
from datetime import datetime, timedelta
import csv
import sys
def parse_timestamp(s):
# try ISO formats; extendable
for fmt in ("%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"):
try:
return datetime.strptime(s, fmt)
except ValueError:
pass
raise ValueError(f"unrecognized timestamp: {s}")
def last_successful_backups(csv_stream):
reader = csv.reader(csv_stream)
latest = {}
for row_no, row in enumerate(reader, 1):
if not row:
continue
try:
db, ts_s, status, size = row
except ValueError:
# malformed row: skip/log
print(f"warn: row {row_no} malformed: {row}", file=sys.stderr)
continue
if status.strip().lower() != "success":
continue
try:
ts = parse_timestamp(ts_s.strip())
except ValueError as e:
print(f"warn: row {row_no} bad timestamp: {e}", file=sys.stderr)
continue
if db not in latest or ts > latest[db]:
latest[db] = ts
return latest
def flag_violations(latest_map, rpo_hours):
now = datetime.utcnow()
rpo = timedelta(hours=rpo_hours)
flagged = {}
for db, ts in latest_map.items():
age = now - ts
if age > rpo:
flagged[db] = {"last_success": ts.isoformat(), "age_h": age.total_seconds()/3600}
return flagged
if __name__ == "__main__":
import argparse
p = argparse.ArgumentParser()
p.add_argument("--rpo-hours", type=float, required=True)
p.add_argument("file", nargs="?", default="-")
args = p.parse_args()
stream = sys.stdin if args.file == "-" else open(args.file)
latest = last_successful_backups(stream)
violations = flag_violations(latest, args.rpo_hours)
for db, info in violations.items():
print(f"{db} violates RPO: last_success={info['last_success']} age_h={info['age_h']:.2f}")
Key points:- Time/space: O(n) time, O(m) space (m = distinct DBs). Streams input to handle large logs.- Error handling: skip malformed rows, warn on stderr, robust timestamp parsing, treat timezone by assuming UTC or mandate ISO+TZ.- Assumptions: CSV columns exactly db,timestamp,status,size_mb; timestamps parseable; "success" indicates good backups; system clock in UTC.- Alternatives: use pandas for richer parsing, or write to monitoring system (Prometheus/Grafana) and alert via alertmanager.