A comprehensive competitive programming platform built with Spring Boot, React, and Docker. This platform enables users to participate in coding contests, submit solutions in multiple languages, and compete on live leaderboards.
- Live Coding Contests: Join active contests and solve programming problems
- Multi-Language Support: Submit solutions in Java, Python3, C++, and JavaScript
- Real-time Code Execution: Secure Docker-based code execution with resource limits
- Live Leaderboard: Track rankings with automatic 15-second refresh when viewing
- Asynchronous Processing: Non-blocking submission processing with 2-second status polling
- Test Case Validation: Automatic validation against sample and hidden test cases
- Simple Registration: Enter username to create account automatically
- Persistent Sessions: User data stored in localStorage for seamless experience
- Global Score Tracking: Cumulative score across all contests
- Problem Count: Track total problems solved across platform
- Active Contests: Currently running, open for participation
- Ended Contests: View-only mode with restricted submission
- Join Contest: One-click join with backend validation
- Contest Timer: Shows time remaining with visual indicators
- Participant Count: Real-time display of joined users
- Contest-specific Scoring: Separate scores per contest
- Structured Layout:
- Problem statement with formatted description
- Input/Output format specifications
- Constraints clearly defined
- Sample test cases visible
- Points allocation shown
- Language Selection: Dropdown to switch between Java, Python3, C++, JavaScript
- Pre-loaded Templates: Language-specific boilerplate code with instructions
- Monospace Font: Code-friendly typography
- Large Text Area: Ample space for coding (adjustable)
-
Run Code (Test Mode)
- Tests against sample cases only
- Immediate feedback without scoring
- Helps debug before final submission
-
Submit Code (Final)
- Tests against all cases (sample + hidden)
- Updates score and leaderboard
- Counts toward contest ranking
- Status States:
PENDING: In queue for processingRUNNING: Currently executingACCEPTED: All test cases passedPARTIALLY_ACCEPTED: Some cases passedWRONG_ANSWER: Output mismatchTIME_LIMIT_EXCEEDED: Execution timeoutCOMPILATION_ERROR: Code syntax issuesRUNTIME_ERROR: Execution crashes
- Live Status Updates: Poll every 2 seconds
- Test Case Results: Shows passed/failed count
- Execution Time: Display milliseconds taken
- Error Messages: Compilation/runtime errors shown
- Score Calculation: Points based on cases passed
- Real-time Rankings: Updates every 15 seconds when viewing
- Comprehensive Metrics:
- Rank position
- Username and full name
- Total score
- Problems solved count
- Last submission time
- Tab-aware: Only polls when leaderboard tab is active
- Automatic Refresh: No manual reload needed
- Efficient: Stops polling when navigating away
- Personal Metrics:
- Total score across all contests
- Total problems solved
- Active contests count
- Active Contests:
- Join button if not participated
- View button if already joined
- Problem count display
- End time shown
- Page Focus Detection: Updates when returning to tab
- Visibility API: Refreshes on page visibility change
- Per-Contest History: All attempts for current contest
- Detailed Information:
- Problem title
- Programming language used
- Submission status
- Score achieved
- Timestamp
- View code option
- Docker Isolation: Each submission in separate container
- Network Disabled: No internet access during execution
- Resource Limits:
- CPU: 1 core maximum
- Memory: 128-256MB per problem
- Time: 5 seconds for Java, varies by language
- Size Limits: Maximum code length enforced
- Input Validation: All API inputs sanitized
- SQL Injection Prevention: JPA parameterized queries
- FIFO Processing: Fair submission order
- Non-blocking: UI remains responsive
- Status Polling: Check result without page refresh
- Graceful Failures: Errors don't crash system
- User Feedback: Clear error messages
- Retry Logic: Automatic retry for transient failures
- Mobile Support: Touch-friendly interface
- Tablet Optimization: Efficient space usage
- Desktop Layout: Multi-column when space allows
- Loading States: Spinners during operations
- Success Indicators: Green checkmarks
- Error Highlights: Red error messages
- Progress Tracking: Status badges
- Efficient Polling: Only when needed
- Cleanup: Proper interval management
- Minimal Re-renders: Optimized React components
- In-Memory Database: Fast for demo
- Connection Pooling: HikariCP optimization
- Async Processing: Non-blocking operations
shodh-a-code/
βββ backend/ # Spring Boot backend
β βββ contest/ # Main application module
β β βββ src/
β β βββ pom.xml
β βββ docker/
β βββ executor/ # Docker execution environment
β βββ Dockerfile
βββ frontend/ # React frontend
β βββ src/
β βββ package.json
β βββ tailwind.config.js
βββ README.md
- Java 17+
- Node.js 18+
- Docker Desktop
- Maven 3.6+
cd backend/docker/executor
docker build -t code-executor .This creates a Docker image with Java, Python3, C++, and Node.js runtimes for code execution.
cd backend/contest
mvn spring-boot:runThe backend will start on http://localhost:8080 with an H2 in-memory database pre-populated with sample contests and problems.
cd frontend
npm install
npm run devThe frontend will start on http://localhost:5177
- Navigate to
http://localhost:5177 - Enter any username to login (e.g., "alice","bob","charlie" or you can register and create your own username)
- Browse contests and start solving problems!
GET /api/contests/{contestId}Response:
{
"id": 1,
"title": "Weekly Challenge #1",
"description": "Test your skills",
"startTime": "2024-01-01T10:00:00",
"endTime": "2024-12-31T18:00:00",
"isActive": true,
"problems": []
}POST /api/submissionsRequest:
{
"userId": 1,
"problemId": 1,
"code": "public class Solution {...}",
"language": "JAVA"
}Response:
{
"submissionId": "uuid-string",
"status": "PENDING"
}GET /api/submissions/{submissionId}Response:
{
"submissionId": "uuid-string",
"status": "ACCEPTED",
"score": 100,
"testCasesPassed": 3,
"totalTestCases": 3,
"executionTime": "150ms",
"errorMessage": null
}GET /api/contests/{contestId}/leaderboardResponse:
[
{
"rank": 1,
"userId": 1,
"username": "alice",
"score": 300,
"problemsSolved": 3,
"lastSubmission": "2024-01-15T14:30:00"
}
]GET /api/contests- List all contestsPOST /api/contests/join- Join a contestPOST /api/submissions/run- Test run without submissionGET /api/submissions/user/{userId}/contest/{contestId}- User's submissionsGET /api/languages- Supported programming languages
The backend follows a layered architecture with clear separation of concerns:
- Controller Layer: RESTful endpoints with request/response DTOs
- Service Layer: Business logic, including
CodeExecutorServicefor Docker orchestration - Repository Layer: JPA repositories for data persistence
- Entity Layer: Domain models with JPA annotations
Challenge: Safely executing untrusted user code with proper isolation and resource limits.
Solution:
- Docker containers provide complete isolation
- ProcessBuilder manages Docker commands programmatically
- Resource limits (CPU, memory, time) prevent abuse
- Automatic cleanup ensures no resource leaks
Trade-offs:
- Pros: Strong security, language flexibility, resource control
- Cons: Docker dependency, container startup overhead
Implemented a SimpleQueueService for submission processing:
- In-memory queue for simplicity
- Async processing with
@Asyncannotation - Could scale to Redis/RabbitMQ for production
Choice: Zustand for global state management
Reasoning:
- Lightweight (8kb) compared to Redux
- Simple API with hooks
- TypeScript support out of the box
- Perfect for medium-sized applications
State Structure:
{
user: User | null,
selectedLanguage: ProgrammingLanguage,
joinedContests: Set<number>
}Polling Strategy:
- Submission Status: 2-second intervals until completion
- Live Leaderboard:
- Polls every 15 seconds when leaderboard tab is active
- Immediate refresh when switching to leaderboard tab
- Stops polling when navigating away (efficient resource usage)
- Automatic cleanup on component unmount
- Visual feedback during all async operations
Component Structure:
Contest.tsx: Main contest hub with tabsProblem.tsx: Code editor and submission logicLeaderboard.tsx: Reusable leaderboard component
- Multi-stage build not needed (execution only)
- Minimal Ubuntu base for smaller image
- Pre-installed language runtimes
- Non-root user for security
- Write user code to temporary file
- Execute Docker run with mounted volume
- Pipe test input via stdin
- Capture stdout and compare
- Clean up container and files
Security Measures:
- Network isolation (
--network none) - Memory limits (
--memory) - CPU limits (
--cpus) - Time limits (
timeoutcommand) - Read-only filesystem where possible
- Code Execution: Fully isolated in Docker containers
- Resource Limits: Strict CPU, memory, and time constraints
- Input Validation: All API inputs validated
- Authentication: Simplified for demo (production would need JWT)
- SQL Injection: Protected via JPA/Hibernate
- In-memory H2 database (switch to PostgreSQL)
- Single-instance processing (add worker nodes)
- Synchronous Docker execution (use message queue)
- PostgreSQL/MySQL for persistence
- Redis for caching and queues
- Kubernetes for container orchestration
- Load balancer for multiple instances
- CDN for static assets
- Login with any username
- Navigate to "Weekly Challenge #1"
- Select "Two Sum" problem
- Submit this solution:
n = int(input())
arr = list(map(int, input().split()))
target = int(input())
for i in range(n):
for j in range(i+1, n):
if arr[i] + arr[j] == target:
print(f"{i} {j}")
exit()- Watch status update from PENDING β RUNNING β ACCEPTED
- Check leaderboard for updated rankings
- 3 Contests:
- Daily Challenge #1 (Active, 3 problems)
- Weekly Coding Challenge #2 (Active, 3 problems)
- Past Challenge #1 (Ended, 2 problems - view only, with leaderboard)
- 8 Problems: Various difficulty levels
- Test Cases: Both sample and hidden for each problem
- 4 Sample Users: alice, bob, charlie, dylan
- Ended Contest Leaderboard:
- Alice: 125 points (2 problems solved)
- Bob: 75 points (1 problem solved)
- Charlie: 50 points (1 problem solved)
- Dylan: 0 points (attempted but failed)
- Rapid development with auto-configuration
- Excellent JPA/Hibernate integration
- Built-in async support
- Strong ecosystem for REST APIs
- Simpler deployment model
- No SSR requirements for this use case
- Faster development iteration
- Client-side routing sufficient
- Alternatives Considered:
- VM (too heavy)
- Process isolation (insufficient security)
- Serverless functions (complex setup)
- Docker Benefits:
- Perfect balance of isolation and performance
- Easy local development
- Consistent across environments
- WebSocket Integration: Replace polling with real-time updates
- Code Templates: Language-specific boilerplate
- Problem Tags: Categorize by difficulty and topic
- User Profiles: Track historical performance
- Admin Panel: Contest and problem management
- Plagiarism Detection: Code similarity checking
Problem: "Code execution environment not available" error
Docker execution is disabled. Cannot execute submission
Solutions:
- Ensure Docker Desktop is installed and running
- Build the executor image:
cd backend/docker/executor docker build -t code-executor .
- Verify Docker is accessible:
docker version docker images | grep code-executor
Problem: "Exit code 124" or timeout errors
Error: Unknown error (exit code: 124)
Solutions:
- This indicates a timeout. The default time limits are problem-specific (usually 1-5 seconds)
- Check if your code has infinite loops
- Java compilation time is included in the time limit
Problem: "class FizzBuzz is public, should be declared in a file named FizzBuzz.java"
Solution:
- Your main class MUST be named
Solution(not Main, not the problem name) - Correct format:
public class Solution { public static void main(String[] args) { // Your code here } }
Problem: Frontend not loading or API connection errors
Solutions:
- Ensure backend is running on
http://localhost:8080 - Frontend should be on
http://localhost:5177 - Check CORS is configured correctly in backend
- Clear browser cache and localStorage
Problem: H2 console not accessible
Solution:
- Access at:
http://localhost:8080/h2-console - JDBC URL:
jdbc:h2:mem:contestdb - Username:
sa - Password: (leave empty)
Problem: Data not persisting after restart
Solution:
- H2 is configured as in-memory database
- Data is re-initialized on each startup from
DataInitializer.java - For persistent storage, switch to PostgreSQL/MySQL in production
Problem: Submissions stuck in PENDING status
Solutions:
- Check backend logs for queue processing errors
- Verify Docker is running and accessible
- Check SimpleQueueService logs:
grep "Queue processor" backend.log grep "Added submission" backend.log
Problem: Incorrect scores or leaderboard not updating
Solutions:
- Scores are calculated as: (test cases passed / total test cases) Γ problem points
- Leaderboard updates every 15 seconds when viewing the leaderboard tab
- Check ContestParticipant records in H2 console
Problem: Maven build fails
Solutions:
- Ensure Java 17+ is installed:
java -version
- Clear Maven cache:
mvn clean mvn dependency:purge-local-repository
Problem: npm install fails
Solutions:
- Ensure Node.js 18+ is installed:
node --version
- Clear npm cache:
npm cache clean --force rm -rf node_modules package-lock.json npm install
Problem: "Contest has ended" message when trying to submit
Solution:
- Check contest end time in the database
- Ended contests are view-only (Past Challenge #1 is intentionally ended)
- Only active contests accept submissions
Problem: No test cases showing for problems
Solution:
- Verify test cases exist in database
- Sample test cases (non-hidden) are shown to users
- Hidden test cases only run during final submission
Backend logging is configured in application.yml:
logging:
level:
com.shodhacode: DEBUG
org.springframework: INFO-
Backend Health Check:
curl http://localhost:8080/api/contests
-
Docker Status:
docker ps # Check running containers docker images | grep code-executor # Verify image exists
-
Queue Processing:
- Look for these log messages:
Queue processor started with 4 worker threads Added submission to queue Processing submission Execution completed
# Check for Docker issues
grep -i "docker" logs/backend.log
# Check submission processing
grep "submission.*RUNNING\|ACCEPTED\|WRONG_ANSWER" logs/backend.log
# Check for errors
grep -i "error\|exception" logs/backend.log
# Monitor queue activity
tail -f logs/backend.log | grep -i "queue\|submission"If experiencing slow performance:
-
Increase JVM heap size:
java -Xmx2048m -jar backend/contest/target/contest-0.0.1-SNAPSHOT.jar
-
Adjust thread pool size in
SimpleQueueService.java:executorService = Executors.newFixedThreadPool(8); // Increase from 4
-
Optimize Docker resource limits in
CodeExecutorService.java:commandParts.add("--memory=256m"); // Increase if needed
For more detailed information about specific components:
- Backend Documentation: See backend/README.md for Spring Boot architecture, API details, and Docker configuration
- Frontend Documentation: See frontend/README.md for React components, state management, and UI implementation
This is a demonstration project showcasing full-stack development capabilities with Spring Boot, React, and Docker integration.