Awesome
Intro
This application is made out of two services, recommender and ad_provider service. Recommender has two circuit breakers: one for sending the request to get the ads from the ad provider (placed in ad_controller.py), and one for the database, in this case PostgreSQL (placed in crud.py).
Docker setup
In order to run this project:
$ git clone https://github.com/gjorgjinac/recommender_server.git
$ cd recommender_server
$ docker-compose up
This will run the two, beforementioned, services. Verification of the deployment can be done by navigating to the following server addresses in your preferred browser:
0.0.0.0:8000 #for the recommended service
0.0.0.0:8001 #for the ad_provider service
Configuration
The configuration on the PyBreaker for the /ads
breaker which protects calls to 0.0.0.0:8000/ads
.
ad_circuit_breaker = pybreaker.CircuitBreaker(
fail_max=1, listeners=[DefaultListener()],
reset_timeout=10,
)
where the DefaultListener()
can be found in recommender/recommender-app/default_ad_listener.py
.
On the other side, the configuration on the PyBreaker which protects calls to the database.
db_circuit_breaker = pybreaker.CircuitBreaker(
fail_max=1,
reset_timeout=20,
)
Test cases for the circuit breakers
- Circuit breaker which protects the calls from the recommender service to ad_provider service: execute a GET request to
http://0.0.0.0:8000/ads
.
@ad_circuit_breaker
def send_post_for_adds():
print('requesting')
response = requests.post("http://127.0.0.1:8001/ads")
return json.loads(response.text)
@ad_circuit_breaker
def sabotage_ad():
raise Exception
@ad_router.get("/ads", response_model=List[schemas.Product])
async def get_ads():
return call_protected(ad_circuit_breaker, default_ads, send_post_for_adds)
2. Circuit breaker meant to protect the calls from the recommender service to the operational database with content type application/json and with body
db_circuit_breaker = pybreaker.CircuitBreaker(
fail_max=1,
reset_timeout=20,
)
@db_circuit_breaker
def get_product_by_asin(db: Session, asin: str):
return db.query(models.Product).filter(models.Product.asin == asin).first()
@db_circuit_breaker
def get_products(db: Session, skip: int = 0, limit: int = None) -> List[models.Product]:
return db.query(models.Product).offset(skip).limit(limit).all()
@db_circuit_breaker
def create_product(db: Session, new_product:any):
db_product = models.Product(**new_product.to_dict())
db.add(db_product)
db.commit()
db.refresh(db_product)
return db_product
@db_circuit_breaker
def get_reviews_by_asin(db: Session, asin: str) -> List[models.Review]:
return db.query(models.Review).filter(models.Review.asin == asin).all()
@db_circuit_breaker
def get_reviews(db: Session, skip: int = 0, limit: int = None) -> List[models.Review]:
return db.query(models.Review).offset(skip).limit(limit).all()
@db_circuit_breaker
def create_review(db: Session, new_review: schemas.ReviewBase) -> models.Review:
product = get_product_by_asin(db, new_review.asin)
db_review = models.Review(**new_review.to_dict(), product_id = product.id)
db.add(db_review)
db.commit()
db.refresh(db_review)
return db_review
@db_circuit_breaker
def fail():
raise Exception