﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36439	Auth hashing blocks event loop if using asyncio	Robert Aistleitner	Roelzkie	"To a large extent, issues with auth in async auth has been fixed in https://github.com/django/django/commit/50f89ae850f6b4e35819fe725a08c7e579bfd099, I guess this is the last part of proper async implementation of authentication.

There exist custom implementations for asyncio regarding checking a password using the configured hashers which can be found in https://github.com/django/django/blob/68c9f7e0b79168007e6ba0139fd315d7c44ca8c9/django/contrib/auth/hashers.py#L86-L91.

This implementation has a flaw because the CPU heavy calculation of the hash is blocking the event loop of asyncio, causing the whole server to stall and queueing up all the following authentications that may rush in in case of heavy load.

My proposal is to use a ThreadPoolExecutor here to be able to unload the work from the event loop:

{{{
CHECK_PASSWORD_THREAD_POOL_EXECUTOR = ThreadPoolExecutor(16)

async def acheck_password(password, encoded, setter=None, preferred=""default""):
    """"""See check_password().""""""

    # verify_password is cpu heavy and needs to be executed in a separate thread to not block a running asyncio event loop
    is_correct, must_update = await sync_to_async(
        verify_password, thread_sensitive=False, executor=CHECK_PASSWORD_THREAD_POOL_EXECUTOR
    )(password, encoded, preferred=preferred)

    if setter and is_correct and must_update:
        await setter(password)
    return is_correct
}}}

The number of available thread could be exposed via setting, or skipped altogether by using a new thread on each verify_password call. What are your thoughts on this?
"	Cleanup/optimization	closed	contrib.auth	5.2	Normal	fixed	async, auth, asyncio, performance	Robert Aistleitner Roelzkie	Ready for checkin	1	0	0	0	0	0
