One of my webpages takes about 3 seconds to load locally, and 15 seconds to load when it's live on Heroku. I believe the problem is how many synchronous Google TTS (Text-To-Speech) API calls and synchronous database / Amazon S3 writes I make.
I think asynchronous coding would help, but I'm not entirely sure how to implement it. Here's what's happening in the view:
# views.py
def convert_str_to_audio_info_if_necessary(audio_str):
audio_info = AudioInfo.objects.get_by_text(audio_str)
if audio_info is None:
audio_content = synthesize_text(audio_str) # returns audio file from Google
# WAIT for the response to come back from Google's API
new_audio = ContentFile(audio_content, 'audio.wav') # converts to file Python can read
audio_info = AudioInfo.objects.create_problem_audio(text=audio_str, audio=new_audio, duration=get_audio_file_duration(new_audio))
# WAIT for the audio file to be written to my S3 bucket
return audio_info
def slow_loading_view(request):
for i in range(100):
audio_str = str(i)
audio_info = convert_str_to_audio_info_if_necessary(audio_str)
context[audio_str] = audio_info
# Now I would like to pass this data in my context to use in the webpage
return render(request, 'my_page.html', context)
And my in my model:
# models.py
class AudioInfoManager(models.Manager):
def get_by_text(self, text):
qs = self.get_queryset().filter(text=text)
if len(qs) == 0:
return None
return qs[0]
def create_problem_audio(self, text, audio, duration):
already_created_entry = self.get_by_text(text)
if already_created_entry != None:
return already_created_entry
problem_audio = self.create(text=text, audio=audio, duration=duration)
return problem_audio
class AudioInfo(models.Model):
text = models.TextField(unique=True)
audio = models.FileField(upload_to=upload_audio_info_path)
duration = models.FloatField()
objects = AudioInfoManager()
As you can see, there's a lot of waiting (idle time) going on in the view, so ideally i'd be able to 1) asynchronously send all the google API requests to generate the audio files, and then after I have all those audio files returned 2) asynchronously write those audio files to the database and S3 buckets, and then once they are all written, 3) query the database for their data and pass the data as context to render my webpage.
It seems like all the asynchronous libraries in Django such as Celery and Redis Queue only help with background tasks (tasks that aren't required to be completed before rendering the view's webpage, e.g. sending emails, writing data to a database that isn't needed in the webpage, etc.). Perhaps asyncio or Django Channels are solutions? Django is a synchronous framework, so I'm not sure whether I'm allowed to update the database asynchronously.
Any suggestions for what I should do?