SSE DJANGO REQUEST COUNT - django

i still new using SSE and i have a question about SSE in Django version 3.2.5, i am using StreamingHttpResponse to send SSE response to EventSource client and it does work fine,
my question that
why it takes to long to open the connection between backend and EventSource?
why it sends only 167 responses/32 seconds ?
i tried to open the code of StreamingHttpResponse but i didn't find anything related to the number of response
here in the code
def sse_movies(request):
def event_stream():
while True:
sleep(.2)
yield f"data: {datetime.time(datetime.now())}\n\n"
return StreamingHttpResponse(event_stream(), content_type='text/event-stream')
i am using sleep() to wait only 200/milliseconds for each iteration.
but whenever send the EventSource it waits almost 32/seconds to initiate the connection with the back-end, and after it sends 167 requests then waits 2 seconds then sends another 167 requests once more and after the second 167 is sent it waits another 32 seconds
here is the code of EventSource client
let url = '/test/' +'sse/movies/'
let sse_client = new EventSource(url)
let movies = document.querySelector('#data-movies')
let movies_list = document.querySelector('#messages')
sse_client.onopen = function(message_event) {
console.log('opened')
}
console.log(sse_client)
sse_client.onmessage = (message_event) => {
console.log(message_event.data)
console.log(sse_client.readyState)
}
NOTE: when i remove white: True EventSource doesn't wait and sends requests as much as possible
maybe i misunderstand something here, but i hope the somebody could help me

i could figure out the issue.
it was n't in the code itself, but it was related with the buffer size of the webserver
so when i edited my code to be as below it worked fine:
def sse_movies(request):
def event_stream():
body = ''.join([letter * 6000 for letter in 'A'])
body_len = len(body)
print(body_len)
while True:
sleep(2)
yield f"data: {body}\n\n"
return StreamingHttpResponse(event_stream(), content_type='text/event-stream')
as you see above the minimum size for buffer is 6000/Characters
i don't not how much that could be in bytes), but yeah it worked Alhumdulliah)
i really don't know that much about buffer/buffer-size/..
but i thought that it could be the issue

Related

Twisted Python for responding multiple clients at a time

