webentwicklung-frage-antwort-db.com.de

Wie führe ich Djangos Testdatenbank nur im Speicher aus?

Meine Django Komponententests dauern sehr lange, daher suche ich nach Möglichkeiten, dies zu beschleunigen. Ich erwäge die Installation einer SSD , aber ich weiß, dass dies auch Nachteile hat. Natürlich gibt es Dinge, die ich mit meinem Code tun könnte, aber ich suche nach einer strukturellen Lösung. Selbst das Ausführen eines einzelnen Tests ist langsam, da die Datenbank dies benötigt jedes Mal wieder aufgebaut/nach Süden migriert werden. Also hier ist meine Idee ...

Da ich weiß, dass die Testdatenbank immer recht klein ist, kann ich das System nicht so konfigurieren, dass immer die gesamte Testdatenbank im RAM bleibt. Berühren Sie niemals die Scheibe. Wie konfiguriere ich das in Django? Ich würde es vorziehen, weiterhin MySQL zu verwenden, da dies das ist, was ich in der Produktion verwende, aber wenn SQLite 3 oder etwas anderes dies einfach macht, würde ich diesen Weg gehen.

Verfügt SQLite oder MySQL über eine Option, um vollständig im Arbeitsspeicher ausgeführt zu werden? Es sollte möglich sein, eine RAM Disk zu konfigurieren und dann die Testdatenbank so zu konfigurieren, dass ihre Daten dort gespeichert werden, aber ich bin nicht sicher, wie ich Django/MySQL verwendet ein anderes Datenverzeichnis für eine bestimmte Datenbank, insbesondere weil es bei jedem Durchlauf gelöscht und neu erstellt wird. (Ich bin auf einem Mac FWIW.)

116
Leopd

Wenn Sie Ihr Datenbankmodul bei der Ausführung Ihrer Tests auf sqlite3 setzen, Django verwendet eine speicherinterne Datenbank .

Ich verwende Code wie diesen in meinem settings.py, um die Engine beim Ausführen meiner Tests auf SQLite zu setzen:

if 'test' in sys.argv:
    DATABASE_ENGINE = 'sqlite3'

Oder in Django 1.2:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'sqlite3'}

Und schließlich in Django 1.3 und 1.4:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'Django.db.backends.sqlite3'}

(Der vollständige Pfad zum Backend ist bei Django 1.3 nicht unbedingt erforderlich, macht die Einstellung jedoch vorwärtskompatibel.)

Sie können auch die folgende Zeile hinzufügen, falls Sie Probleme mit Südmigrationen haben:

    SOUTH_TESTS_MIGRATE = False
155
Etienne

Normalerweise erstelle ich eine separate Einstellungsdatei für Tests und verwende sie im Testbefehl, z.

python manage.py test --settings=mysite.test_settings myapp

Es hat zwei Vorteile:

  1. Sie müssen nicht nach test oder einem solchen Zauberwort in sys.argv suchen, test_settings.py Kann einfach sein

    from settings import *
    
    # make tests faster
    SOUTH_TESTS_MIGRATE = False
    DATABASES['default'] = {'ENGINE': 'Django.db.backends.sqlite3'}
    

    Sie können es aber auch an Ihre Anforderungen anpassen und die Testeinstellungen sauber von den Produktionseinstellungen trennen.

  2. Ein weiterer Vorteil ist, dass Sie Tests mit der Produktionsdatenbank-Engine ausführen können, anstatt sqlite3 zu verwenden, um subtile Fehler zu vermeiden

    python manage.py test --settings=mysite.test_settings myapp
    

    und vor dem Festschreiben des Codes einmal ausführen

    python manage.py test myapp
    

    nur um sicherzugehen, dass alle Tests wirklich bestanden haben.

80
Anurag Uniyal

MySQL unterstützt eine Speicher-Engine namens "MEMORY", die Sie in Ihrer Datenbankkonfiguration (settings.py) Folgendermaßen konfigurieren können:

    'USER': 'root',                      # Not used with sqlite3.
    'PASSWORD': '',                  # Not used with sqlite3.
    'OPTIONS': {
        "init_command": "SET storage_engine=MEMORY",
    }

Beachten Sie, dass die MEMORY-Speicher-Engine keine Blob-/Textspalten unterstützt. Wenn Sie also Django.db.models.TextField Verwenden, funktioniert dies nicht für Sie.

22
muudscope

Ich kann Ihre Hauptfrage nicht beantworten, aber es gibt ein paar Dinge, die Sie tun können, um die Dinge zu beschleunigen.

Stellen Sie zunächst sicher, dass Ihre MySQL-Datenbank für die Verwendung von InnoDB eingerichtet ist. Dann kann es Transaktionen verwenden, um den Status der Datenbank vor jedem Test zurückzusetzen, was meiner Erfahrung nach zu einer massiven Beschleunigung geführt hat. Sie können einen Datenbank-Init-Befehl in Ihrer settings.py übergeben (Django 1.2-Syntax):

DATABASES = {
    'default': {
            'ENGINE':'Django.db.backends.mysql',
            'Host':'localhost',
            'NAME':'mydb',
            'USER':'whoever',
            'PASSWORD':'whatever',
            'OPTIONS':{"init_command": "SET storage_engine=INNODB" } 
        }
    }

Zweitens müssen Sie die Südmigrationen nicht jedes Mal durchführen. Einstellen SOUTH_TESTS_MIGRATE = False in Ihrer settings.py und die Datenbank wird mit einfachem syncdb erstellt, was viel schneller ist als das Durchlaufen aller historischen Migrationen.

15
Daniel Roseman

Sie können doppelte Optimierungen vornehmen:

  • verwenden Sie Transaktionstabellen: Der anfängliche Fixtures-Status wird nach jedem TestCase mit Hilfe des Datenbank-Rollbacks festgelegt.
  • legen Sie Ihr Datenbank-Datenverzeichnis auf die Ramdisk: Sie werden viel gewinnen, was die Datenbankerstellung betrifft, und auch das Ausführen von Tests wird schneller sein.

Ich benutze beide Tricks und bin ziemlich glücklich.

So richten Sie es für MySQL unter Ubuntu ein:

$ Sudo service mysql stop
$ Sudo cp -pRL /var/lib/mysql /dev/shm/mysql

$ vim /etc/mysql/my.cnf
# datadir = /dev/shm/mysql
$ Sudo service mysql start

Achtung, es ist nur zum Testen, nach dem Neustart geht Ihre Datenbank aus dem Speicher verloren!

10
Potr Czachur

Ein anderer Ansatz: Lassen Sie eine andere Instanz von MySQL in einem tempfs laufen, das eine RAM Disk verwendet. Anweisungen in diesem Blogeintrag: Beschleunigen von MySQL für Testen in Django .

Vorteile:

  • Sie verwenden genau die Datenbank, die Ihr Produktionsserver verwendet
  • sie müssen Ihre Standard-MySQL-Konfiguration nicht ändern
3
neves

Ausgehend von der Antwort von Anurag habe ich den Prozess vereinfacht, indem ich dieselben test_settings erstellt und Folgendes zu manage.py hinzugefügt habe

if len(sys.argv) > 1 and sys.argv[1] == "test":
    os.environ.setdefault("Django_SETTINGS_MODULE", "mysite.test_settings")
else:
    os.environ.setdefault("Django_SETTINGS_MODULE", "mysite.settings")

scheint sauberer zu sein, da sys bereits importiert ist und manage.py nur über die Befehlszeile verwendet wird, sodass keine Einstellungen überladen werden müssen

2
Alvin