pythonpytorchcelery

Celery worker exited prematurely signal 11: trying to run a python script on button click from Django view


I am working on a Django app whose partial process is transcribing audio with timestamps. When a user clicks on a button from a web interface, the Django server launches a Python script that helps with transcribing.

Now, here are a few approaches I have tried already: I have a separate transcribe.py file. When a user clicks the transcribe button from the web page, it accesses a view from the project app. However, after partially running the script, the Django server terminates from the terminal.

Since the Python script is a long-running process, I figured I should run the program in the background so the Django server doesn't terminate. So, I implemented Celery and Redis. First, the transcribe.py script runs perfectly well when I run it from the Django shell. However, it terminates once again when I try to execute it from the view/web page.

python manage.py shell

Since I implemented the celery worker part, the server doesn't terminate but the worker throws the following error.

[tasks]
  . transcribeApp.tasks.run_transcription

[2024-11-25 03:26:04,500: INFO/MainProcess] Connected to redis://localhost:6379/0
[2024-11-25 03:26:04,514: INFO/MainProcess] mingle: searching for neighbors
[2024-11-25 03:26:05,520: INFO/MainProcess] mingle: all alone
[2024-11-25 03:26:05,544: INFO/MainProcess] celery@user.local ready.
[2024-11-25 03:26:16,253: INFO/MainProcess] Task searchApp.tasks.run_transcription[c684bdfa-ec21-4b4e-9542-0ca1f7729682] received
[2024-11-25 03:26:16,255: INFO/ForkPoolWorker-15] Starting transcription process.
[2024-11-25 03:26:16,509: WARNING/ForkPoolWorker-15] /Users/user/Desktop/project/django_app/django_venv/lib/python3.12/site-packages/whisper/__init__.py:150: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.
  checkpoint = torch.load(fp, map_location=device)

