Skip to content

Commit

Permalink
add server side caching
Browse files Browse the repository at this point in the history
  • Loading branch information
teticio committed Oct 12, 2021
1 parent 6114bc7 commit 16ee9d4
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ jobs:
run: |
yarn build
python download.py
APP_URL=http://localhost:3000 uvicorn backend.main:app --port=8001 &
APP_URL=http://localhost:3000 NO_CACHE=1 uvicorn backend.main:app --port=8001 &
pytest backend
yarn test
2 changes: 1 addition & 1 deletion .travis_old.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ install:
script:
- yarn build
- python download.py
- APP_URL=http://localhost:3000 uvicorn backend.main:app --port=8001 &
- APP_URL=http://localhost:3000 NO_CACHE=1 uvicorn backend.main:app --port=8001 &
- pytest backend
- yarn test
6 changes: 5 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,19 @@
"request": "launch",
"env": {
"APP_URL": "http://localhost:3000",
"REDIS_NAMESPACE": "deejai-dev", // If we do cache, use a separate namespace
"NO_CACHE": "1", // Disable caching
"DOCS_URL": "/docs",
"REDOC_URL": "/redoc"
},
"osx": {
"env": {
"APP_URL": "http://localhost:3000",
"REDIS_NAMESPACE": "deejai-dev", // If we do cache, use a separate namespace
"NO_CACHE": "1", // Disable caching
"DOCS_URL": "/docs",
"REDOC_URL": "/redoc",
"HACKINTOSH": "1"
"HACKINTOSH": "1" // I don't have a real Macintosh
}
},
"module": "uvicorn",
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ librosa = "*"
pymysql = "*"
sqlalchemy-utils = "*"
beautifulsoup4 = "*"
fastapi-cache2 = {extras = ["redis"], version = "*"}

[dev-packages]
yapf = "*"
Expand Down
319 changes: 197 additions & 122 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ You will need to have already created your `credentials.py`, as explained above.
```
./install_helm.sh
```
The scripts assume you are running a `minikube`. To install on an AWS cluster with Kops
The scripts assume you are running a `minikube`. This will install the backend FastAPI server, an SQL database to store the playlists and a Redis instance to cache server side static requests. To install on an AWS cluster with Kops
```
./deploy_kops.sh <Your external webpage domain>
```
Expand Down
39 changes: 37 additions & 2 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@
from fastapi.middleware.cors import CORSMiddleware
from fastapi.exception_handlers import http_exception_handler

from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
from fastapi_cache.coder import JsonCoder

from sqlalchemy import desc, event
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError, SQLAlchemyError

import aiohttp
import aioredis

from bs4 import BeautifulSoup

Expand All @@ -35,6 +41,16 @@

credentials.REDIRECT_URL = os.environ.get('SPOTIFY_REDIRECT_URI',
credentials.REDIRECT_URL)
REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost')
REDIS_NAMESPACE = os.environ.get('REDIS_NAMESPACE', 'deejai')
if 'NO_CACHE' in os.environ:

def cache(**kwargs): # noqa: F811
def do_nothing(f):
return f

return do_nothing


# Create tables if necessary
models.Base.metadata.create_all(bind=engine)
Expand Down Expand Up @@ -72,6 +88,23 @@ def receive_before_insert(mapper, connection, target): # pylint: disable=unused
target.hash = models.Playlist.hash_it(target)


@app.on_event('startup')
async def startup():
if 'NO_CACHE' not in os.environ:
redis = aioredis.from_url(REDIS_URL,
encoding='utf8',
decode_responses=True)
FastAPICache.init(RedisBackend(redis), prefix='fastapi-cache')


class HTTPCoder(JsonCoder):
@classmethod
def decode(cls, value):
response = JsonCoder.decode(value)
return HTMLResponse(content=response['body'],
status_code=response['status_code'])


