Day 7: Quiz Session Management

Lesson 7 30 min

Day 7: Quiz Session Management - Building Robust Session Tracking for Scale


The Challenge: Keeping Track of Millions of Quiz Attempts

State Machine

Quiz Session Management Use Cases Session Management System Student Instructor System Admin Start Quiz Session Save Progress Resume Session Submit Quiz View Session History Monitor Sessions Auto-Save Progress (Background Process) Expire Sessions «include» Legend: Primary Actor Include Relationship Background Process

Flowchart

Quiz Session Management Sequence Client API Session Manager Redis PostgreSQL POST /sessions {user_id, quiz_id} create_session() INSERT attempt SETEX session TTL: 1800s session_id 201 Created {session_data} Auto-Save Loop every 30 seconds UPDATE last_updated Update Progress: PUT /progress update_progress() GET session UPDATE with version SETEX updated Synchronous Call Return Message Activation Background Process

Component Architecture

Quiz Session Management Class Diagram QuizAttempt - id: str - user_id: str - quiz_id: str - started_at: datetime - answers: Dict[int, str] - status: AttemptStatus - version: int + to_dict(): dict «enumeration» AttemptStatus STARTED IN_PROGRESS COMPLETED EXPIRED SessionManager - auto_save_tasks: Dict + create_session(user_id, quiz_id) + get_session(session_id) + update_progress(id, q_id, answer) + complete_session(session_id) - _auto_save_loop(session_id) + expire_sessions() + validate_session(session_id) DatabaseService - pg_pool: Pool - redis_client: Redis + init_db(): void + create_tables(): void + get_connection(): Connection + close(): void SessionEndpoints - session_manager: SessionManager + create_session(request) + get_session(session_id) + update_progress(id, request) + complete_session(session_id) + get_user_sessions(user_id) AutoSaveService - save_interval: int - running_tasks: Set + start_auto_save(session_id) + stop_auto_save(session_id) - save_session(session_id) CreateSessionRequest + user_id: str + quiz_id: str + validate(): bool SessionResponse + id: str + status: str + time_remaining: int + to_json(): str UpdateProgressRequest + question_id: int + answer: str + timestamp: datetime + validate(): bool uses creates uses uses uses Relationships: Association Dependency Entity Class Service Class DTO/Request Design Patterns Used: • Repository Pattern (DatabaseService) • Service Layer Pattern (SessionManager) • DTO Pattern (Request/Response Objects) • Background Task Pattern (AutoSaveService) • Optimistic Locking (Version Control)

Imagine you're Netflix, but instead of tracking what episode someone watched last, you're tracking where students left off in their quizzes. When a student accidentally closes their browser mid-quiz, can your system remember their progress? When your server crashes during peak hours, do thousands of students lose their work? This is the core challenge of session management in distributed systems.

Today, we're building a quiz attempt tracking service that can handle real-world scale - the kind of system that powers platforms like Khan Academy or Coursera, where millions of students take quizzes simultaneously.

Stateful vs Stateless: The Fundamental Choice


Stateful Sessions: The Personal Assistant Approach


Think of a stateful session like having a personal assistant who remembers everything about your conversation. When you say "Continue where we left off," they know exactly what you mean. In our quiz system, this means storing session data on the server.

Advantages:


  • Rich user experience with seamless continuity

  • Lower bandwidth (no need to send full state each time)

  • Complex interactions possible (like adaptive questioning)


Challenges:

  • Server memory consumption grows with users

  • Horizontal scaling requires session sharing mechanisms

  • Server failures can lose user progress


Stateless Sessions: The Ticket System Approach


Stateless sessions work like airline tickets - everything you need to know is written on the ticket itself. Each request contains all necessary information to process it independently.

Advantages:


  • Infinite horizontal scaling potential

  • No memory pressure on servers

  • Server failures don't affect user sessions

  • Perfect for microservices architecture


Challenges:

  • Larger payloads per request

  • Security considerations for client-stored data

  • More complex state validation logic


Real-World Application: Why This Matters


Major platforms make different choices based on their constraints:

  • Google Forms: Stateless with periodic auto-saves to cloud storage

  • Khan Academy: Hybrid approach with stateless progress tracking but stateful user preferences

  • Proctored Exams: Heavily stateful for security and monitoring


