From 31d98b67cfc0de68add808063a45cd48341835d9 Mon Sep 17 00:00:00 2001 From: emmatveev Date: Mon, 2 Dec 2024 21:41:37 +0300 Subject: [PATCH] fix --- .deploy/deploy-dev.yaml | 15 +++++++++++++++ .deploy/deploy-prod.yaml | 15 +++++++++++++++ app/daemons/__init__.py | 0 app/daemons/api.py | 20 ++++++++++++++++++++ app/daemons/base.py | 3 +++ app/daemons/cleaner.py | 11 +++++++++++ app/routers/acquire.py | 7 ++----- app/routers/release.py | 2 +- app/storage/mongo/__init__.py | 6 ++++++ app/storage/mongo/locks.py | 21 ++++++++++++--------- main.py | 25 +++++++++---------------- 11 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 app/daemons/__init__.py create mode 100644 app/daemons/api.py create mode 100644 app/daemons/base.py create mode 100644 app/daemons/cleaner.py diff --git a/.deploy/deploy-dev.yaml b/.deploy/deploy-dev.yaml index 385b442..b54bdba 100644 --- a/.deploy/deploy-dev.yaml +++ b/.deploy/deploy-dev.yaml @@ -6,6 +6,21 @@ services: image: mathwave/sprint-repo:locks networks: - locks-development + command: api + environment: + MONGO_HOST: "mongo.develop.sprinthub.ru" + MONGO_PASSWORD: $MONGO_PASSWORD_DEV + deploy: + mode: replicated + restart_policy: + condition: any + update_config: + parallelism: 1 + order: start-first + + cleaner: + image: mathwave/sprint-repo:locks + command: cleaner environment: MONGO_HOST: "mongo.develop.sprinthub.ru" MONGO_PASSWORD: $MONGO_PASSWORD_DEV diff --git a/.deploy/deploy-prod.yaml b/.deploy/deploy-prod.yaml index 0aab243..c47b1b9 100644 --- a/.deploy/deploy-prod.yaml +++ b/.deploy/deploy-prod.yaml @@ -6,6 +6,21 @@ services: image: mathwave/sprint-repo:locks networks: - locks + command: api + environment: + MONGO_HOST: "mongo.sprinthub.ru" + MONGO_PASSWORD: $MONGO_PASSWORD_PROD + deploy: + mode: replicated + restart_policy: + condition: any + update_config: + parallelism: 1 + order: start-first + + cleaner: + image: mathwave/sprint-repo:locks + command: cleaner environment: MONGO_HOST: "mongo.sprinthub.ru" MONGO_PASSWORD: $MONGO_PASSWORD_PROD diff --git a/app/daemons/__init__.py b/app/daemons/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/daemons/api.py b/app/daemons/api.py new file mode 100644 index 0000000..9b3fc28 --- /dev/null +++ b/app/daemons/api.py @@ -0,0 +1,20 @@ +import fastapi +import uvicorn + +from app.routers import acquire +from app.routers import release + +from app.storage import mongo +from daemons import base + + +class Daemon(base.Daemon): + def execute(self): + app = fastapi.FastAPI() + + app.include_router(acquire.router) + app.include_router(release.router) + + mongo.create_indexes() + + uvicorn.run(app, host="0.0.0.0", port=80) diff --git a/app/daemons/base.py b/app/daemons/base.py new file mode 100644 index 0000000..aca86ad --- /dev/null +++ b/app/daemons/base.py @@ -0,0 +1,3 @@ +class Daemon: + def execute(self): + raise NotImplementedError diff --git a/app/daemons/cleaner.py b/app/daemons/cleaner.py new file mode 100644 index 0000000..f512f9a --- /dev/null +++ b/app/daemons/cleaner.py @@ -0,0 +1,11 @@ +from app.daemons import base +from app.storage.mongo import locks +import time +from app.utils import time as time_utils + + +class Daemon(base.Daemon): + def execute(self): + while True: + locks.collection.delete_many({'locked_until': {'$lt': time_utils.now()}}) + time.sleep(5) diff --git a/app/routers/acquire.py b/app/routers/acquire.py index e59c843..cc6d69b 100644 --- a/app/routers/acquire.py +++ b/app/routers/acquire.py @@ -1,9 +1,7 @@ -import datetime import fastapi import pydantic from app.storage.mongo import locks -from app.utils import time class RequestBody(pydantic.BaseModel): @@ -17,7 +15,6 @@ router = fastapi.APIRouter() @router.post('/api/v1/acquire', status_code=fastapi.status.HTTP_202_ACCEPTED, responses={'409': {'description': 'Conflict'}}) async def execute(body: RequestBody): try: - await locks.acquire(locks.Lock(name=body.name, locked_until=time.now() + datetime.timedelta(seconds=body.ttl))) - except Exception as e: - print(e) + await locks.acquire(body.name, ttl=body.ttl) + except locks.ConflictException: raise fastapi.HTTPException(409) diff --git a/app/routers/release.py b/app/routers/release.py index 68947ce..2b7b806 100644 --- a/app/routers/release.py +++ b/app/routers/release.py @@ -16,4 +16,4 @@ router = fastapi.APIRouter() @router.post('/api/v1/release', status_code=fastapi.status.HTTP_202_ACCEPTED) async def execute(body: RequestBody): - await locks.release(locks.Lock(name=body.name, locked_until=time.now() + datetime.timedelta(seconds=body.ttl))) + await locks.release(body.name) diff --git a/app/storage/mongo/__init__.py b/app/storage/mongo/__init__.py index 31348dc..b73de30 100644 --- a/app/storage/mongo/__init__.py +++ b/app/storage/mongo/__init__.py @@ -18,3 +18,9 @@ def create_indexes(): database.get_collection('locks').create_index([ ('name', 1), ], unique=True) + + +def remove_outdated(): + client = pymongo.MongoClient(CONNECTION_STRING) + while True: + \ No newline at end of file diff --git a/app/storage/mongo/locks.py b/app/storage/mongo/locks.py index 1e055ad..ee7ca66 100644 --- a/app/storage/mongo/locks.py +++ b/app/storage/mongo/locks.py @@ -1,22 +1,25 @@ -import bson import datetime -import pydantic from app.storage.mongo import database from app.utils import time from bson import codec_options +from pymongo import errors + + +class ConflictException(Exception): + pass + collection = database.get_collection("locks", codec_options=codec_options.CodecOptions(tz_aware=True)) -class Lock(pydantic.BaseModel): - name: str - locked_until: pydantic.AwareDatetime - - -async def acquire(lock: Lock): - await collection.insert_one(lock.model_dump()) +async def acquire(name: str, ttl: int): + locked_until = time.now() + datetime.timedelta(seconds=ttl) + try: + await collection.insert_one({'name': name, 'locked_until': locked_until}) + except errors.DuplicateKeyError: + raise ConflictException async def release(name: str): diff --git a/main.py b/main.py index 411a403..4b698d2 100644 --- a/main.py +++ b/main.py @@ -1,19 +1,12 @@ -import fastapi -import uvicorn - -from app.routers import acquire -from app.routers import release - -from app.storage import mongo +import sys -app = fastapi.FastAPI() +arg = sys.argv[-1] +if arg == 'api': + from app.daemons.api import Daemon +elif arg == 'cleaner': + from app.daemons.cleaner import Daemon +else: + raise NotImplementedError('Daemon is not implemented') -app.include_router(acquire.router) -app.include_router(release.router) - -mongo.create_indexes() - - -if __name__ == '__main__': - uvicorn.run(app, host="0.0.0.0", port=80) +Daemon().execute()