origins = [
'http://deej-ai.online',
'https://deej-ai.online',
Expand Down Expand Up @@ -233,7 +266,7 @@ async def get_access_token(request: Request):
text = await response.text()
except aiohttp.ClientError as error:
raise HTTPException(status_code=400, detail=str(error)) from error
return text
return HTMLResponse(content=text, status_code=200)


async def get_track_widget(track_id):
Expand Down Expand Up @@ -270,6 +303,7 @@ async def get_track_widget(track_id):


@app.get('/api/v1/widget')
@cache(namespace=REDIS_NAMESPACE, expire=24 * 60 * 60)
async def widget(track_id: str):
"""Get Spotify track widget.
(Deprecated.)
Expand All @@ -285,6 +319,7 @@ async def widget(track_id: str):


@app.get('/api/v1/track_widget')
@cache(namespace=REDIS_NAMESPACE, expire=24 * 60 * 60, coder=HTTPCoder)
async def track_widget(track_id: str):
"""Get Spotify track widget.
Expand All @@ -299,6 +334,7 @@ async def track_widget(track_id: str):


@app.get('/api/v1/playlist_widget')
@cache(namespace=REDIS_NAMESPACE, expire=24 * 60 * 60, coder=HTTPCoder)
async def make_playlist_widget(track_ids, waypoints='[]', playlist_id=''):
"""Make a new Spotify playlist widget.
Expand All @@ -310,7 +346,6 @@ async def make_playlist_widget(track_ids, waypoints='[]', playlist_id=''):
Returns:
str: HTML which can be embedded in an iframe.
"""

track_ids = json.loads(track_ids)
waypoints = json.loads(waypoints)
assert len(track_ids) > 0
Expand Down
2 changes: 2 additions & 0 deletions helm-chart/deejai/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ spec:
key: mysql-root-password
- name: SQLALCHEMY_DATABASE_URL
value: mysql+pymysql://root:$(MYSQL_ROOT_PASSWORD)@mysql:3306/deejai
- name: REDIS_URL
value: redis://redis
- name: APP_URL
value: {{ .Values.app.url }}
- name: REACT_APP_API_URL
Expand Down
63 changes: 63 additions & 0 deletions helm-chart/deejai/templates/redis-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
ports:
- port: 6379
selector:
app: redis
clusterIP: None
---
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
data:
redis-config: |
maxmemory 2mb
maxmemory-policy allkeys-lru
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
selector:
matchLabels:
app: redis
strategy:
type: Recreate
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5.0.4
command:
- redis-server
- "/redis-master/redis.conf"
env:
- name: MASTER
value: "true"
ports:
- containerPort: 6379
resources:
limits:
cpu: "0.1"
volumeMounts:
- mountPath: /redis-master-data
name: data
- mountPath: /redis-master
name: config
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: redis-config
items:
- key: redis-config
path: redis.conf
27 changes: 16 additions & 11 deletions requirements-lock.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
absl-py==0.14.1
aiofiles==0.7.0
aiohttp==3.7.4.post0
aioredis==2.0.0
anyio==3.3.3
appdirs==1.4.4
asgiref==3.4.1
astunparse==1.6.3
Expand All @@ -9,14 +11,15 @@ attrs==21.2.0
audioread==2.1.9
beautifulsoup4==4.10.0
cachetools==4.2.4
certifi==2021.5.30
certifi==2021.10.8
cffi==1.14.6
chardet==4.0.0
charset-normalizer==2.0.6
charset-normalizer==2.0.7
clang==5.0
click==8.0.1
click==8.0.3
decorator==5.1.0
fastapi==0.68.1
fastapi==0.70.0
fastapi-cache2==0.1.6
flatbuffers==1.12
gast==0.4.0
google-auth==1.35.0
Expand All @@ -27,38 +30,40 @@ grpcio==1.41.0
h11==0.12.0
h5py==3.1.0
idna==3.2
joblib==1.0.1
joblib==1.1.0
keras==2.6.0
Keras-Preprocessing==1.1.2
librosa==0.8.1
llvmlite==0.37.0
Markdown==3.3.4
multidict==5.2.0
numba==0.54.0
numba==0.54.1
numpy==1.19.5
oauthlib==3.1.1
opt-einsum==3.3.0
packaging==21.0
pooch==1.5.1
protobuf==3.18.0
pooch==1.5.2
protobuf==3.18.1
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.20
pydantic==1.8.2
PyMySQL==1.0.2
pyparsing==2.4.7
python-dateutil==2.8.2
requests==2.26.0
requests-oauthlib==1.3.0
resampy==0.2.2
rsa==4.7.2
scikit-learn==1.0
scipy==1.7.1
six==1.15.0
sniffio==1.2.0
SoundFile==0.10.3.post1
soupsieve==2.2.1
SQLAlchemy==1.4.25
SQLAlchemy-Utils==0.37.8
starlette==0.14.2
starlette==0.16.0
tensorboard==2.6.0
tensorboard-data-server==0.6.1
tensorboard-plugin-wit==1.8.0
Expand All @@ -69,6 +74,6 @@ threadpoolctl==3.0.0
typing-extensions==3.7.4.3
urllib3==1.26.7
uvicorn==0.15.0
Werkzeug==2.0.1
Werkzeug==2.0.2
wrapt==1.12.1
yarl==1.6.3
yarl==1.7.0
3 changes: 2 additions & 1 deletion run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pipenv run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics &&
pipenv run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics && \
export SQLALCHEMY_DATABASE_URL="sqlite:///:memory:" && \
export CUDA_VISIBLE_DEVICES="" && \
export NO_CACHE='1' && \
pipenv run "pytest backend" &&\
export CI=true && \
yarn test
yarn test

0 comments on commit 16ee9d4

Please sign in to comment.