Skip to content

Qasim-Rizwan/Ticketing_system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎫 In-Memory Ticketing System

A backend service for managing event tickets with thread-safe booking operations. Built with Python and FastAPI.

Overview

This system allows users to create events, check seat availability, book seats, and cancel bookings. All data is stored in-memory with proper concurrency handling to prevent race conditions during simultaneous booking attempts.

Project Structure

Ticketing_system/
β”œβ”€β”€ models.py           ← Data structures (Events and Bookings)
β”œβ”€β”€ storage.py          ← In-memory storage with thread safety
β”œβ”€β”€ business_logic.py   ← Core booking logic (race condition prevention)
β”œβ”€β”€ main.py             ← FastAPI routes (API endpoints)
β”œβ”€β”€ test_api.py         ← Tests including concurrency test
β”œβ”€β”€ requirements.txt    ← Dependencies
└── README.md           ← This file

πŸš€ Quick Start (3 Steps!)

Step 1: Install Dependencies

cd Ticketing_system
pip install -r requirements.txt

Step 2: Run the Server

uvicorn main:app --reload

You should see:

==================================================
  TICKETING SYSTEM API STARTED
==================================================
  API Documentation: http://localhost:8000/docs
  Test it: http://localhost:8000/
==================================================

Step 3: Open Browser

Go to: http://localhost:8000/docs

You'll see an interactive API page where you can test everything!


πŸ“š Understanding the Code

1. models.py - Data Structures

Defines what our data looks like using Pydantic models:

  • Event: name, date, venue, total_seats, available_seats
  • Booking: event_id, num_seats, status (confirmed/cancelled)

Key Point: Pydantic automatically validates data for us!

2. storage.py - In-Memory Database

Stores data in Python dictionaries:

events = {event_id: Event object}
bookings = {booking_id: Booking object}

Key Point: Uses threading.Lock() to prevent race conditions!

3. business_logic.py -

Contains the actual logic for:

  • Creating events
  • Making bookings (with race condition prevention!)
  • Cancelling bookings

Most Important Part: The create_booking() function uses a lock:

with booking_lock:  # Only ONE person can book at a time
    # Check if seats available
    # Reduce available seats
    # Create booking
# Lock released - next person can book

This prevents overbooking when multiple people book simultaneously!

4. main.py - API Endpoints

Defines all the URLs you can call:

Events:

  • POST /events - Create event
  • GET /events - List all events
  • GET /events/{id} - Get event details
  • GET /events/{id}/availability - Check seats

Bookings:

  • POST /bookings - Book seats
  • GET /bookings/{id} - Get booking details
  • PATCH /bookings/{id}/cancel - Cancel booking

5. test_api.py - Proof It Works!

Tests including the critical race condition test:

  • 5 people try to book 3 seats each from event with 10 total seats
  • Only 3 succeed (9 seats), 2 fail
  • Proves our locking works!

πŸ§ͺ Running Tests

pytest test_api.py -v

Expected output:

test_create_event PASSED
test_list_events PASSED
test_check_availability PASSED
test_create_booking PASSED
test_booking_not_enough_seats PASSED
test_cancel_booking PASSED
test_concurrent_bookings_no_overbooking PASSED 

 RACE CONDITION TEST PASSED!
   - 3 bookings succeeded (correct)
   - 2 bookings failed (correct)
   - 1 seat remaining (correct)

High-Level Architecture Design

System Architecture

The application follows a clean, layered architecture pattern:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     API Layer (FastAPI - main.py)   β”‚  ← HTTP endpoints, request/response handling
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Business Logic (business_logic.py) β”‚  ← Core booking logic, race condition prevention
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    Storage Layer (storage.py)       β”‚  ← In-memory data storage with thread safety
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    Data Models (models.py)          β”‚  ← Pydantic models for validation
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Component Responsibilities