I have a problem. I'm having a echoserver which will accept clients and process his requirement and it returns the result to client.
Suppose I have two clients and 1 client requirement processing time would be 10 sec and 2 client requirement processing time would be 1 sec.
So when both clients connected to server at a time. how to run both the clients tasks at a time parallely and return the response to specific client which ever finishes first.
I have read that we can achieve this problem using python twisted. I have tried my luck, but Im unable to do it.
Please help me out of this Issue
Your code (https://trinket.io/python/87fd18ca9e) has many mistakes in terms of async design patterns, but I will only address the most blatant mistake. There are a few calls to time.sleep(), this is blocking code and is causing your code to stop until the sleep function is done running. The number 1 rule it async programming is do not use blocking functions! Don't worry, this is a very common mistake and the Twisted and Python async communities are there to help you :) I'll give you a naive solution for your server:
from twisted.internet.protocol import Factory
from twisted.internet import reactor, protocol, defer, task
def sleep(n):
return task.deferLater(reactor, n, lambda: None)
class QuoteProtocol(protocol.Protocol):
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
self.factory.numConnections += 1
#defer.inlineCallbacks
def recur_factorial(self,n):
fact=1
print(n)
for i in range(1,int(n)+1):
fact=fact*i
yield sleep(5) # async sleep
defer.returnValue(str(fact))
def dataReceived(self, data):
try:
number = int(data) # validate data is an int
except ValueError:
self.transport.write('Invalid input!')
return # "exit" otherwise
# use Deferreds to write to client after calculation is finished
deferred_factorial = self.recur_factorial(number)
deferred_factorial.addCallback(self.transport.write)
def connectionLost(self, reason):
self.factory.numConnections -= 1
class QuoteFactory(Factory):
numConnections = 0
def buildProtocol(self, addr):
return QuoteProtocol(self)
reactor.listenTCP(8000, QuoteFactory())
reactor.run()
The main differences are in recur_factorial() and dataReceived(). The recur_factorial() is now utilizing Deferred (search how inlineCallbacks or coroutine's works) which allows for functions to execute after the result is available. So when the data in received, the factorial is calculated, then written to the end user. Finally there's the new sleep() function which allows for an async sleep function. I hope this helps. Keep reading the Krondo blog.

Flask: streaming file with stream_with_context is very slow

The following code streams a postgres BYTEA column to a browser
from flask import Response, stream_with_context
#app.route('/api/1/zfile/<file_id>', methods=['GET'])
def download_file(file_id):
file = ZFile.query.filter_by(id=file_id).first()
return Response(stream_with_context(file.data), mimetype=file.mime_type)
it is extreemely slow (aprox 6 minutes for 5 mb).
I am downloading with curl from the same host, so network is not the issue,
also I can extract the file from the psql console in less than a second,
so it seems the database side is also not to blame :
COPY (select f.data from z_file f where f.id = '4ec3rf') TO 'zazX.pdf' (FORMAT binary)
Update:
I have further evidence that the "fetch from the DB" step is not slow, If I write file.data to a file using
with open("/vagrant/zoz.pdf", 'wb') as output:
output.write(file.data)
it also takes a fraction of a second. So the slowness is caused by the way Flask does the streaming.
I had this issue while using Flask to proxy streaming from another url using python-requests.
In this use case, the trick is setting the chunk_size parameter in iter_content:
def flask_view():
...
req = requests.get(url, stream=True, params=args)
return Response(
stream_with_context(req.iter_content(chunk_size=1024)),
content_type=req.headers['content-type']
otherwise it will use chunk_size=1, which can slow things down quite a bit. In my case the streaming went from a couple of kb/s to several mb/s after the increase in chunk_size.
Flask can be given a generator that returns the whole array in a single yield and will "know" how to deal with it, this returns in milliseconds :
from flask import Response, stream_with_context
#app.route('/api/1/zfile/<file_id>', methods=['GET'])
def download_file(file_id):
file = ZFile.query.filter_by(id=file_id).first()
def single_chunk_generator():
yield file.data
return Response(stream_with_context(single_chunk_generator()), mimetype=file.mime_type)
stream_with_context, when given an array will create a generator that iterates through it and do various checks on every element, which causes a huge performance hit.

gevent-socketio: browser receiving another browser's response

I've been trying this for a couple of days now but I can't seem to wrap my head around this. My problem essentially lies with the fact if I have 2 browsers requesting at the same time, my server-side socketio response will return the wrong results to the wrong browser requesting (the results get swapped). I think my problem is that I don't know how does socket.io determines which browser to return the results to. The current code has lotsa moving parts and it'll be a pain to strip to manner that people can find meaningful so instead, I think I will be able to resolve my bug if someone can help me work through and understand the django_chat example found at https://github.com/abourget/gevent-socketio/tree/master/examples/django_chat. So here goes:
So sequentially, when a user enters something into chat, this code fires
$('#send-message').submit(function () {
message('me', $('#message').val());
socket.emit('user message', $('#message').val());
clear();
$('#lines').get(0).scrollTop = 10000000;
return false;
});
The socket.emit function then triggers this function in the ChatNameSpace class:
def on_user_message(self, msg):
self.log('User message: {0}'.format(msg))
self.emit_to_room(self.room, 'msg_to_room',
self.socket.session['nickname'], msg)
return True
Which in turn calls this emit_to_room function found in the RoomsMixin class
def emit_to_room(self, room, event, *args):
"""This is sent to all in the room (in this particular Namespace)"""
pkt = dict(type="event",
name=event,
args=args,
endpoint=self.ns_name)
room_name = self._get_room_name(room)
for sessid, socket in self.socket.server.sockets.iteritems():
if 'rooms' not in socket.session:
continue
if room_name in socket.session['rooms'] and self.socket != socket:
socket.send_packet(pkt)
I understand that when a user joins a chat room, the [rooms] session gets updated with the chatroom he belongs to. It looks something like ['/chat_1', '/chat_2'] where the numbers signify the primary key of the room object.
This is where I get lost. Where does this distinction of specific chatrooms meet the frontend js code? How does the emit function know where to send the response to which room?

Django Celery reduce time, 5 hours to complete 1000 tasks

I'm running on a development environment so this maybe different in production, but when I run a task from Django Celery, it seems to only fetch tasks from the broker every 10-20 seconds. I'm only testing at this point but lets say I'm sending around 1000 tasks this means it will take over 5 hours+ to complete.
Is this is normal? Should it be quicker? Or I'm I doing something wrong?
This is my task
class SendMessage(Task):
name = "Sending SMS"
max_retries = 10
default_retry_delay = 3
def run(self, message_id, gateway_id=None, **kwargs):
logging.debug("About to send a message.")
# Because we don't always have control over transactions
# in our calling code, we will retry up to 10 times, every 3
# seconds, in order to try to allow for the commit to the database
# to finish. That gives the server 30 seconds to write all of
# the data to the database, and finish the view.
try:
message = Message.objects.get(pk=message_id)
except Exception as exc:
raise SendMessage.retry(exc=exc)
if not gateway_id:
if hasattr(message.billee, 'sms_gateway'):
gateway = message.billee.sms_gateway
else:
gateway = Gateway.objects.all()[0]
else:
gateway = Gateway.objects.get(pk=gateway_id)
#response = gateway._send(message)
print(message_id)
logging.debug("Done sending message.")
which gets run from my view
for e in Contact.objects.filter(contact_owner=request.user etc etc):
SendMessage.delay(e.id, message)
Yes this is normal. This is the default workers to be used. They set this default so that it will not affect the performance of the app.
There is another way to change it. The task decorator can take a number of options that change the way the task behaves. Any keyword argument passed to the task decorator will actually be set as an attribute of the resulting task class.
You can set the rate limit which limits the number of tasks that can be run in a given time frame.
//means hundred tasks a minute, another /s (second) and /h (hour)
CELERY_DEFAULT_RATE_LIMIT = "100/m" --> set in settings

In Django, how to find out if a request has been canceled?

I have a view in Django which streams a response. (Think of a web-based chat circa 1999, or the comet technique.)
def events(request):
def generate_events():
for i in range(10):
time.sleep(2)
yield " " * 1024
yield "This is some text.\n"
return HttpResponse(generate_events())
Now, I'd like to detect when the user cancels the loading of the page, since there is no point in sending more data. Ideally, there would be something like:
if not request.is_alive():
return
Is there a way to achieve this in Django?
I really don't think you can do that from the server-side. But I'm sure you could use JavaScript to get some decent results. The "stream" will die when the JS stops asking for stuff from the server like in the case of a cancelled request.