This project implements a distributed online bookstore using a microservices architecture.
The system was first implemented in Lab 1 using three services:
- Frontend Service
- Catalog Service
- Order Service
In Lab 2, the system was extended to support:
- Replication
- Caching
- Cache consistency
- Round-robin load balancing
- Performance measurements
- Docker-based deployment
All services are implemented using Python Flask and communicate using REST APIs and JSON.
The Lab 2 system runs five services:
frontendcatalog1catalog2order1order2
The frontend is not replicated. It receives all client requests and forwards them to backend replicas.
Client
|
v
Frontend
|-- In-memory cache
|-- Round-robin load balancer
|
|---- Catalog1
|---- Catalog2
|
|---- Order1
|---- Order2
For Lab 2, replicated data files are stored in the data/ directory:
data/catalog1.csv
data/catalog2.csv
data/orders1.csv
data/orders2.csv
The original Lab 1 files are kept for backward compatibility:
catalog/catalog.csv
order/orders.csv
Lab 2 uses the files inside data/.
| Service | Description | Host Port |
|---|---|---|
| frontend | Client-facing service, cache, load balancer | 5000 |
| catalog1 | First catalog replica | 5001 |
| catalog2 | Second catalog replica | 5003 |
| order1 | First order replica | 5002 |
| order2 | Second order replica | 5004 |
The frontend:
- Receives all client requests.
- Performs round-robin load balancing.
- Caches item info responses.
- Invalidates cached items when backend data changes.
Frontend endpoints:
GET /
GET /health
GET /search/<topic>
GET /info/<id>
POST /purchase/<id>
POST /invalidate/<id>
POST /cache/clear
GET /cache/state
The catalog service:
- Stores book data in CSV files.
- Supports query by topic.
- Supports query by item.
- Updates stock.
- Synchronizes updates with the other catalog replica.
- Sends cache invalidation requests before writes.
Catalog endpoints:
GET /health
GET /query/topic/<topic>
GET /query/item/<id>
PUT /update/<id>
PUT /sync/update/<id>
The order service:
- Handles purchase requests.
- Checks stock from catalog replicas.
- Updates catalog stock.
- Stores orders in CSV files.
- Synchronizes order logs with the other order replica.
Order endpoints:
GET /health
GET /orders
POST /purchase/<id>
POST /sync/order
Make sure Docker Desktop is running.
From the project root directory, run:
docker compose up --buildTo stop the system:
docker compose downcurl.exe http://localhost:5000/health
curl.exe http://localhost:5001/health
curl.exe http://localhost:5003/health
curl.exe http://localhost:5002/health
curl.exe http://localhost:5004/healthExpected result: all services should return "status": "ok".
curl.exe http://localhost:5000/search/distributed%20systemsExample output:
[
{
"id": "1",
"title": "How to get a good grade in DOS in 40 minutes a day"
},
{
"id": "2",
"title": "RPCs for Noobs"
},
{
"id": "5",
"title": "How to finish Project 3 on time"
}
]First request:
curl.exe http://localhost:5000/info/2Example output:
{
"cache": "MISS",
"id": "2",
"price": 50,
"quantity": 10,
"served_by": "http://catalog1:5001",
"title": "RPCs for Noobs"
}Second request for the same item:
curl.exe http://localhost:5000/info/2Example output:
{
"cache": "HIT",
"id": "2",
"price": 50,
"quantity": 10,
"served_by": "http://catalog1:5001",
"title": "RPCs for Noobs"
}curl.exe -X POST http://localhost:5000/purchase/2Example output:
{
"book_id": "2",
"catalog_used": "http://catalog1:5001",
"forwarded_to": "http://order1:5002",
"message": "bought book RPCs for Noobs",
"order_id": 1,
"processed_by": "order1",
"response_time_ms": 52.99,
"title": "RPCs for Noobs"
}After purchase, the cached item is invalidated.
curl.exe http://localhost:5000/info/2Example output:
{
"cache": "MISS",
"id": "2",
"price": 50,
"quantity": 9,
"served_by": "http://catalog1:5001",
"title": "RPCs for Noobs"
}This shows that the cache was invalidated and the updated quantity was fetched from the catalog.
curl.exe http://localhost:5001/query/item/2
curl.exe http://localhost:5003/query/item/2Both catalog replicas should return the same quantity.
curl.exe http://localhost:5002/orders
curl.exe http://localhost:5004/ordersBoth order replicas should show the same orders.
The performance script is located at:
experiments/performance_test.py
Run it using:
python experiments/performance_test.pyIt generates:
docs/performance_results.csv
docs/performance_summary.md
docs/response_time_plot.png
docs/cache_invalidation_plot.png
Measured results:
| Operation | Mode | Average Response Time |
|---|---|---|
| GET /info/2 | Without cache | 15.72 ms |
| GET /info/2 | With cache | 4.27 ms |
| POST /purchase/7 | Buy request | 67.33 ms |
Caching improvement for repeated info queries:
72.84%
Important documentation and output files are located in docs/:
docs/design_document_lab2.md
docs/output.txt
docs/performance_summary.md
docs/performance_results.csv
docs/response_time_plot.png
docs/cache_invalidation_plot.png
- Each service runs in a separate Docker container.
- All communication is done through REST APIs.
- The frontend is not replicated.
- The frontend contains the in-memory cache and round-robin load balancer.
- Catalog and order services are replicated.
- Cache invalidation is performed before catalog writes.
- CSV files are used for persistence because this is a lab-scale project.