A lakehouse for behavior.
A verdict at checkout.
- 01
Bronze · raw events
pod_risk.bronze_ordersAppend-only ingestion from transaction databases, user accounts and external return APIs. Schema-on-read, zero transformation. Auto Loader writes parquet into Delta. - 02
Silver · normalized
pod_risk.silver_user_ordersDeduplicated, joined to user master, normalized status enums (PLACED / REJECTED / RETURNED / DELIVERED). Indexed on user_id and ts via Liquid Clustering. - 03
Gold · per-user features
pod_risk.gold_user_pod_behavior_30dMaterialised feature store keyed by user_id. Trailing 30-day metrics: pod_percentage, pod_failure_rate, pod_rejected_30d, account_age_days, avg_basket_value_inr. Refreshed every 5 minutes by a Databricks workflow. - 04
Decision engine
POST /api/decisions/evaluateServer function reads the user's gold row in a single SQL call, fires rule layer + ML classifier, returns allowed/blocked methods with a reason trail. Synchronous, p99 < 2s.
Five minute lag, end to end.
A scheduled Databricks Workflow recomputes the gold feature table every five minutes via Delta Live Tables. The decision engine reads only the gold row, so the SQL warehouse handles thousands of concurrent checkouts with a single indexed lookup.
CREATE OR REPLACE TABLE pod_risk.gold_user_pod_behavior_30d AS SELECT user_id, ANY_VALUE(display_name) AS display_name, ANY_VALUE(city) AS city, ANY_VALUE(months_between(current_date, signup_date)) AS tenure_months, COUNT(*) AS total_orders_30d, SUM(CASE WHEN payment_type = 'POD' THEN 1 END) AS pod_orders_30d, SUM(CASE WHEN payment_type='POD' AND status='REJECTED' THEN 1 END) AS pod_rejected_30d, SUM(CASE WHEN payment_type='POD' AND status='RETURNED' THEN 1 END) AS pod_returned_30d, SUM(CASE WHEN payment_type<>'POD' AND status='DELIVERED' THEN 1 END) AS prepaid_success_30d FROM pod_risk.silver_user_orders WHERE order_ts >= current_date - INTERVAL 30 DAYS GROUP BY user_id;