Django Request langsam

Django, Flask, Bottle, WSGI, CGI…
BlackJack

Passiert sonst noch irgend etwas, zum Beispiel schreibende Zugriffe auf die Datenbank? Wie ist der Raspi ans Netz angebunden? WiFi oder Netzwerkkabel?
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

noisefloor hat geschrieben:Hallo,

die Langsamkeit ist aber nicht dadurch bedingt, dass das bootstrap.css zu langsam ausgeliefert wird, oder? Normalerweise ist das zwar kein Problem (zumindest nicht bei meinen Django-Projekten), aber wenn wir gerade noch beim rumraten sind, sollten wir das nicht ausschließen ;-)

Zweiter Tipp zum Debugging:
Modifiziert den View mal so, dass die DB-Abfrage ausfällt und du dem Template direkt eine fertige Liste mitgibst. Wenn das dann immer noch lange dauert, kannst du DB-Abfrage als Quelle der Langsamkeit ausschließen.

Gruß, noisefloor
Ich habe das Laden des bootstrap.css mal ausgeschaltet. Es hat sich nichts geändert.

Wie soll ich dem View einer fertige Liste mitgeben? Dazu müsste ich 95 Daten fest reincoden?
BlackJack

@xerion21: Die kannst Du ja erstellen. Vielleicht auch einen Eintrag 95 mal an eine Liste anhängen.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
xerion21 hat geschrieben:Wie soll ich dem View einer fertige Liste mitgeben? Dazu müsste ich 95 Daten fest reincoden?
Du kannst den Query auch in der Shell ausführen, das Ergebnis per `print` ausgeben lassen und dann per Copy&Paste in den Code hardcoded einfügen.

Oder halt wie BlackJack sagt einen (Fantasie-) Datensatz von Hand tippen und dann eine Liste mit 95x dem gleichen Eintrag hardocden.

Gruß, noisefloor
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

xerion21 hat geschrieben:

Code: Alles auswählen

{% for player in players %}
    <li class="well">{% include "Spielerdatenbank/player_list_item.html" %}</li>
{% empty %}
Inklude in der Schleife ist möglicherweise ein Template-Resolve pro Spieler. Aktivier mal den cached Templateloader, oder tu testweise den Inhalt von player_list_item.html direkt hinein.
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

Hi,

ich habe nun mein ganzen "Webservice" mal unter einem baugleichen RPI getestet.
Das Result:
Der RPI ist genauso langsam bei den Aufrufen. Also wird es wahrscheinlich an dem RPI liegen. Das Modell hat wahrscheinliche einfach nicht genug Kapazitäten um diesen Dienst auszuführen...

Ich werde mir noch ein RPI vom Typ B2 anlegen und mal schauen, wie der mit dem Webservice zurecht kommt.
BlackJack

@xerion21: Das kann nicht sein. Ein Raspi, auch einer der ersten Generation, ist in der Lage ein einfaches SELECT von 100 Datensätzen aus einer SQLite-DB abzufragen und als Webseite aufzubereiten und auszuliefern ohne dafür *10* *Sekunden* zu brauchen. Das muss irgendwo ein Problem beim Code sein.

Schau Dir mal den CPU-Verbrauch während der 10 Sekunden kontinuierlich an. Falls die CPU durchgehend ackert, dann macht der Code sehr wahrscheinlich irgend etwas ungünstiges. Falls die CPU dagegen die meiste Zeit nichts macht, dann blockiert irgend etwas und der Rechner wartet auf einen Timeout. Dann müsste man dann heraus finden warum und auf was gewartet wird.
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

BlackJack hat geschrieben:@xerion21: Das kann nicht sein. Ein Raspi, auch einer der ersten Generation, ist in der Lage ein einfaches SELECT von 100 Datensätzen aus einer SQLite-DB abzufragen und als Webseite aufzubereiten und auszuliefern ohne dafür *10* *Sekunden* zu brauchen. Das muss irgendwo ein Problem beim Code sein.

Schau Dir mal den CPU-Verbrauch während der 10 Sekunden kontinuierlich an. Falls die CPU durchgehend ackert, dann macht der Code sehr wahrscheinlich irgend etwas ungünstiges. Falls die CPU dagegen die meiste Zeit nichts macht, dann blockiert irgend etwas und der Rechner wartet auf einen Timeout. Dann müsste man dann heraus finden warum und auf was gewartet wird.
Die Frage ist dann, warum der Code in meiner VM, der ich die gleichen Ressourcen gegeben habe wie der RPI, diesen Code ohne Probleme abhandelt.

