Ride Sharing (Uber) ডিজাইন
- ●Driver-রা প্রতি কয়েক সেকেন্ডে location পাঠায়, যা geospatial index-এ রাখা হয় দ্রুত nearby খোঁজার জন্য।
- ●Geohash/QuadTree/S2 দিয়ে এলাকা ভাগ করে rider-এর কাছাকাছি driver খুঁজে matching করা হয়।
- ●Trip-এর পুরো lifecycle, live tracking (WebSocket), ETA ও surge pricing মিলে পুরো system গঠিত।
Uber খুলে "Confirm" চাপলে মাত্র কয়েক সেকেন্ডে কাছের একজন driver খুঁজে পাওয়া যায়, ম্যাপে তার গাড়ি নড়তে দেখা যায়, ETA দেখায়, আবার বৃষ্টির দিনে ভাড়া বেড়ে যায়। এই পুরোটা একটা চমৎকার real-time, geospatial system-এর খেলা। চলো interview-এর ৮ ধাপে এটা ভেঙে দেখি।
১. সমস্যা বোঝা (Requirements)
Functional Requirements:
- Driver প্রতি কয়েক সেকেন্ডে নিজের location update পাঠাবে।
- Rider একটা ride request করবে, system কাছের driver খুঁজবে।
- Driver ও rider কে match করা।
- Live tracking — rider ম্যাপে গাড়ি নড়তে দেখবে।
- ETA (কতক্ষণে পৌঁছাবে) দেখানো।
- Surge pricing — চাহিদা অনুযায়ী ভাড়া।
- পুরো trip lifecycle — requested → accepted → ongoing → completed।
Non-functional Requirements:
- Low latency — matching সেকেন্ডের মধ্যে।
- High availability — বিশেষত পিক টাইমে।
- Scalability — শহরভিত্তিক লক্ষ driver ও rider।
- Consistency — একই driver যেন দুজনকে assign না হয়।
ধরো ঢাকা শহরকে দাবার বোর্ডের মতো ছোট ছোট ঘরে ভাগ করলে। তুমি যে ঘরে দাঁড়িয়ে আছো, রিকশা খুঁজতে শুধু সেই ঘর আর পাশের কয়েকটা ঘরই দেখলে চলে — পুরো ঢাকা চষে বেড়াতে হয় না। Geospatial index ঠিক এই কাজটাই করে।
২. স্কেল আন্দাজ (Estimation)
ধরি:
Active drivers (একসাথে) = 30 লাখ = 3M
location update interval = প্রতি 4 সেকেন্ডে একবার
location write QPS = 3M / 4 ≈ 750,000 update/sec
Riders/দিন (ride request) = 2 কোটি = 20M
গড় ride request QPS = 20M / 86400 ≈ 230/sec
পিক (×10, অফিস টাইম) ≈ 2,300 ride request/sec
Storage:
প্রতি location update ~100 bytes
প্রতিদিন location data = 750k × 86400 × 100B ≈ 6.5 TB/দিন
(তবে current location শুধু মেমরিতে; history আলাদা/সংক্ষিপ্ত রাখা হয়)
Trip records/দিন = 20M × ~1KB ≈ 20 GB/দিন
মূল শিক্ষা: location write QPS বিশাল (সেকেন্ডে লক্ষ লক্ষ), তাই current location একটা দ্রুত in-memory store-এ রাখতে হবে, প্রতি update DB-তে নয়।
৩. API ডিজাইন
// Driver
POST /v1/drivers/location // { driverId, lat, lng } প্রতি ~4s
POST /v1/trips/{id}/accept // ride accept
// Rider
POST /v1/rides/request // { riderId, pickup, drop }
GET /v1/rides/{id}/eta // ETA জানা
WS /v1/rides/{id}/track // live tracking (WebSocket)
// Trip lifecycle
POST /v1/trips/{id}/start // ongoing
POST /v1/trips/{id}/complete // completed + ভাড়া হিসাব
GET /v1/pricing?lat=&lng= // surge multiplier
৪. ডেটা মডেল
| Table/Collection | মূল field |
|---|---|
| drivers | driver_id, name, vehicle, status (online/busy/offline) |
| driver_location (in-memory) | driver_id, geohash, lat, lng, updated_at |
| trips | trip_id, rider_id, driver_id, status, pickup, drop, fare, timestamps |
| riders | rider_id, name, payment_info |
| pricing_zones | zone_id, geohash_prefix, surge_multiplier |
SQL vs NoSQL choice: Trip records হলো transactional ও relational (একই driver দুজনকে assign হওয়া যাবে না, payment-এর সাথে যুক্ত) — তাই SQL (Postgres/MySQL) with ACID transaction। কিন্তু driver location হলো প্রচণ্ড high-write আর শুধু "এখনকার" মান দরকার — তাই এটা in-memory store (Redis, geospatial সাপোর্টসহ)-এ রাখা হয়, ধীর disk DB-তে নয়।
৫. হাই-লেভেল ডিজাইন
মূল components:
- Driver/Rider App: location পাঠায়, tracking দেখায় (WebSocket)।
- Load Balancer + API Gateway।
- Location Service: geospatial index update ও query।
- Matching Service: কাছের driver খুঁজে assign।
- Trip Service: trip lifecycle (SQL, transactional)।
- Pricing Service: surge হিসাব।
- Notification/WebSocket Service: live push।
Ride request flow:
- Rider
pickupদিয়ে ride request করে। - Pricing Service ওই zone-এর surge multiplier দেয়।
- Matching Service Location Service-কে জিজ্ঞেস করে: "এই geohash cell ও পাশের cell-এ online driver কারা?"
- কাছের driver-দের ranked তালিকা থেকে একজনকে offer পাঠানো হয়।
- Driver accept করলে Trip Service একটা transaction-এ trip তৈরি করে ও driver-কে
busymark করে (যাতে দুজনকে assign না হয়)। - WebSocket দিয়ে driver-এর live location rider-কে push হতে থাকে — live tracking।
- Trip শেষে fare হিসাব হয়, payment নেওয়া হয়।
৬. গভীরে (Deep Dive)
Geospatial Index ও Geohash
মূল সমস্যা: কোনো একটা বিন্দুর কাছের driver খুঁজতে গিয়ে লাখ লাখ driver-এর দূরত্ব এক এক করে হিসাব করা অসম্ভব ধীর। সমাধান হলো Geospatial Index — পুরো পৃথিবীকে ছোট ছোট cell-এ ভাগ করা।
- Geohash: lat/lng কে একটা ছোট string-এ রূপান্তর করে (যেমন
wh0r2)। কাছাকাছি জায়গার geohash prefix একই হয়, তাই "এই prefix-এর সব driver দাও" — এটা দ্রুত lookup। Redis-এ এটা সহজে index করা যায়। - QuadTree: ম্যাপকে recursively চার ভাগে ভাগ করে; ঘন এলাকা (যেমন গুলশান) বেশি ভাগ, ফাঁকা এলাকা কম ভাগ — adaptive।
- S2 (Google): গোলককে cell-এ ভাগ করে, খুব নিখুঁত — Uber এটা ব্যবহার করে।
কাজের ধারা: rider-এর cell ও পাশের cell-গুলোর (neighbor) driver বের করে, তারপর শুধু সেই ছোট তালিকায় নিখুঁত দূরত্ব হিসাব করা হয়।
প্রতি driver প্রতি 4 সেকেন্ডে location পাঠায় — এটা DB-তে লিখলে DB মরে যাবে। তাই current location শুধু Redis-এর মতো in-memory geospatial store-এ overwrite হয়; পুরো history দরকার হলে আলাদা stream/queue-তে যায়।
Driver-Rider Matching
Matching শুধু "সবচেয়ে কাছের driver" নয়। বিবেচনা করা হয় — দূরত্ব, ETA (রাস্তার অবস্থা), driver-এর rating, গাড়ির ধরন, আর কে আগে accept করবে। একটা বড় চ্যালেঞ্জ — race condition: একই driver যেন একই সময়ে দুজন rider-কে assign না হয়। তাই assignment হয় atomic ভাবে (lock বা DB transaction দিয়ে), আর driver সাথে সাথে busy হয়ে যায়।
Surge Pricing
Surge Pricing demand আর supply-এর ভারসাম্য রাখে। প্রতিটি zone-এ চলমান ride request আর available driver-এর অনুপাত দেখা হয়; request অনেক বেশি হলে multiplier বাড়ে (যেমন 1.8x)। এতে দুটো কাজ হয় — কিছু rider অপেক্ষা করে (চাহিদা কমে) আর বেশি driver ওই এলাকায় আসে (supply বাড়ে)। এটা একটা near-real-time aggregation pipeline-এ চলে, zone ধরে ধরে।
Matching-এ যদি সবাই একই "সবচেয়ে কাছের" driver-কে অফার পাঠায়, তাহলে একজন popular driver-এর কাছে অনুরোধের বন্যা আর বাকিরা বসে থাকবে। তাই matching-এ locking + state ঠিকমতো সামলানো জরুরি, নইলে double-booking বা missed match হবে।
৭. বটলনেক ও স্কেলিং
- Location write storm: সেকেন্ডে লক্ষ লক্ষ update — Redis geospatial + শহর/region ধরে sharding (প্রতি শহর আলাদা cluster)।
- Matching hotspot: পিক টাইমে নির্দিষ্ট এলাকায় চাপ — geohash cell ধরে কাজ ভাগ, আর queue দিয়ে smoothing।
- Trip consistency: double-assignment ঠেকাতে atomic transaction/lock।
- Live tracking scale: কোটি WebSocket connection — region-wise gateway server + horizontal scaling।
- Read scale: ETA, pricing-এর জন্য caching।
- City-based partitioning: Uber-এর সবচেয়ে কার্যকর কৌশল — প্রতিটি শহর প্রায় স্বাধীন unit হিসেবে চলে, কারণ ঢাকার ride সিলেটের driver-এর সাথে কখনো match হবে না।
৮. সারসংক্ষেপ
Uber ডিজাইনের কেন্দ্রে আছে geospatial indexing (Geohash/QuadTree/S2) — যা লক্ষ লক্ষ driver-এর মধ্যে কাছেরজনকে মুহূর্তে খুঁজে দেয়। Location update যায় দ্রুত in-memory store-এ, matching হয় atomic ভাবে (double-booking ঠেকাতে), trip চলে transactional SQL-এ, live tracking আসে WebSocket-এ, আর surge pricing চাহিদা-যোগানের ভারসাম্য রাখে। শহরভিত্তিক sharding পুরোটাকে scale করায়।
Interview-তে interviewer দেখতে চায় তুমি geospatial query-র চ্যালেঞ্জ বুঝেছ কিনা (সব driver scan করা যাবে না), high-write location কেন DB-তে নয়, এবং matching-এ race condition কীভাবে সামলাবে। "geohash দিয়ে কাছের driver খোঁজা" পরিষ্কার বললেই তুমি অন্যদের থেকে এগিয়ে।
মিনি কুইজ
1. Rider-এর কাছাকাছি driver দ্রুত খুঁজতে কোন কৌশল ব্যবহার হয়?
2. চাহিদা বেশি কিন্তু driver কম হলে ভাড়া বাড়ানোর কৌশলকে কী বলে?
3. Rider যখন গাড়ি ম্যাপে নড়তে দেখে, সেটা সম্ভব হয় মূলত কীসে?