Welcome to the ShellCon Smart Aquarium System! This interactive project will introduce you to building high-performance microservices with Rust and Shuttle Cloud.
Imagine you've just joined the emergency technical response team for ShellCon, the world's premier convention for Rustaceans and crustaceans alike! This year's main attraction is a revolutionary Smart Aquarium system built with Rust and deployed on Shuttle.
The problem? Just hours before the convention opens, several performance issues have been detected in the backend services. As the newly recruited Rustacean engineer, you've been called in to optimize these systems before the doors open to the public.
The convention organizers are in a pinchβquite literally, as the convention's mascot, a giant Coconut Crab named Ferris, is anxiously clicking his claws at the mounting technical issues!
The Smart Aquarium System consists of three backend microservices and a separate frontend application:
- aqua-monitor: Collects real-time environmental data from tank sensors
- species-hub: Manages the species database and feeding requirements
- aqua-brain: Performs analysis and coordinates system responses
The frontend application is available in a separate repository at shuttle-shellcon-frontend. The UI provides:
- A description of the challenges to solve (They can also be found in this repository under the
challenges
folder) - Interactive challenge validation
- Detailed lecture materials for each challenge
- Visual feedback on your solutions
- Real-time monitoring of your aquarium system
To get started with the frontend:
- Clone the frontend repository (Not now, you will do it later as indicated in step 4 below)
- Follow the setup instructions in its README
- Configure it to point to your deployed services
Shuttle is a platform that makes deploying Rust applications simple. For best productivity, you'll iterate locally with Shuttle, then deploy to the cloud for final validation.
- Rust (1.70 or newer)
- Docker (for local database provisioning and running services locally)
- Important: In Docker Desktop settings, enable "Allow the default Docker socket to be used" option
- Shuttle CLI (latest version)
If you don't have a Shuttle account, please create one here.
# Install the Shuttle CLI
curl -sSfL https://www.shuttle.dev/install | bash
Login right after installing the CLI to avoid interruptions later:
shuttle login
Open three terminal windows/tabs and run each service on its designated port:
# Terminal 1
cd services/aqua-monitor
shuttle run --port 8000
# Terminal 2
cd services/species-hub
shuttle run --port 8001
# Terminal 3
cd services/aqua-brain
shuttle run --port 8002
- You can directly run
curl
commands to test the services by using the ports:- aqua-monitor: http://localhost:8000
- species-hub: http://localhost:8001
- aqua-brain: http://localhost:8002
For example, validate the aqua-monitor service by running:
# Test aqua-monitor service health
curl http://localhost:8000/api/health
Iterate and solve the challenges by editing code and re-running the services as needed.
If you get an error about a port being in use, free it with:
lsof -ti :<port> | xargs kill -9
Clone the frontend repository:
git clone https://github.com/shuttle-hq/shuttle-shellcon-frontend.git
Then follow the setup instructions in the frontend repository's README.
- Make your code changes and run the affected service locally by killing the process and restarting the service with
shuttle run --port <port>
. - Start the frontend with
npm run dev:localhost
(see frontend repo for setup). - Use the frontend UI to validate your solution by clicking the "Validate your solution" button under the challenge description. This is the required method for validation.
- Optionally, use Thunder Client, curl, or Postman to test API endpoints for debugging.
- Ensure all challenges pass locally before moving to cloud deployment.
When your solution passes locally, deploy each service to Shuttle Cloud. Repeat these steps for all three services:
For each service:
- Go to the service directory from the repository root:
- For aqua-monitor:
cd services/aqua-monitor
- For species-hub:
cd services/species-hub
- For aqua-brain:
cd services/aqua-brain
- For aqua-monitor:
- Verify that the
shuttle.toml
file is present in the directory. This file is required for deployment. - Deploy the service:
shuttle deploy
- When prompted:
- Select
[CREATE NEW]
. - For the project name, use the name of the service you are currently deploying (the name of your current folder). For example, enter
aqua-monitor
if you are in theaqua-monitor
directory.
- Select
- After deploying, Shuttle will provide a unique URL for the service (e.g.,
https://aqua-monitor-xxxx.shuttleapp.app
).
Repeat these steps for all three services.
Update your frontend configuration:
- Open your frontend project's
.env.prod
file. - Copy the unique URLs for all three services (aqua-monitor, species-hub, aqua-brain) into the corresponding variables in
.env.prod
. - Your
.env.prod
file should look like this (replace with your actual URLs):
VITE_AQUA_MONITOR_URL=https://aqua-monitor-xxxx.shuttleapp.app
VITE_SPECIES_HUB_URL=https://species-hub-xxxx.shuttleapp.app
VITE_AQUA_BRAIN_URL=https://aqua-brain-xxxx.shuttleapp.app
- Restart the frontend with
npm run dev:prod
to connect to your cloud services. - Use the frontend UI to validate your solution against the cloud endpoints by clicking the "Validate your solution" button.
After deploying, Shuttle will provide a unique URL for each service. Example output:
Created project 'aqua-monitor' with id proj_xxxxxx
Linking to project 'aqua-monitor' with id proj_xxxxxx
Packing files...
Uploading code...
Creating deployment...
Deployment depl_xxxxxx - running
https://aqua-monitor-xxxx.shuttle.app
Record the URLs for:
- aqua-monitor (e.g., https://aqua-monitor-xxxx.shuttleapp.app)
- species-hub (e.g., https://species-hub-xxxx.shuttleapp.app)
- aqua-brain (e.g., https://aqua-brain-xxxx.shuttleapp.app)
Local vs Cloud:
- Local: Use ports (8000, 8001, 8002) with
localhost
and run frontend withnpm run dev:localhost
. - Cloud: Use the
.shuttleapp.app
URLs and run frontend withnpm run dev:prod
.
Verify that all services are running and accessible:
# Test aqua-brain service
curl https://aqua-brain-xyz123.shuttle.app/api/health
# Test aqua-monitor service
curl https://aqua-monitor-abc456.shuttle.app/api/health
# Test species-hub service
curl https://species-hub-def789.shuttle.app/api/health
Each service should respond with a 200 OK status.
- Re-validate your solution against the cloud endpoints using the frontend UI button "Validate your solution".
Your mission is to solve five performance challenges across the microservices. Each challenge focuses on a different aspect of backend optimization in Rust.
- Service: aqua-monitor
- File: src/challenges.rs
- Function: get_tank_readings
- Problem: The environmental monitoring system is experiencing severe delays due to inefficient file I/O operations.
- Service: species-hub
- File: src/challenges.rs
- Function: get_species
- Problem: The species database is responding slowly to searches due to inefficient queries.
- Service: aqua-brain
- File: src/challenges.rs
- Function: get_analysis_result
- Problem: The analysis engine is consuming excessive memory due to inefficient string handling.
- Service: aqua-monitor
- File: src/challenges.rs
- Function: get_sensor_status
- Problem: The sensor status API is creating a new HTTP client for every request, causing resource leaks.
Follow this workflow to solve each challenge:
Examine the challenge description and the problematic code:
# View the source code for the challenge
cat services/aqua-monitor/src/challenges.rs
Look for the challenge tag (e.g., // β οΈ CHALLENGE #1: ASYNC I/O β οΈ
).
Edit the code to fix the performance issue. You can:
- Read the challenge lecture in the UI for detailed explanations
- Click the "Show Hint" button in the UI if you're stuck
- View the solution guide for step-by-step instructions
- Check the code comments for additional hints
# Format and check your code
cd services/aqua-monitor
cargo fmt
cargo check
# Run the service locally to verify your changes compile and run
shuttle run --port 8000
Make sure your frontend is running with npm run dev:localhost
to connect to your local services.
- Look for blocking I/O operations that should be async
- Consider using
tokio::fs
instead of standardstd::fs
- Examine the SQL query for inefficient patterns
- Consider adding indexes or using case-insensitive search
- Look for excessive String allocations
- Consider using string references (&str) where appropriate
- Identify resources being created for each request
- Use static instances for expensive resources
If your deployment fails:
# Check the deployment logs
shuttle logs --latest
Common issues include:
- Compilation errors
- Missing dependencies
- Configuration problems
If your solution isn't being validated correctly:
- Check Implementation: Ensure your solution matches the expected pattern
- Verify Deployment: Make sure your changes were properly deployed
- Examine Logs: Check the service logs for validation errors
The Smart Aquarium System follows a microservices architecture where each service has a specific responsibility. Importantly, services do not communicate directly with each other - the frontend is responsible for coordinating data between services.
- Purpose: Real-time environmental monitoring service
- Key Features:
- Collects sensor data (temperature, pH, oxygen, salinity)
- Manages sensor connections and status
- Provides historical readings and alerts
- Tech Stack: Rust, Axum, SQLx, PostgreSQL
- Challenges: Async I/O optimization, resource management
- Purpose: Species information and feeding management
- Key Features:
- Maintains species database with environmental requirements
- Manages feeding schedules and nutritional data
- Provides species compatibility information
- Tech Stack: Rust, Axum, SQLx, PostgreSQL
- Challenges: Database query optimization
- Purpose: Analysis and system coordination
- Key Features:
- Analyzes tank conditions and species health
- Detects patterns and anomalies
- Coordinates system-wide responses
- Tech Stack: Rust, Axum, reqwest
- Challenges: Memory optimization, concurrency management
Each challenge includes validation functionality that checks if your solution correctly implements the required optimization. Always use the frontend UI and the "Validate your solution" button for both local and cloud validation. The UI provides immediate feedback and error details.
The validation process performs syntactic checks of your implementation. It verifies that your code genuinely implements the required solution while respecting the architectural constraints.
By completing these challenges, you'll learn:
- Asynchronous I/O: How to properly use async/await for non-blocking file operations
- Database Query Optimization: Techniques for writing efficient database queries and using appropriate indexing
- Memory Management: Best practices for reducing allocations and using static references in Rust
- Resource Management: How to properly manage and reuse expensive resources like HTTP clients
The ShellCon Smart Aquarium System is designed to provide a hands-on learning experience with real-world optimization challenges. By solving these challenges, you'll gain valuable experience with Rust backend development and Shuttle deployment.
Remember these key principles:
- Keep It Simple: Focus on straightforward, effective solutions
- Verify Your Work: Always test your solutions with the validation endpoints
- Check Logs: Use
shuttle logs
to troubleshoot issues - Format and Check: Run
cargo fmt
andcargo check
before deploying
Good luck, Rustacean! The crustaceans of ShellCon are counting on you! π¦
Q: I get an error that a port is already in use when running a service locally. What do I do?
A: Free the port with:
lsof -ti :<port> | xargs kill -9
Q: Shuttle asks me to log in when I deploy, interrupting my workflow.
A: Always run shuttle login
right after installing the CLI, before starting any work.
Q: My service isn't picking up environment variables.
A: Make sure you have set the variables in your shell before running shuttle run
or shuttle deploy
, or use a .env
file in the service directory.
Q: How do I test endpoints locally?
A: Use Thunder Client (VS Code), curl, or Postman. Example:
curl http://localhost:8000/api/health
Q: How do I find my cloud endpoint after deploying?
A: See the "Deploy to Shuttle Cloud" section above for instructions on updating your frontend and validating against cloud endpoints.
Q: The validation endpoint says my solution is incorrect, but it works locally!
A: Double-check that you followed the full validation workflow in the main instructions above:
- Validated locally using the frontend UI and
shuttle run
- Deployed the latest code to Shuttle Cloud
- Updated your frontend to point to the correct cloud endpoints
- Checked Shuttle logs for errors (
shuttle logs --latest
)
Q: I get a database connection error locally or an error about Docker socket.
A: Make sure Docker is running on your machine. Shuttle uses Docker to provision PostgreSQL databases locally. If you see errors like Socket not found: /var/run/docker.sock
, try these steps:
- Start Docker Desktop or your Docker daemon
- In Docker Desktop settings, enable "Allow the default Docker socket to be used" option (requires password)
- Ensure your user has permissions to access the Docker socket
- Restart the terminal where you're running Shuttle commands
- If using VS Code devcontainer, ensure Docker-in-Docker is properly configured
Q: How do I restart a service after code changes?
A: Stop the running process (Ctrl+C) and re-run shuttle run --port <port>
in the relevant directory.
Q: Services aren't talking to each other.
A: This is by design! Each service is independent. Only the frontend (when implemented) will coordinate between them.
If you have a question not answered here, check the Shuttle documentation or open an issue in this repository.