Multi-Producer ยท Kafka ยท MongoDB or MySQL ยท Enrichment ยท 3 Fraud Rules ยท Live Alerts
| Time | What We Do | Format |
|---|---|---|
| 0 โ 10 | Module 2 recap โ full path from L6 โ L9 | Discussion |
| 10 โ 20 | Why fraud detection? Architecture overview | Lecture |
| 20 โ 30 | The dataset โ 2,003 synthetic transactions, 3 fraud patterns planted | Demo |
| 30 โ 45 | Step 1 โ Generate data + Run producer | Live build |
| 45 โ 60 | Step 2 โ choose MongoDB or MySQL storage consumer | Live build |
| 60 โ 80 | Step 3 โ consumer_fraud.py (3 rules + MongoDB enrichment) | Live build |
| 80 โ 90 | See fraud alerts fire, discuss extensions, module wrap-up | Discussion |
Every card swipe triggers a real-time decision. Banks have milliseconds โ not hours โ to approve or hold.
| Batch System (Old) | Streaming System (Now) |
|---|---|
| Check transactions overnight | Check every transaction as it arrives |
| Fraud flagged next morning | Fraud flagged before transaction clears |
| Customer already abroad, card maxed | Transaction held, customer called instantly |
| No geographic context | Knows where card was used 3 minutes ago |
The pattern we use today: Kafka brings the current signal (this transaction). MongoDB holds the historical context (last known location, average spend). The consumer combines both to decide.
| Network | Peak Transactions/sec |
|---|---|
| Visa | 65,000 |
| Mastercard | 50,000+ |
| NPCI / UPI | 10,000+ |
None of these run on batch. All use streaming architectures similar to what you're building today.
Choose by objective: use consumer_store.py with MongoDB for the fraud-enrichment lab, or consumer_store_mysql.py to create a live SQL table for the Grafana lectures. Both consume the same Kafka topic.
Run generate_transactions.py to create transactions.csv โ 2,003 rows, 50 fake users, 8 Indian cities.
| Field | Example | Notes |
|---|---|---|
transaction_id | TXN-00847 | Unique per row |
user_id | USR-042 | 50 synthetic users |
user_name | Bhavna Thakur | Realistic Indian names |
amount | 4500.00 | INR, varies by merchant type |
merchant | Swiggy | 30 merchant types |
city / lat / lon | Mumbai / 19.076 / 72.877 | Real GPS coordinates |
timestamp | 2025-08-01 10:00:00 | Spread over 30 days |
Safe repeated runs: The generator stores the last issued number in .transactions_sequence. If the first batch ends at TXN-02003, the next batch starts at TXN-02004. The CSV contains the latest batch, while IDs continue to avoid MySQL primary-key conflicts.
Open the course-wide Configurable Transaction Generator. Choose the total rows, total fraud as count or percentage, and the count or percentage for each fraud type. The browser validates the allocation and downloads a Python configuration file.
python generate_transactions.py
# โ
Generated 2003 transactions โ transactions.csv
# Legitimate: 1927 | Fraud planted: 76
# Next run continues after the displayed ID range
python transaction_producer.py
You'll see the live feed:
Notice: The producer marks fraud rows with ๐จ in its own output โ but the consumer doesn't know this yet. It's discovering fraud independently from Kafka + MongoDB. Leave this running and open Terminal 2.
The consumer can write the same Kafka stream to either destination:
| Option | Use It When | Consumer |
|---|---|---|
| MongoDB | You want to run the three fraud rules using user profiles and recent history | consumer_store.py |
| MySQL | You want SQL analysis and a live data source for the Lecture 14โ16 Grafana dashboards | consumer_store_mysql.py |
Two MongoDB writes occur on every message:
# 1. Append raw transaction to audit log
txns.insert_one({**txn, "ingested_at": datetime.utcnow()})
# 2. Upsert user_profiles โ freshest location + running totals
profiles.update_one(
{"user_id": txn["user_id"]},
{
"$set": {"last_city": txn["city"], "last_lat": lat, "last_lon": lon, ...},
"$inc": {"total_spend": amount, "txn_count": 1}
},
upsert=True
)
upsert=True means| Situation | Behaviour |
|---|---|
| User seen for the first time | Creates a new document in user_profiles |
| User seen before | Updates only the specified fields โ no duplicate |
Check Atlas: After 30 seconds, open MongoDB Atlas โ Browse Collections โ sda_course. You'll see user_profiles filling up with one document per user, each showing their latest city and cumulative spend.
Start the free MySQL container used in Lecture 14, install the connector, and run the alternative consumer:
docker run -d \
--name sda-mysql \
-e MYSQL_ROOT_PASSWORD=admin \
-p 3306:3306 \
mysql:8.4
pip install kafka-python mysql-connector-python
python consumer_store_mysql.py
The script creates sda_course.transactions, converts numeric and date fields, and writes one row after each Kafka message. Its upsert protects against duplicate rows if Kafka replays a transaction.
# Save first, then commit the Kafka offset
cursor.execute(UPSERT_SQL, transaction_values(txn))
db.commit()
consumer.commit()
Checkpoint: Run docker exec -it sda-mysql mysql -uroot -padmin, then query SELECT COUNT(*) FROM sda_course.transactions;. The count should rise while the producer is running.
Important: consumer_fraud.py still reads MongoDB profiles. Choose MongoDB for the complete fraud lab; choose MySQL for the optional SQL/Grafana path.
For the live Grafana extension, open another terminal and run a second consumer group. It reads the same topic, keeps only flagged transactions, classifies the classroom fraud pattern, and writes each alert to sda_course.fraud_alerts.
python consumer_alerts_mysql.py
# Check the live alert table
docker exec -it sda-mysql mysql -uroot -padmin \
-e "SELECT alert_type, severity, COUNT(*)
FROM sda_course.fraud_alerts
GROUP BY alert_type, severity;"
The mysql-store-group and mysql-alert-group are independent. Each receives every transaction: one builds the transaction table; the other builds the operational alert queue.
For every Kafka message, query MongoDB for the user's history โ then apply rules:
last_lat/lon (MongoDB) to current > 500 km AND time diff < 60 mintransactions collection by timestampAll three rules need MongoDB. None of them can fire on Kafka data alone โ they need context that only the storage consumer has been building.
IMPOSSIBLE_TRAVEL_KM = 100. What new alerts fire? Are any of them false positives?