For our quiz platform, we'll implement a hybrid approach that gives us the best of both worlds.

Component Architecture Overview


Our quiz session management system consists of four key components:

  1. Session Manager: Handles session lifecycle (create, update, complete)

  2. State Store: Persistent storage for quiz progress

  3. Session Validator: Ensures session integrity and prevents cheating

  4. Auto-Save Service: Periodic background saves for fault tolerance


The system uses JWT tokens for stateless session identification while maintaining stateful progress data in Redis for performance and PostgreSQL for persistence.

Implementation Strategy


Core Data Models


@dataclass
class QuizAttempt:

id: str
user_id: str
quiz_id: str
started_at: datetime
current_question: int
answers: Dict[int, str]
status: AttemptStatus
time_remaining: int


Session Management Flow



  1. Start Quiz: Generate unique session ID, initialize attempt record

  2. Save Progress: Update answers and current position atomically

  3. Auto-Save: Background service saves every 30 seconds

  4. Complete Quiz: Finalize attempt, calculate score, update statistics


Key Implementation Insights


Atomic Operations: Use database transactions to ensure consistency
async def saveprogress(attemptid: str, question_id: int, answer: str):

async with db.transaction():
    await update_attempt_progress(attempt_id, question_id, answer)
    await update_session_timestamp(attempt_id)


Optimistic Locking: Handle concurrent updates gracefully
# Version-based conflict resolution
if attempt.version != stored_version:

raise ConflictError("Session modified by another request")


Circuit Breaker Pattern: Degrade gracefully when storage fails
# Fallback to in-memory storage if Redis fails
if not redis_available():

return await in_memory_store.save(attempt)


Hands-On Implementation Guide


Project Structure


quiz-session-service/
├── src/
│ ├── models/attempt.py
│ ├── services/sessionmanager.py
│ ├── api/session
endpoints.py
│ └── storage/repositories.py
├── tests/
├── docker/
└── scripts/

Build and Test Commands


Without Docker:
# Setup virtual environment
python -m venv venv && source venv/bin/activate

Install dependencies

pip install fastapi redis sqlalchemy pytest

Run tests

pytest tests/ -v

Start service

uvicorn src.main:app --reload --port 8002


With Docker:
# Build and run services
docker-compose up --build

Run integration tests

docker-compose exec app pytest tests/integration/


Verification Steps:
# Test session creation
curl -X POST localhost:8002/sessions
-H "Content-Type: application/json"
-d '{"userid": "123", "quizid": "quiz_1"}'

Test progress saving

curl -X PUT localhost:8002/sessions/{sessionid}/progress
-d '{"question
id": 1, "answer": "A"}'

Verify auto-save functionality

Check logs for periodic save messages every 30 seconds


Assignment: Build Your Own Session Manager


Objective: Implement a basic quiz session manager that handles concurrent users

Requirements:


  1. Create session endpoints for start/save/complete operations

  2. Implement auto-save functionality using background tasks

  3. Add session expiration (30-minute timeout)

  4. Build conflict resolution for concurrent updates

  5. Create integration tests for the complete flow


Success Criteria:

  • Handle 100 concurrent quiz sessions without data loss

  • Gracefully recover from server restarts

  • Provide sub-200ms response times for progress saves


Bonus Challenge: Implement session migration between server instances using Redis pub/sub.

Key Takeaways



  1. Choose the right pattern: Stateless for scale, stateful for experience

  2. Design for failures: Auto-save and recovery mechanisms are essential

  3. Think atomically: Use transactions for data consistency

  4. Monitor everything: Session lifecycle metrics reveal system health

  5. Security matters: Validate session ownership and prevent tampering


Next week, we'll integrate AI services into our platform, building on this robust session foundation to create adaptive quiz experiences that adjust difficulty based on user performance.

Production Readiness Checklist



  • [ ] Implement session encryption for sensitive data

  • [ ] Add distributed rate limiting per user

  • [ ] Create session analytics and monitoring

  • [ ] Build session migration for zero-downtime deployments

  • [ ] Implement audit logging for compliance requirements


Remember: Great systems aren't just about handling happy paths - they shine when things go wrong. Your session management system is the safety net that keeps user trust intact when servers fail and networks partition.