Der CPU-Verbrauch ist während der Anfrage kontinuierlich auf 95%.

Ich weiß leider nicht mehr weiter... Ich kann euch den Code gerne zur Verfügung stellen, sagt mir jediglich was ihr von dem Code benötigt.
BlackJack

@xerion21: Naja, man bräuchte alles um das Problem nachstellen zu können. Also ein Short, Self Contained, Correct (Compilable), Example.
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

BlackJack hat geschrieben:@xerion21: Naja, man bräuchte alles um das Problem nachstellen zu können. Also ein Short, Self Contained, Correct (Compilable), Example.
Das Problem ist, dass ich euch die Datenbank mit den Daten leider nicht zur Verfügung stellen kann, aus datenschutztechnischen Gründen...
Die restlichen Scripte kann ich euch gerne zur Verfügung stellen
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@xerion21: wobei wir wieder bei der Frage von vor x Posts wären: Ist die Datenbankabfrage das Bottleneck?
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

Sirius3 hat geschrieben:@xerion21: wobei wir wieder bei der Frage von vor x Posts wären: Ist die Datenbankabfrage das Bottleneck?
wie gesagt, laut der django toolbar dauert die DB-Abfrage 40ms. Ich hatte leider noch keine Zeit, dies zu testen. Dies werde ich heute abend tun.
BlackJack

@xerion21: Das Beispiel muss ja nur das Problem illustrieren/nachvollziehbar machen. Füll die DB mit gleich Zufallsdaten die die gleichen Eigenschaften haben, prüfe ob das Problem damit immer noch besteht, und dann kannst Du das ja herzeigen und wir können auch auf die Suche gehen.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

xerion21 hat geschrieben:

Code: Alles auswählen

<a class="lead" href="/player/show/{{ player.id }}">{{ player.lastname }}, {{ player.firstname }}</a>
{% if player.birthday %}
    <br><b>Geburtstag: </b>{{player.birthday|date:"d.m.Y"|linebreaksbr }}
{% endif %}
<br><b>Erstellt vor: </b>{{ player.date_created|timesince }}
Mal wild geraten: Ist es möglicherweise eine schlechte Idee, "timesince" auf dem Raspi in einer Schleife aufzurufen? Da wird ja sicherlich die aktuelle Zeit vom System abgefragt. Vielleicht ist das ja langsam, wenn man es für 100 Spieler macht? Wäre zwar komisch, aber da der ganze Sachverhalt seltsam klingt, kommt ja erstmal alles mögliche in Betracht...

Vielleicht nimmst du einfach mal die letzte Zeile aus dem Template raus und guckst, ob sich was verändert hat.
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

snafu hat geschrieben:
xerion21 hat geschrieben:

Code: Alles auswählen

<a class="lead" href="/player/show/{{ player.id }}">{{ player.lastname }}, {{ player.firstname }}</a>
{% if player.birthday %}
    <br><b>Geburtstag: </b>{{player.birthday|date:"d.m.Y"|linebreaksbr }}
{% endif %}
<br><b>Erstellt vor: </b>{{ player.date_created|timesince }}
Mal wild geraten: Ist es möglicherweise eine schlechte Idee, "timesince" auf dem Raspi in einer Schleife aufzurufen? Da wird ja sicherlich die aktuelle Zeit vom System abgefragt. Vielleicht ist das ja langsam, wenn man es für 100 Spieler macht? Wäre zwar komisch, aber da der ganze Sachverhalt seltsam klingt, kommt ja erstmal alles mögliche in Betracht...

Vielleicht nimmst du einfach mal die letzte Zeile aus dem Template raus und guckst, ob sich was verändert hat.
Werde ich heute abend mal testen. Das Problem ist, dass die langsame Geschwindigkeit auch bei anderen Templates auftritt. So auch bei diesem:

Code: Alles auswählen

{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block title %}{% if create %}Erstelle{% else %}Bearbeite{% endif %} Spieler{% endblock %}

{% block heading %}
{% if create %}Erstelle Spieler{% else %}Bearbeite Spieler{% endif %}
{% endblock %}

