Simulating timeouts on client with django.test.client.Client

665 views
Skip to first unread message

Александр Христюхин

unread,
Aug 23, 2016, 12:59:40 PM8/23/16
to Django developers (Contributions to Django itself)
Hi!

I'm trying to simulate timeouts on client to test my app's behaviour with requests, that take too much time. I want to use Django's test client for that and supply timeout arg for each request (like in requests), but django.test.client.Client doesn't support timeouts.

Here's a ticket about that: https://code.djangoproject.com/ticket/27112

Tim Graham

unread,
Aug 23, 2016, 1:41:51 PM8/23/16
to Django developers (Contributions to Django itself)
Have you tried writing your own subclass of the test client (or writing a patch for Django's own test client) so we can see what it looks like?

(I suggested writing to the mailing list to get other feedback because I'm not sure if this is a good idea or even feasible.)

Shai Berger

unread,
Aug 23, 2016, 4:16:10 PM8/23/16
to django-d...@googlegroups.com
Hi,

Just to make it clear: Based on the ticket, you want to test how the server
acts when facing client timeouts and/or disconnects. Please correct me if I'm
wrong.

Assuming I'm right, the test client seems like the wrong tool for this job,
because it doesn't actually make network connections; it calls directly into
Django. The way your production app reacts to these timeouts and disconnects
depends on the nature of the network issues and their handling by your web
server and WSGI implementation; you cannot test them with a mechanism which
bypasses these components.

HTH,
Shai.

On Tuesday 23 August 2016 19:58:29 Александр Христюхин wrote:
> Hi!
>
> I'm trying to simulate timeouts on client to test my app's behaviour with
> requests, that take too much time. I want to use Django's test client for
> that and supply timeout arg for each request (like in requests
> <http://docs.python-requests.org/en/master/user/advanced/#timeouts>),

Александр Христюхин

unread,
Aug 24, 2016, 7:33:35 AM8/24/16
to Django developers (Contributions to Django itself)
Actually network is not an issue here. I'm using locks in distributed cache to manage requests between backends and that's what I want to test. In this case calling Django directly is okay.

вторник, 23 августа 2016 г., 23:16:10 UTC+3 пользователь Shai Berger написал:

Shai Berger

unread,
Aug 24, 2016, 9:44:53 AM8/24/16
to django-d...@googlegroups.com
On Wednesday 24 August 2016 14:33:34 Александр Христюхин wrote:
> Actually network is not an issue here. I'm using locks in distributed cache
> to manage requests between backends and that's what I want to test. In this
> case calling Django directly is okay.
>

I am sorry, then; it seems I have misunderstood your needs. Now I am quite
unsure about who are the "players" in your use-case -- who makes requests to
whom, what kind of requests, which side of these requests is supposed to time
out, and how the other side is supposed to discover the problem.

It may be helpful to do as Tim suggested and write a prototype, just so we get
the details in a clear and unambiguous way.

HTH,
Shai.

GMail

unread,
Aug 24, 2016, 10:11:00 AM8/24/16
to django-d...@googlegroups.com
"Players" are backends (Django) and load balancer that proxies all user requests on one of the backends.
In my case load balancer has small backend timeout (default is 3 sec, preferable is 1 sec) and it will send the same request (with the same X-Request-Id header) to another backend after this small timeout.
Some requests may take a long time to process (well, longer than 1 sec), so backends should not try to process requests that are being processed by other backends (they actually return "HTTP 202 Accepted" instead).

For testing purposes I have '/slow/<time>' view that takes exactly <time> seconds to process.

What I want to do is this:
1. Use django.test.client.Client to send request with small timeout (like load balancer will do) and make assertion, that TimeoutError is raised.
2. Send same request again and make assertion, that backend responded with "HTTP 202 Accepted".
3. Wait for some time (<time> seconds to be sure, that request has been processed by first backend) and send same request again and make final assertion, that backend responded with "HTTP 200 OK".

In my case backends are synchronised through distributed cache, so it doesn't matter which backend accepts each request, response will be the same.

I do realise, that this can be accomplished by just firing up "manage.py runserver" and sending requests as real load balancer would do, but I think it's too much overhead for a simple test.

By "prototype" you mean pull-request to Django with django.test.client.Client that supports 'timeout' argument, right?

Shai Berger

unread,
Aug 24, 2016, 10:19:24 AM8/24/16
to django-d...@googlegroups.com
Yes, the prototype I mentioned is a PR against Django, except that if it is
intended to clarify the request rather than supply it, it doesn't have to be
full -- e.g. it doesn't need tests or documentation untill we decide we want
the feature in.

But before you go there -- isn't LiveServerTestCase (which starts a server you
can send requests to) a better answer to your needs?

GMail

unread,
Aug 24, 2016, 10:26:46 AM8/24/16
to django-d...@googlegroups.com
I didn't try LiveServerTestCase yet (and to be honest never heard of it before), I'll give it a try. Thanks!

GMail

unread,
Aug 24, 2016, 2:21:07 PM8/24/16
to django-d...@googlegroups.com
So I've tried LiveServerTestCase and it seems that live server is single-threaded.

If client timeout is 5 seconds, then LiveServerTestCase works fine for slow requests, that take from 1 to 9 seconds to process. Second try will send request to the same thread and client will get TimeoutError instead of "HTTP 202 Accepted" (because thread is busy processing first request).

I didn't find any information about how to start live server with multiple threads in Django docs or LiveServerTestCase code, is that even possible?

Tim Graham

unread,
Aug 24, 2016, 2:36:45 PM8/24/16
to Django developers (Contributions to Django itself)
multithreading support has been proposed a couple times but it hasn't passed the discussion stage yet.

https://code.djangoproject.com/ticket/20238
https://code.djangoproject.com/ticket/25970

GMail

unread,
Aug 24, 2016, 2:37:20 PM8/24/16
to django-d...@googlegroups.com
I found this ticket about same issue: https://code.djangoproject.com/ticket/20238
It was closed 2 years ago and one of the reasons is that use case was considered "somewhat fictional". I personally think my case is very real :)

I'm thinking about using 'manage.py runserver' manually, because it seems it would do the same thing as LiveServerTestCase and also server with multiple threads. But that solution doesn't seem right to me.

GMail

unread,
Aug 24, 2016, 2:39:41 PM8/24/16
to django-d...@googlegroups.com
Wow, that was a quick response!
Tim, is there a way for me to join the discussion? Is there another mail thread?

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/93c1ee6d-a301-475f-a664-d8d5d63660c7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

roboslone

unread,
Aug 24, 2016, 2:46:44 PM8/24/16
to Django developers (Contributions to Django itself)
In answer ti "Any idea how complex the patch would be?" in https://code.djangoproject.com/ticket/25970#comment:2 I think using socketserver.ThreadingMixin with django.core.servers.WSGIServer would do the job. Not completely sure about consequences though...

вторник, 23 августа 2016 г., 19:59:40 UTC+3 пользователь roboslone написал:

GMail

unread,
Aug 24, 2016, 3:05:58 PM8/24/16
to django-d...@googlegroups.com
I've actually tried adding ThreadingMixIn to WSGIServer and it worked like a charm in my case.
Here's the code I added: 

from django.test.testcases import LiveServerThread, QuietWSGIRequestHandler
from django.core.servers.basehttp import WSGIServer
from socketserver import ThreadingMixIn

class ThreadedWSGIServer(ThreadingMixIn, WSGIServer):
    pass


class ThreadedLiveServerThread(LiveServerThread):
    def _create_server(self, port):
        return ThreadedWSGIServer((self.host, port), QuietWSGIRequestHandler)


class ThreadedLiveServerTestCase(LiveServerTestCase):
    @classmethod
    def _create_server_thread(cls, host, possible_ports, connections_override):
        return ThreadedLiveServerThread(
            host,
            possible_ports,
            cls.static_handler,
            connections_override=connections_override,
        )

(just in case, same on pastebin: http://pastebin.com/wTBCWQKJ)


--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
Reply all
Reply to author
Forward
0 new messages