1. API Layer (main.py)

  • Defines HTTP endpoints (POST /events, POST /bookings, etc.)
  • Handles request validation using Pydantic
  • Returns standardized JSON responses
  • Delegates business logic to the service layer

2. Business Logic Layer (business_logic.py)

  • Implements core operations (create event, create booking, cancel booking)
  • Enforces business rules (seat availability validation)
  • Contains the critical race condition prevention mechanism
  • Orchestrates calls to the storage layer

3. Storage Layer (storage.py)

  • Manages in-memory data structures (dictionaries for O(1) lookups)
  • Provides thread-safe data access using threading.Lock()
  • Handles CRUD operations for events and bookings

4. Data Models (models.py)

  • Defines structure for Events and Bookings using Pydantic
  • Automatic validation (dates must be in future, seats must be positive)
  • Type safety throughout the application

Data Flow Example: Creating a Booking

1. Client β†’ POST /bookings {"event_id": "...", "num_seats": 2}
2. API Layer validates request using Pydantic models
3. API calls business_logic.create_booking()
4. Business Logic:
   a. Acquires lock (prevents race conditions)
   b. Checks event exists
   c. Validates seat availability
   d. Updates available seats (via storage layer)
   e. Creates booking record
   f. Releases lock
5. API returns booking confirmation to client

Important Implementation Decisions

1. Race Condition Prevention (Most Critical)

Problem: When multiple users try to book seats simultaneously, there's a risk of overbooking. Without proper synchronization:

  • User A checks: 5 seats available βœ“
  • User B checks: 5 seats available βœ“
  • User A books 3 seats β†’ 2 remaining
  • User B books 3 seats β†’ OVERBOOKING! (-1 seats)

Solution: I used Python's threading.Lock() to make the entire check-and-update operation atomic.

# In business_logic.py
booking_lock = threading.Lock()

def create_booking(booking_data):
    with booking_lock:  # Only ONE booking at a time
        # 1. Check event exists
        # 2. Check seat availability
        # 3. Reduce available seats
        # 4. Create booking
        # All steps happen atomically
    # Lock released - next booking can proceed

Why this works:

  • The with booking_lock: ensures only one thread can execute the booking logic at a time
  • Other threads wait their turn
  • By the time the second user's request is processed, they see the updated seat count
  • No overbooking possible

Verification: The test_concurrent_bookings_no_overbooking test proves this works by simulating 5 simultaneous booking attempts on a 10-seat event. Only 3 succeed (using 9 seats), 2 fail correctly.

2. Dictionary-Based Storage

Decision: Use Python dictionaries with UUID keys for data storage.

events = {event_id: Event_object}
bookings = {booking_id: Booking_object}

3. Separate Locks for Events and Bookings

Decision: Use separate threading.Lock() instances for event and booking operations.

Why:

  • Better concurrency: Reading event details doesn't block booking operations
  • Prevents unnecessary waiting
  • More granular control over critical sections

Could improve: Use per-event locks instead of global lock for even better concurrency (explained in "Future Improvements" section).

Known Limitations

1. Data Persistence

Issue: All data is stored in memory and lost when the server restarts.

Impact: Not suitable for production use. Every server restart erases all events and bookings.

2. Single-Process Architecture

Issue: The threading.Lock() only works within a single Python process.

Impact: Cannot scale horizontally by adding more server instances. If you run multiple servers behind a load balancer, each has its own memory and locks don't synchronize across processes.

3. Global Booking Lock

Issue: Only one booking can be processed at a time, even for different events.

Impact: Potential bottleneck under high load. If 1000 people are booking tickets for different events, they all wait in line.

Better approach: Per-event locks (explained in improvements section).


What I Would Improve With More Time

High Priority Improvements

1. Database Integration

Current: In-memory dictionaries Improvement: PostgreSQL database

Current: threading.Lock() (single process only) Improvement: Redis-based distributed locks

Current: Global lock for all bookings Improvement: Separate lock for each event

About

Event Ticketing System

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages