Wie generiere ich große Dateien (PDF und CSV) mit AppEngine und Datastore?

8

Als ich mit der Entwicklung dieses Projekts anfing, gab es keine Notwendigkeit, große Dateien zu erzeugen, aber jetzt ist es ein Ergebnis.

Lange Rede, kurzer Sinn: GAE spielt einfach nicht mit großen Datenmanipulationen oder der Generierung von Inhalten. Abgesehen von der Tatsache, dass keine Dateispeicher vorhanden sind, scheint selbst etwas so Einfaches wie das Erzeugen einer PDF mit ReportLab mit 1500 Datensätzen einen DeadlineExceededError zu treffen. Dies ist nur eine einfache PDF aus einer Tabelle.

Ich verwende den folgenden Code:

%Vor%

Nichts besonders Besonderes, aber es erstickt. Gibt es einen besseren Weg, dies zu tun? Wenn ich in irgendeine Art von Dateisystem schreiben und die Datei in Bits erzeugen könnte, und dann wieder beitreten könnte, die funktionieren könnte, aber ich denke, das System schließt dies aus.

Ich muss das Gleiche für eine CSV-Datei tun, aber das Limit ist offensichtlich ein bisschen höher, da es nur Rohausgabe ist.

%Vor%

Irgendwelche Vorschläge würden sehr geschätzt werden.

Bearbeiten:

Oben hatte ich drei mögliche Lösungen basierend auf meinen Recherchen, Vorschlägen usw. dokumentiert.

Sie schließen sich nicht unbedingt gegenseitig aus und könnten eine geringfügige Variation oder Kombination von irgendwelchen der drei sein, aber der Kern der Lösungen ist da. Lass mich wissen, welche deiner Meinung nach am meisten Sinn macht und vielleicht die beste Leistung bringt.

Lösung A: Verwenden Sie mapreduce (oder tasks), serialisieren Sie jeden Datensatz und erstellen Sie einen Memcache-Eintrag für jeden einzelnen Datensatz, der mit dem Schlüsselnamen codiert ist. Verarbeiten Sie diese Elemente dann einzeln in die pdf / xls-Datei. (Verwenden Sie get_multi und set_multi)

Lösung B: Verwenden Sie Aufgaben, serialisieren Sie Gruppen von Datensätzen und laden Sie sie als Blob in die Datenbank. Dann lösen Sie eine Aufgabe aus, sobald alle Datensätze verarbeitet sind, die jeden Blob laden, deserialisieren und dann die Daten in die endgültige Datei laden.

Lösung C: Verwenden Sie Mapreduce, rufen Sie die Schlüsselnamen ab und speichern Sie sie als Liste oder serialisiertes Blob. Dann laden Sie die Datensätze per Schlüssel, was schneller wäre als die aktuelle Ladeart. Wenn ich dies tun würde, was wäre besser, sie als Liste zu speichern (und was wären die Einschränkungen ... ich nehme an, eine Liste von 100.000 würde die Fähigkeiten des Datenspeichers übersteigen) oder als serialisiertes Blob (oder klein Stücke, die ich dann verkette oder verarbeite)

Vielen Dank im Voraus für einen Rat.

    
etc 15.10.2010, 01:34
quelle

2 Antworten

3

Hier ist ein kurzer Gedanke, vorausgesetzt, es wird aus dem Datenspeicher abgerufen. Sie können Aufgaben und Cursor , um die Daten in kleineren Blöcken zu holen, dann die Generierung am Ende.

Starten Sie eine Aufgabe, die die anfängliche Abfrage durchführt und 300 Datensätze (beliebige Anzahl) abruft, und reiht dann eine benannte (! wichtige) Aufgabe in die Warteschlange ein, an die Sie den Cursor übergeben. Dieser wiederum fragt [Ihre beliebige Nummer] Datensätze ab und übergibt dann den Cursor an eine neue benannte Aufgabe. Fahren Sie fort, bis Sie genug Datensätze haben.

Innerhalb jeder Aufgabe verarbeiten Sie die Entitäten und speichern dann das serialisierte Ergebnis in einer Text- oder Blob-Eigenschaft in einem 'Verarbeitungsmodell'. Ich würde das key_name des Modells dasselbe wie die Aufgabe machen, die es erzeugte. Beachten Sie, dass die serialisierten Daten unter der Größenbeschränkung für API-Aufrufe liegen müssen.

Um Ihre Tabelle ziemlich schnell serialisieren zu können, könnten Sie:

verwenden %Vor%

Haben Sie die letzte Aufgabe (wenn Sie genug Datensätze haben) Kick der PDf oder CSV-Generation. Wenn Sie key_names für Ihre Modelle verwenden, sollten Sie in der Lage sein, alle Entitäten mit codierten Daten per Schlüssel zu erfassen. Fetchings per Schlüssel sind ziemlich schnell, Sie werden die Schlüssel des Modells kennen, da Sie den letzten Tasknamen kennen. Auch hier sollten Sie die Größe Ihrer Abrufe im Datenspeicher berücksichtigen!

Um zu deserialisieren:

%Vor%

Führen Sie nun Ihre PDF / CSV-Generierung für die Daten aus. Wenn das Aufteilen der Datenspeicherabrufzugriffe alleine nicht hilft, müssen Sie mehr Verarbeitungsvorgänge in den einzelnen Aufgaben ausführen.

Vergessen Sie nicht, dass Sie in der Aufgabe 'build' eine Exception auslösen wollen, wenn eines der Interim-Modelle noch nicht vorhanden ist. Ihre letzte Aufgabe wird automatisch erneut versuchen.

    
Robert Kluin 15.10.2010, 02:49
quelle
1

Vor einiger Zeit hatte ich das gleiche Problem mit GAE. Nach vielen Versuchen bin ich einfach zu einem anderen Webhosting umgezogen, da ich es geschafft habe. Bevor ich umzog, hatte ich noch zwei Ideen, wie ich es lösen könnte. Ich habe sie nicht implementiert, aber Sie können es versuchen.

Die erste Idee ist, SOA / RESTful Service auf einem anderen Server zu verwenden, wenn dies der Fall ist möglich. Sie können sogar eine andere Anwendung auf GAE in Java erstellen, die ganze Arbeit dort erledigen (ich denke, mit Javas PDFBox ) wird es viel weniger Zeit benötigen erzeugt PDF) und gibt das Ergebnis an Python zurück. Aber diese Option erfordert, dass Sie Java kennen und auch Ihre App in mehrere Teile mit schrecklicher Modularität teilen.

Also gibt es einen anderen Ansatz: Sie können ein " ping-pong " Spiel mit dem Browser eines Benutzers erstellen. Die Idee ist, dass, wenn Sie nicht alles in einer einzigen Anfrage machen können, zwingen Sie den Browser, Ihnen mehrere zu senden. Machen Sie während der ersten Anfrage nur einen Teil der Arbeit, der 30 Sekunden Grenze entspricht, speichern Sie dann den Status und generieren Sie "Ticket" - eindeutige Kennung eines "Jobs". Schließlich senden Sie die Benutzerantwort, die einfache Seite mit Redirect zurück zu Ihrer App ist, durch ein Jobticket parametrisiert. Wenn du es bekommst. einfach den Status wiederherstellen und mit dem nächsten Teil des Jobs fortfahren.

    
ffriend 15.10.2010 02:39
quelle