Compare commits

..

No commits in common. "master" and "prod" have entirely different histories.
master ... prod

10 changed files with 34 additions and 88 deletions

View File

@ -6,21 +6,6 @@ services:
image: mathwave/sprint-repo:locks image: mathwave/sprint-repo:locks
networks: networks:
- locks-development - 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: environment:
MONGO_HOST: "mongo.develop.sprinthub.ru" MONGO_HOST: "mongo.develop.sprinthub.ru"
MONGO_PASSWORD: $MONGO_PASSWORD_DEV MONGO_PASSWORD: $MONGO_PASSWORD_DEV

View File

@ -6,21 +6,6 @@ services:
image: mathwave/sprint-repo:locks image: mathwave/sprint-repo:locks
networks: networks:
- locks - 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: environment:
MONGO_HOST: "mongo.sprinthub.ru" MONGO_HOST: "mongo.sprinthub.ru"
MONGO_PASSWORD: $MONGO_PASSWORD_PROD MONGO_PASSWORD: $MONGO_PASSWORD_PROD

View File

@ -1,20 +0,0 @@
import fastapi
import uvicorn
from app.routers import acquire
from app.routers import release
from app.storage import mongo
from app.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)

View File

@ -1,3 +0,0 @@
class Daemon:
def execute(self):
raise NotImplementedError

View File

@ -1,11 +0,0 @@
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)

View File

@ -1,7 +1,9 @@
import datetime
import fastapi import fastapi
import pydantic import pydantic
from app.storage.mongo import locks from app.storage.mongo import locks
from app.utils import time
class RequestBody(pydantic.BaseModel): class RequestBody(pydantic.BaseModel):
@ -15,6 +17,7 @@ router = fastapi.APIRouter()
@router.post('/api/v1/acquire', status_code=fastapi.status.HTTP_202_ACCEPTED, responses={'409': {'description': 'Conflict'}}) @router.post('/api/v1/acquire', status_code=fastapi.status.HTTP_202_ACCEPTED, responses={'409': {'description': 'Conflict'}})
async def execute(body: RequestBody): async def execute(body: RequestBody):
try: try:
await locks.acquire(body.name, ttl=body.ttl) await locks.acquire(locks.Lock(name=body.name, locked_until=time.now() + datetime.timedelta(seconds=body.ttl)))
except locks.ConflictException: except Exception as e:
print(e)
raise fastapi.HTTPException(409) raise fastapi.HTTPException(409)

View File

@ -1,11 +1,14 @@
import datetime
import fastapi import fastapi
import pydantic import pydantic
from app.storage.mongo import locks from app.storage.mongo import locks
from app.utils import time
class RequestBody(pydantic.BaseModel): class RequestBody(pydantic.BaseModel):
name: str name: str
ttl: int
router = fastapi.APIRouter() router = fastapi.APIRouter()
@ -13,4 +16,4 @@ router = fastapi.APIRouter()
@router.post('/api/v1/release', status_code=fastapi.status.HTTP_202_ACCEPTED) @router.post('/api/v1/release', status_code=fastapi.status.HTTP_202_ACCEPTED)
async def execute(body: RequestBody): async def execute(body: RequestBody):
await locks.release(body.name) await locks.release(locks.Lock(name=body.name, locked_until=time.now() + datetime.timedelta(seconds=body.ttl)))

View File

@ -1,25 +1,22 @@
import bson
import datetime import datetime
import pydantic
from app.storage.mongo import database from app.storage.mongo import database
from app.utils import time from app.utils import time
from bson import codec_options 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)) collection = database.get_collection("locks", codec_options=codec_options.CodecOptions(tz_aware=True))
async def acquire(name: str, ttl: int): class Lock(pydantic.BaseModel):
locked_until = time.now() + datetime.timedelta(seconds=ttl) name: str
try: locked_until: pydantic.AwareDatetime
await collection.insert_one({'name': name, 'locked_until': locked_until})
except errors.DuplicateKeyError:
raise ConflictException async def acquire(lock: Lock):
await collection.insert_one(lock.model_dump())
async def release(name: str): async def release(name: str):

25
main.py
View File

@ -1,12 +1,19 @@
import sys import fastapi
import uvicorn
from app.routers import acquire
from app.routers import release
from app.storage import mongo
arg = sys.argv[-1] app = fastapi.FastAPI()
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')
Daemon().execute() 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)