Wednesday, August 12, 2020

Coupon Management System: System Design Interview

 Original link: Link


I was asked to design a Coupon Management System for XYZ e-commerce website where 10k new coupons are released into the system by employees of company every day and 1M users are trying to get hold of coupon, so that they can buy something on the website. These are the constraints:

  1. One user can get only one coupon per day
  2. Once a user gets a coupon he/she has to buy something on the website within 5 mins.

Functional Requirements:

  • Generate valid coupons
  • Users can only get one coupon per day

Non-Functional Requirements:

  • Low Latency
  • High Availability

Estimation:

  • Read heavy traffic
  • Traffic
    • 1 million requests per to get coupons per day
    • 10k coupons generated by employees each day
  • Storage
    • If we store coupon data for a year’s time:
      • 10k coupons * 365 = 3.65M objects
    • If each object is roughly of size 100bytes
      • Total storage req: 100 * 3.65M = 365 MB/year

API:

  • Customers requesting coupon:
    • getCoupon(string id)
    • GET /api/v1/coupon?id=xyz
  • Employees posting coupon
    • putCoupon(string empId, string category)
    • POST /api/v1/coupon?
      • JSON: { id:””, category:””}

**High Level Design: **

  • U1, U2, … U-n are the users requesting for coupons from this service
  • E1, E2, … E-n are the employees generating coupons**

image

**DB Schema: **

  • Since we need ACID kind of compliance I chose relational DB(Postgres)
  • Cassandra is used to store history of coupons like which coupon was claim which particular user
  • Table Coupons
    • couponCode, couponEmpId, category, status, generatedTime
    • couponCode: is the primary key. This is generated by the Key Generation Service
    • couponEmpId: employee id who generated this coupon. For analytics purposes
    • category: What category does this coupon belong to? Electronics, Sport etc.
    • status: Current status of system. Encoded like 101 for not-used and 102 for used
  • Table Users
    • userId, name, dob, location, lastCouponRequestTime
    • userId: Primary Key
    • name: User name
    • location: User location
    • lastCouponRequestTime: Time when this user successfully got a coupon.

** Deep Dive: **

  • Coupon Generation Service: Responsible for generating new coupons in the system. Employees would create new coupons in the system. This service would get the coupon details from employee, get the coupon code from Key Generation Service and store the corresponding data in clustered Coupon table (to master node)
  • Key Generation Service: Responsible for generating unique id’s/coupon-code to be used in the system. Can use Twitter SnowFlake approach to generate 64bit unique id’s.
    *|| 1bit reserved || 41 bits: timestamp || 5 bits: datacenter id || 5 bits: machine id || 12 bits: sequence number ||
    • Can talk about Clock Synchronization if time permits
  • Rate Limiter: Keeps the system fair. Prevents users from abusing the system. If a user can claim a coupon only once a day or once every hour, this service would ensure that. It would check the user table when was the last time this user received a coupon & update the row with new time if necessary. Can blacklist abusive clients if necessary
  • Coupon Claim Service: Hands out coupons to user’s. When user’s call getCoupon(), requests are routed to this service. The service:
    • Calls Rate Limiter service to see if a new coupon can be handed to this user or not. If cannot be allocated, then sends out a 429 Too Many Requests to client.
    • If the client can get a coupon, then
      • Gets a new coupon data from Coupon table & marks it used.
      • Then puts this coupon in Redis with an expiry time of 5 mins.
      • Informs Usage Tracking Service that this particular user got the coupon at this time so that it can track whether the coupon got used within 5 mins or not
      • On successful transaction, persists the data(coupon code) into Cassandra for future analytics
      • On unsuccessful transaction updates coupon table indicating this coupon is available for use
  • Usage Tracking Service & Redis: Used to track if a transaction succeeded or not.
    • Successful Tx: User got the coupon & used it within 5 mins. Usage Tracking service would delete the corresponding entry from Redis so that we do not get a timeout exception from Redis when the timer for this entry expire.
    • Unsuccessful Tx: Redis expires and this exception will be handled by Coupon Claim Service, which will mark the coupon as available in the Coupon table

I am looking forward for feedback & what are good techniques to shard db in this scenario when data size increases?

system design

No comments:

Post a Comment

Amazon OA Questions Compiled

 Original post: Link Here is a list of Amazon OA i have compiled during my preparation. Sorry if the problems are repeated. I hope this help...