[2024-11-25 03:26:16,670: ERROR/MainProcess] Process 'ForkPoolWorker-15' pid:38956 exited with 'signal 11 (SIGSEGV)'
[2024-11-25 03:26:16,683: ERROR/MainProcess] Task handler raised error: WorkerLostError('Worker exited prematurely: signal 11 (SIGSEGV) Job: 0.')
Traceback (most recent call last):
  File "/Users/user/Desktop/project/django_app/django_venv/lib/python3.12/site-packages/billiard/pool.py", line 1265, in mark_as_worker_lost
    raise WorkerLostError(
billiard.einfo.ExceptionWithTraceback: 
"""
Traceback (most recent call last):
  File "/Users/user/Desktop/project/django_app/django_venv/lib/python3.12/site-packages/billiard/pool.py", line 1265, in mark_as_worker_lost
    raise WorkerLostError(
billiard.exceptions.WorkerLostError: Worker exited prematurely: signal 11 (SIGSEGV) Job: 0.
"""

The implementation looks like this,

# Views.py
from . import tasks
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse

def trainVideos(request):
    try:
        tasks.run_transcription.delay()
        return JsonResponse({"status": "success", "message": "Transcription has started check back later."})
    # return render(request, 'embed.html', {'data': data})
    except Exception as e:
        JsonResponse({"status": "error", "message": str(e)})

Here is what the transcribe function looks like, where the celery worker throws the worker exited prematurely error.

# Add one or two audios possibly .wav, .mp3 in a folder,
# and provide the file path here.
# transcribe.py 

import whisper_timestamped as whisper
import os
def transcribeTexts(model_id, filePath):
    result = []
    fileNames = os.listdir()
    
    model = whisper.load_model(model_id)

    for files in fileNames:
        audioPath = filePath + "/" + files

        audio = whisper.load_audio(audioPath)

        result.append(model.transcribe(audio, language="en"))
    
    return result
 model_id = "tiny"
 audioFilePath = path/to/audio
 transcribeTexts(model_id, audioFilePath)

Install the following libraries to reproduce the problem:

 pip install openai-whisper
 pip3 install whisper-timestamped
 pip install Django
 pip install celery redis
 pip install redis-server

The Celery Implementation: # celery.py from project main_app directory

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main_app.settings')

app = Celery('main_app')

app.config_from_object('django.conf:settings', namespace='CELERY')

app.autodiscover_tasks()

def debug_tasks(self):
    print(f"Request: {self.request!r}")

tasks.py from the transcribe_app directory:

from __future__ import absolute_import, unicode_literals
from . import transcribe
from celery import shared_task

@shared_task
def run_transcription():
    transcribe.transcribe()
    return "Transcription Completed..."

The settings.py is also updated with the following:

CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True

Also, modified the init.py file from django_app

from __future__ import absolute_import, unicode_literals

from .celery import app as celery_app

__all__ = ('celery_app',) 

For this application, some of the libraries are dependent on particular versions. All libraries and packages are listed below:

Package              Version
-------------------- -----------
amqp                 5.3.1
asgiref              3.8.1
billiard             4.2.1
celery               5.4.0
certifi              2024.8.30
charset-normalizer   3.3.2
click                8.1.7
click-didyoumean     0.3.1
click-plugins        1.1.1
click-repl           0.3.0
Cython               3.0.11
Django               5.1.2
django-widget-tweaks 1.5.0
dtw-python           1.5.3
faiss-cpu            1.9.0
ffmpeg               1.4
filelock             3.16.1
fsspec               2024.9.0
huggingface-hub      0.25.2
idna                 3.10
Jinja2               3.1.4
kombu                5.4.2
lfs                  0.2
llvmlite             0.43.0
MarkupSafe           3.0.1
more-itertools       10.5.0
mpmath               1.3.0
msgpack              1.1.0
networkx             3.3
numba                0.60.0
numpy                2.0.2
packaging            24.1
panda                0.3.1
pillow               10.4.0
pip                  24.3.1
prompt_toolkit       3.0.48
pydub                0.25.1
python-dateutil      2.9.0.post0
PyYAML               6.0.2
redis                5.2.0
regex                2024.9.11
requests             2.32.3
safetensors          0.4.5
scipy                1.14.1
semantic-version     2.10.0
setuptools           75.1.0
setuptools-rust      1.10.2
six                  1.16.0
sqlparse             0.5.1
sympy                1.13.3
tiktoken             0.8.0
tokenizers           0.20.1
torch                2.4.1
torchaudio           2.4.1
torchvision          0.19.1
tqdm                 4.66.5
transformers         4.45.2
txtai                7.4.0
typing_extensions    4.12.2
tzdata               2024.2
urllib3              2.2.3
vine                 5.1.0
wcwidth              0.2.13
whisper-timestamped  1.15.4

Overall, when I run the program independently, it works perfectly fine. But within Django, it just terminates however I execute it. I thought one of the reasons might be since I am loading long audios, so I chunked it and tried to run the transcribe.py program using the user interface; however, it's the same thing worker exited prematurely, signal 11 (SIGSEGV) Job: 0. I tried changing memory pool size to a higher level for a worker, didn't work. I am unsure exactly what needs to be done to run the transcribe.py file within Django since most known methods are not working for me. I may have missed something, so please help me figure this out. Thank you for your time.


Solution

  • sigsegv often comes when you try to access memory that's not accessible by your program, see here. I could re-create the code and it worked completely fine on my end. Here are the probable reasons why this happened to you:

    I'll walk you through how I re-created your code, and maybe you made a typo or a little mistake that resulted in the error you mentioned.

    django-admin startproject project101
    cd project101
    python3 manage.py startapp app101
    

    project101/urls.py:

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include("app101.urls"))
    ]
    

    project101/settings.py:

    INSTALLED_APPS = [
        # ...
        
        'app101'
    ]
    
    # put this at the end of settings.py
    CELERY_BROKER_URL = 'redis://localhost:6379/0'
    CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
    

    project101/celery.py

    from __future__ import absolute_import, unicode_literals
    import os
    from celery import Celery
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project101.settings')
    
    app = Celery('project101')
    
    app.config_from_object('django.conf:settings', namespace='CELERY')
    
    app.autodiscover_tasks()
    
    def debug_tasks(self):
        print(f"Request: {self.request!r}")
    
    

    project101/init.py:

    from __future__ import absolute_import, unicode_literals
    
    from .celery import app as celery_app
    
    __all__ = ('celery_app',) 
    

    app101/views.py:

    from . import tasks
    from django.shortcuts import render
    from django.http import HttpResponse, JsonResponse
    
    def trainVideos(request):
        try:
            tasks.run_transcription.delay()
            return JsonResponse({"status": "success", "message": "Transcription has started check back later."})
        # return render(request, 'embed.html', {'data': data})
        except Exception as e:
            JsonResponse({"status": "error", "message": str(e)})
    
    

    app101/urls.py:

    from django.urls import path, include
    from . import views
    
    urlpatterns = [
        path('transcribe', views.trainVideos)
    ]
    
    

    app101/tasks.py:

    from __future__ import absolute_import, unicode_literals
    from . import transcribe
    from celery import shared_task
    
    @shared_task
    def run_transcription():
        transcribe.transcribe()
        return "Transcription Completed..."
    
    

    app101/transcribe.py:

    
    import whisper_timestamped as whisper
    import os
    
    def transcribeTexts(model_id, audio_directory_path):
        result = []
        fileNames = os.listdir(audio_directory_path)
        
        model = whisper.load_model(model_id)
    
        for files in fileNames:
            print(files)
            audioPath = audio_directory_path + "/" + files
    
            audio = whisper.load_audio(audioPath)
    
            result.append(model.transcribe(audio, language="en"))
        print(result)
        return result
    
    def transcribe():
        model_id = "tiny"
        audio_directory_path = 'audio_sample'
        transcribeTexts(model_id, audio_directory_path)
    
    

    Note that audio_sample is a folder outside app101, it has the same level as app101 and project101. You could make it in another folder but make sure to specify the correct directory path. I've added directory structure below.

    .
    ├── app101
    │   ├── admin.py
    │   ├── apps.py
    │   ├── __init__.py
    │   ├── migrations
    │   ├── models.py
    │   ├── __pycache__
    │   ├── tasks.py
    │   ├── tests.py
    │   ├── transcribe.py
    │   ├── urls.py
    │   └── views.py
    ├── audio_sample
    │   └── some_audio.mp3
    ├── db.sqlite3
    ├── manage.py
    └── p101
        ├── asgi.py
        ├── celery.py
        ├── __init__.py
        ├── __pycache__
        ├── settings.py
        ├── urls.py
        └── wsgi.py
    
    

    After this, run the following commands on separate terminals:

    python3 manage.py runserver
    
    celery -A project101 worker --pool=solo -l info
    

    This should make your project up and running. To test, send a get request to http://localhost:8000/transcribe or simply open it in your browser.

    Note the following: