dev: exception handling

This commit is contained in:
pablohashescobar 2024-01-22 10:11:26 +00:00
parent dd95dd9f5e
commit e6c8d513ed
3 changed files with 39 additions and 30 deletions

0
apiserver/bin/beat Normal file → Executable file
View File

View File

@ -1,5 +1,7 @@
# Python imports # Python imports
import time
import uuid import uuid
import atexit
# Django imports # Django imports
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
@ -7,42 +9,47 @@ from django.core.management import call_command
from django.db.migrations.executor import MigrationExecutor from django.db.migrations.executor import MigrationExecutor
from django.db import connections, DEFAULT_DB_ALIAS from django.db import connections, DEFAULT_DB_ALIAS
# Module imports
from plane.settings.redis import redis_instance
class Command(BaseCommand): class Command(BaseCommand):
help = 'Run migrations with Redis distributed locking if there are pending migrations' help = 'Wait for migrations to be completed and acquire lock before starting migrations'
def handle(self, *args, **kwargs): def handle(self, *args, **kwargs):
# Check for pending migrations self.lock_key = 'django_migration_lock'
if not self._pending_migrations(): self.lock_value = str(uuid.uuid4()) # Unique value for the lock
self.stdout.write("No pending migrations.") self.client = redis_instance()
return
# Proceed with acquiring the lock and running migrations # Register the cleanup function
lock_key = 'django_migration_lock' atexit.register(self.cleanup)
lock_value = str(uuid.uuid4()) # Unique value for the lock
client = redis_instance()
while self._pending_migrations():
# Try acquiring the lock # Try acquiring the lock
if client.set(lock_key, lock_value, nx=True, ex=300): # 5 minutes expiry if self.client.set(self.lock_key, self.lock_value, nx=True, ex=300): # 5 minutes expiry
try: try:
self.stdout.write("Acquired migration lock, running migrations...") self.stdout.write("Acquired migration lock, running migrations...")
call_command('migrate') call_command('migrate')
except Exception as e:
self.stdout.write(f"An error occurred during migrations: {e}")
finally: finally:
# Release the lock if it belongs to this process # Release the lock if it belongs to this process
stored_value = client.get(lock_key) self.cleanup()
if stored_value and stored_value.decode() == lock_value: return # Exit after attempting migration
client.delete(lock_key)
self.stdout.write("Released migration lock.")
else: else:
self.stdout.write("Lock was not released, as it was held by another instance.") self.stdout.write("Migration lock is held by another instance. Waiting 10 seconds to retry...")
else: time.sleep(10) # Wait for 10 seconds before retrying
self.stdout.write("Migration lock is held by another instance.")
self.stdout.write("No pending migrations.")
def _pending_migrations(self): def _pending_migrations(self):
connection = connections[DEFAULT_DB_ALIAS] connection = connections[DEFAULT_DB_ALIAS]
executor = MigrationExecutor(connection) executor = MigrationExecutor(connection)
targets = executor.loader.graph.leaf_nodes() targets = executor.loader.graph.leaf_nodes()
return bool(executor.migration_plan(targets)) return bool(executor.migration_plan(targets))
def cleanup(self):
"""
Clean up function to release the lock.
"""
stored_value = self.client.get(self.lock_key)
if stored_value and stored_value.decode() == self.lock_value:
self.client.delete(self.lock_key)
self.stdout.write("Released migration lock.")

View File

@ -76,6 +76,8 @@ services:
- web - web
api: api:
deploy:
replicas: 3
build: build:
context: ./apiserver context: ./apiserver
dockerfile: Dockerfile.dev dockerfile: Dockerfile.dev
@ -86,7 +88,7 @@ services:
- dev_env - dev_env
volumes: volumes:
- ./apiserver:/code - ./apiserver:/code
# command: /bin/sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000 --settings=plane.settings.local" command: ./bin/takeoff.local
env_file: env_file:
- ./apiserver/.env - ./apiserver/.env
depends_on: depends_on:
@ -104,7 +106,7 @@ services:
- dev_env - dev_env
volumes: volumes:
- ./apiserver:/code - ./apiserver:/code
command: /bin/sh -c "celery -A plane worker -l info" command: ./bin/worker
env_file: env_file:
- ./apiserver/.env - ./apiserver/.env
depends_on: depends_on:
@ -123,7 +125,7 @@ services:
- dev_env - dev_env
volumes: volumes:
- ./apiserver:/code - ./apiserver:/code
command: /bin/sh -c "celery -A plane beat -l info" command: ./bin/beat
env_file: env_file:
- ./apiserver/.env - ./apiserver/.env
depends_on: depends_on: