Real-Time Fraud Detection¶
Fraud detection requires analyzing complex relationships (graph) and patterns in real-time, often while ingesting high volumes of transaction data. Uni's single-writer architecture with L0 memory buffering makes it ideal for high-velocity ingest combined with instant "read-your-writes" queries.
Why Uni for Fraud Detection?¶
| Challenge | Traditional Approach | Uni Approach |
|---|---|---|
| Ingest Rate | Graph DBs struggle with high write loads; Relational DBs can't do hops. | L0 Buffer: Writes hit memory first (WAL-backed), amortized flush to disk. |
| Freshness | "Eventual consistency" or ETL lag (minutes/hours). | Snapshot Isolation: Readers see writes immediately (if configured). |
| Deep Links | Detecting "Payment Rings" requires 3+ hops. SQL joins choke. | CSR Cache: O(1) adjacency lookups make multi-hop checks fast. |
Scenario: Payment Ring Detection¶
We want to detect a circular payment pattern: User A -> User B -> User C -> User A happening within a short time window.
1. Schema Definition¶
We model Users, Devices, and IPs as nodes to detect shared infrastructure usage. Transactions are edges with timestamps.
schema.json
{
"labels": {
"User": { "id": 1 },
"Device": { "id": 2 },
"IP": { "id": 3 }
},
"edge_types": {
"SENT_MONEY": { "id": 1, "src_labels": ["User"], "dst_labels": ["User"] },
"USED_DEVICE": { "id": 2, "src_labels": ["User"], "dst_labels": ["Device"] },
"USED_IP": { "id": 3, "src_labels": ["User"], "dst_labels": ["IP"] }
},
"properties": {
"SENT_MONEY": {
"amount": { "type": "Float64", "nullable": false },
"ts": { "type": "Int64", "nullable": false }
},
"User": {
"risk_score": { "type": "Float32", "nullable": true }
}
},
"indexes": []
}
2. Configuration¶
For high write throughput, we tune the WAL and L0 Buffer.
uni.toml
[storage]
# Allow large in-memory buffer before flushing to Lance
max_l0_size_mb = 512
max_mutations_before_flush = 100000
[storage.wal]
# "Periodic" is faster than "Sync", still safe enough for most fraud cases
sync_mode = "periodic"
sync_interval_ms = 50
3. Streaming Ingestion¶
Use the HTTP API or Rust bindings to ingest transactions as they happen.
// Rust API Example
let writer = db.writer();
let mut tx_props = std::collections::HashMap::new();
tx_props.insert("amount".to_string(), json!(5000.00));
tx_props.insert("ts".to_string(), json!(current_time));
// Allocate edge ID and insert
// This hits the L0 memory buffer - <1ms latency
// insert_edge(src, dst, type_id, eid, props)
writer.insert_edge(user_a, user_b, sent_money_type, eid, tx_props).await?;
4. Real-time Detection Query¶
Run this query synchronously when a transaction is attempted. It checks for a cycle of length 3-4 involving the sender.
// Check for cycles starting from User A
MATCH (a:User)-[t1:SENT_MONEY]->(b:User)-[t2:SENT_MONEY]->(c:User)-[t3:SENT_MONEY]->(a)
// Filter recent transactions only (e.g., last 1 hour)
WHERE t1.ts > $threshold AND t2.ts > $threshold AND t3.ts > $threshold
// Return the cycle details
RETURN a.id, b.id, c.id, t1.amount, t2.amount, t3.amount
5. Identity Resolution Query¶
Check if the sender shares an IP or Device with known fraudsters.
MATCH (sender:User)-[:USED_DEVICE]->(shared_resource)<-[:USED_DEVICE]-(other:User)
WHERE other.risk_score > 0.8
RETURN count(other) as suspicious_links
Key Advantages¶
- Ingest Speed: Uni's L0 buffer acts like a Write-Optimized Store (WOS), handling spikes in transaction volume without blocking readers.
- Latency: Checking a 3-hop cycle takes milliseconds due to the in-memory CSR cache.
- Data Locality: The graph structure and properties are co-located; no need to query a Redis cache for "risk scores" separately.