{% block content %}
{% if create %}
{% url "Spielerdatenbank_player_create" as action_url %}
{% else %}
{% url "Spielerdatenbank_player_edit" player.pk as action_url %}
{% endif %}
<form action="{{ action_url }}" method="post" accept-charset="utf-8">
    {{ form|crispy }}
    {% csrf_token %}
    <p><input type="submit" class="btn btn-default" value="Speichern" /></p>
</form>
{% endblock %}
Das Modell dazu:

Code: Alles auswählen

class Player(models.Model):
    firstname = models.CharField('Vorname', max_length = 20)
    lastname = models.CharField('Nachname', max_length = 30)
    gender = models.CharField('Geschlecht', max_length = 1, choices = [ ('M', 'Männlich'), ('W', 'Weiblich') ] )
    birthday = models.DateField('Geburtstag')
    address = models.ForeignKey(Address, verbose_name = 'Adresse', related_name = 'player_address')
    phone_privat = models.CharField('Telefon Privat', max_length = 20, blank = True)
    phone_handy = models.CharField('Handy', max_length = 20, blank = True)
    phone_work = models.CharField('Telefon Arbeit', max_length = 20, blank = True)
    email_privat = models.EmailField('Email Privat', blank = True)
    email_work = models.EmailField('Email Arbeit', blank = True)
    email_club = models.EmailField('Email Verein', blank = True)
    mother = models.ForeignKey(Parent,  verbose_name = 'Mutter', related_name = 'player_mothers', null = True, blank = True)
    father = models.ForeignKey(Parent, verbose_name = 'Vater', related_name = 'player_fathers', null = True, blank = True)
    club = models.ForeignKey(Club, verbose_name = 'Verein', related_name = 'player_clubs')
    # Beitrittsjahr Verein?
    club_entry = models.DateField('Beitrittsdatum Verein')
    is_supporter = models.BooleanField('Mitglied Förderverein', default = False)
    date_supporter = models.DateField('Beitrittsdatum Förderverein', blank = True, null = True)
    # Förderverein (Beitritt)
    passnumber = models.CharField('Passnummer', max_length = 15, validators = [ RegexValidator(r'^[0-9 ]*$', 'Passnummer falsch') ] )
    play_eligibility = models.DateField('Spielberechtigt seit')
    position_attack = models.ForeignKey(PositionAttack, verbose_name = 'Position Angriff', null = True, blank = True)
    position_defence = models.ForeignKey(PositionDefence, verbose_name = 'Position Abwehr', null = True, blank = True)
    handed = models.CharField('Wurfhand', max_length = 1, choices = [ ('L', 'links' ), ('R', 'rechts') ], null = True, blank = True)
    body_weight = models.DecimalField('Körpergewicht',  help_text = 'in kg', max_digits = 4, decimal_places = 1, blank = True, null = True)
    body_height = models.DecimalField('Körpergröße', help_text = 'in cm', max_digits = 3, decimal_places = 0, blank = True, null = True)
    medics = models.TextField('Medikamente', blank = True)
    diseases = models.TextField('Krankheiten', blank = True)
    comment = models.TextField('Bemerkungen', blank = True)
    
    school = models.CharField('Schule', max_length = 75, blank = True)
    
    date_created = models.DateTimeField('Erstellt')
    date_updated = models.DateTimeField('Aktualiert')
    creator = models.ForeignKey(User, verbose_name='Ersteller')
    
    class Meta:
        verbose_name = 'Spieler'
        verbose_name_plural = 'Spieler'
        ordering = ['lastname']
    
    def __unicode__(self):
        return '%s, %s' % (self.lastname, self.firstname)
    
    def __str__(self):
        return '%s, %s' % (self.lastname, self.firstname)
            
    def save(self, *args, **kwargs):
        if not self.id:
            self.date_created = now()
        self.date_updated = now()
        super(Player, self).save(*args, **kwargs)

class Parent(models.Model):
    firstname = models.CharField('Vorname', max_length = 20)
    lastname = models.CharField('Nachname', max_length = 30)
    gender = models.CharField('Geschlecht', max_length = 1, choices = [ ('M', 'Männlich'), ('W', 'Weiblich') ] )
    birthday = models.DateField('Geburtstag', blank = True, null = True)
    address = models.ForeignKey(Address, verbose_name = 'Adresse', related_name = 'parent_address')
    phone_privat = models.CharField('Telefon Privat', max_length = 20, blank = True, null = True)
    phone_handy = models.CharField('Handy', max_length = 20, blank = True, null = True)
    phone_work = models.CharField('Telefon Arbeit', max_length = 20, blank = True, null = True)
    email_privat = models.EmailField('Email Privat', blank = True, null = True)
    email_work = models.EmailField('Email Arbeit', blank = True, null = True)
    email_club = models.EmailField('Email Verein', blank = True, null = True)
    
    is_supporter = models.BooleanField('Mitglied Förderverein', default = False)
    date_supporter = models.DateField('Beitrittsdatum Förderverein', blank = True, null = True)
    
    date_created = models.DateTimeField('Erstellt')
    date_updated = models.DateTimeField('Aktualiert')
    creator = models.ForeignKey(User, verbose_name='Ersteller')
    
    class Meta:
        verbose_name = 'Eltern'
        verbose_name_plural = 'Eltern'
        ordering = ['lastname']
            
    def __unicode__(self):
        return '%s, %s' % (self.lastname, self.firstname)
    
    def __str__(self):
        return '%s, %s' % (self.lastname, self.firstname)
            
    def save(self, *args, **kwargs):
        if not self.id:
            self.date_created = now()
        self.date_updated = now()
        super(Parent, self).save(*args, **kwargs)

class Address(models.Model):
    street = models.CharField('Straße', max_length = 50)
    number = models.CharField('Nummer', max_length = 5, validators = [ RegexValidator(r'^[0-9a-z]*$', 'Keine Hausnummer') ] )
    city = models.CharField('Ort', max_length = 50)
    postal_code = models.CharField('PLZ', max_length = 5, validators = [ RegexValidator(r'^[1-9][0-9]*$', 'Keine Postleitzahl'), MinLengthValidator(5) ] )
    
    class Meta:
        verbose_name = 'Adresse'
        verbose_name_plural = 'Adressen'
        ordering = ['street']
            
    def __unicode__(self):
        return '%s %s, %s %s' % (self.street, self.number, self.postal_code, self.city) 
    
    def __str__(self):
        return '%s %s, %s %s' % (self.street, self.number, self.postal_code, self.city) 

class Club(models.Model):
    club = models.CharField('Verein', max_length = 75)
    
    class Meta:
        verbose_name = 'Verein'
        verbose_name_plural = 'Vereine'
        ordering = ['club']
            
    def __unicode__(self):
        return self.club
    
    def __str__(self):
        return self.club
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, auch dort sind ja wieder mehrere `now()`-Abfragen drin. Wenn auch sicherlich weit unter 100.

Ich habe gelesen, der Raspi holt sich die Uhrzeit nicht von der Hardware, sondern aus dem Internet mittels NTP-Server? Stimmt das? Ich hoffe mal nicht, dass jede Abfrage der aktuellen Uhrzeit den Server befragt, oder? Das wäre irgendwie eine kranke Idee, aber wer weiß... :mrgreen:
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

snafu hat geschrieben:Naja, auch dort sind ja wieder mehrere `now()`-Abfragen drin. Wenn auch sicherlich weit unter 100.

Ich habe gelesen, der Raspi holt sich die Uhrzeit nicht von der Hardware, sondern aus dem Internet mittels NTP-Server? Stimmt das? Ich hoffe mal nicht, dass jede Abfrage der aktuellen Uhrzeit den Server befragt, oder? Das wäre irgendwie eine kranke Idee, aber wer weiß... :mrgreen:
Doch soweit ich weiß, holt sich der RPI die Uhrzeit von einem NTP-Server. Dies kann man aber ggf. auch ausschalten.


Aber ich denke, dass die Uhrzeit kein Problem sein sollte, da diese ja beim 2. Template nicht wirklich verwendet wird.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Dieses ``now()`` aus Django kommt ja von ``datetime.datetime.now()``. Mein RPi liefert 1000 Abfragen in 0.017462015152 Sekunden.

Code: Alles auswählen

0.017462015152
root@raspberrypi:/home/pi/Python#
Edit:
https://docs.djangoproject.com/en/1.7/_ ... ezone/#now

Code: Alles auswählen

def now():
    """
    Returns an aware or naive datetime.datetime, depending on settings.USE_TZ.
    """
    if settings.USE_TZ:
        # timeit shows that datetime.now(tz=utc) is 24% slower
        return datetime.utcnow().replace(tzinfo=utc)
    else:
        return datetime.now()
BlackJack

@snafu: Der Raspi holt sich die Zeit von einem NTP-Server so wie da jeder andere Rechner auch tut: Beim starten. Ab da kümmert sich der Kernel lokal um die Uhrzeit. (Also normalerweise wird NTP regelmässig abgefragt, zum Beispiel einmal am Tag, um eventuell nachzukorrigieren, aber das ist auch auf anderen Systemen so und hat keinen Einfluss darauf wenn die Zeit in einem Programm ermittelt wird.)

@xerion21: Beim DB-Entwurf finde ich übrigens seltsam das `Player` und `Parent` *sehr* viele gemeinsame Felder haben. Das riecht komisch.

Je nach dem was damit abgebiltet werden soll ist Vater und Mutter auch ein bisschen naiv gedacht. Zumindest solange es hier nicht tatsächlich um biologische Eltern geht. Denn ansonsten will man ja beispielsweise oft eher Erziehungsberechtigte/Ansprechpartner haben. Das können zwei Väter sein, oder zwei Mütter, oder die Grosseltern, oder ein biologisches Elternteil und jemand vom Sozialamt, und auch mehr als zwei Personen.

Und bei Adressen würde ich mir fast alle Einschränkungen verkneifen. Es gibt Hausnummern die nur aus Grossbuchstaben bestehen (Haus A) oder römische Ziffern, deine Postleitzahlen können nicht mal alle gültigen, normalen deutschen Postleitzahlen abdecken weil es welche gibt die mit 0 anfangen. Es gibt auch Leute die eine Anschrift im Ausland haben. Und es gibt auch ”besondere” Adressen wie zum Beispiel Feldlager der Bundeswehr im Ausland oder Schiffe.
xerion21
User
Beiträge: 19
Registriert: Freitag 13. März 2015, 16:31

BlackJack hat geschrieben: @xerion21: Beim DB-Entwurf finde ich übrigens seltsam das `Player` und `Parent` *sehr* viele gemeinsame Felder haben. Das riecht komisch.

Je nach dem was damit abgebiltet werden soll ist Vater und Mutter auch ein bisschen naiv gedacht. Zumindest solange es hier nicht tatsächlich um biologische Eltern geht. Denn ansonsten will man ja beispielsweise oft eher Erziehungsberechtigte/Ansprechpartner haben. Das können zwei Väter sein, oder zwei Mütter, oder die Grosseltern, oder ein biologisches Elternteil und jemand vom Sozialamt, und auch mehr als zwei Personen.
Hier geht es wirklich nur um die biologischen Eltern erstmal. Dies wird natürlich noch erweitert.
Dies ist ja auch erstmal der erste Entwurf, bei dem ich mich strikt an meine Vorlage (Excel-Tabelle) gehalten hatte.

Was meinst du mit: riecht komisch?
BlackJack hat geschrieben: Und bei Adressen würde ich mir fast alle Einschränkungen verkneifen. Es gibt Hausnummern die nur aus Grossbuchstaben bestehen (Haus A) oder römische Ziffern, deine Postleitzahlen können nicht mal alle gültigen, normalen deutschen Postleitzahlen abdecken weil es welche gibt die mit 0 anfangen. Es gibt auch Leute die eine Anschrift im Ausland haben. Und es gibt auch ”besondere” Adressen wie zum Beispiel Feldlager der Bundeswehr im Ausland oder Schiffe.
[/quote]

Auch hier habe ich mich erstmal an den angegeben Adressen orientiert. Das mit der PLZ habe ich erstmal nicht wirklich bedacht, sondern einfach nur an die PLZ aus meiner Umgebung gedacht, da diese Software auch nur dort eingesetzt werden soll.
Solche besonderen Adressen wie Feldlager oder so wird es nicht geben ;)
Anschriften im Ausland werden erstmal auch nicht berücksichtigt, da dies keine Anforderung ist/war (Anforderung sind nur Adressen des lokalen Umfeldes)
Antworten