mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 23:07:42 -02:30
prefer simple async -> sync
* asgiref async_to_sync was causing a Redis connection _for each_ call to emit_channel_notification i.e. every event that the callback receiver processes. This is a "known" issue https://github.com/django/channels_redis/pull/130#issuecomment-424274470 and the advise is to slow downn the rate at which you call async_to_sync. That is not an option for us. Instead, we put the async group_send call onto the event loop for the current thread and wait for it to be processed immediately. The known issue has to do with event loop + socket relationship. Each connection to redis is achieved via a socket. That conection can only be waiting on by the event loop that corresponds to the calling thread. async_to_sync creates a _new thread_ for each invocation. Thus, a new connection to redis is required. Thus, the excess redis connections that can be observed via netstat | grep redis | wc -l.
This commit is contained in:
committed by
Ryan Petrello
parent
dc6c353ecd
commit
5818dcc980
@@ -5,6 +5,7 @@ import logging
|
|||||||
import codecs
|
import codecs
|
||||||
import datetime
|
import datetime
|
||||||
import hmac
|
import hmac
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
@@ -200,28 +201,66 @@ class EventConsumer(AsyncJsonWebsocketConsumer):
|
|||||||
await self.send(event['text'])
|
await self.send(event['text'])
|
||||||
|
|
||||||
|
|
||||||
def emit_channel_notification(group, payload):
|
def run_sync(func):
|
||||||
|
event_loop = None
|
||||||
try:
|
try:
|
||||||
payload = json.dumps(payload, cls=DjangoJSONEncoder)
|
event_loop = asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
event_loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(event_loop)
|
||||||
|
return event_loop.run_until_complete(func)
|
||||||
|
|
||||||
|
|
||||||
|
def _dump_payload(payload):
|
||||||
|
try:
|
||||||
|
return json.dumps(payload, cls=DjangoJSONEncoder)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.error("Invalid payload emitting channel {} on topic: {}".format(group, payload))
|
logger.error("Invalid payload to emit")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def emit_channel_notification_async(group, payload):
|
||||||
|
payload_dumped = _dump_payload(payload)
|
||||||
|
if payload_dumped is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
channel_layer = get_channel_layer()
|
||||||
|
await channel_layer.group_send(
|
||||||
|
group,
|
||||||
|
{
|
||||||
|
"type": "internal.message",
|
||||||
|
"text": payload_dumped
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await channel_layer.group_send(
|
||||||
|
BROADCAST_GROUP,
|
||||||
|
{
|
||||||
|
"type": "internal.message",
|
||||||
|
"text": wrap_broadcast_msg(group, payload_dumped),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_channel_notification(group, payload):
|
||||||
|
payload_dumped = _dump_payload(payload)
|
||||||
|
if payload_dumped is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
channel_layer = get_channel_layer()
|
channel_layer = get_channel_layer()
|
||||||
|
|
||||||
async_to_sync(channel_layer.group_send)(
|
run_sync(channel_layer.group_send(
|
||||||
group,
|
group,
|
||||||
{
|
{
|
||||||
"type": "internal.message",
|
"type": "internal.message",
|
||||||
"text": payload
|
"text": payload_dumped
|
||||||
},
|
},
|
||||||
)
|
))
|
||||||
|
|
||||||
async_to_sync(channel_layer.group_send)(
|
run_sync(channel_layer.group_send(
|
||||||
BROADCAST_GROUP,
|
BROADCAST_GROUP,
|
||||||
{
|
{
|
||||||
"type": "internal.message",
|
"type": "internal.message",
|
||||||
"text": wrap_broadcast_msg(group, payload),
|
"text": wrap_broadcast_msg(group, payload_dumped),
|
||||||
},
|
},
|
||||||
)
|
))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user