abbiamo la necessità di esportare un file CSV comprendente i dati dal modello da admin Django che gira su Heroku. Pertanto abbiamo creato un'azione in cui abbiamo creato il CSV e lo abbiamo restituito nella risposta. Questo ha funzionato fino a quando il nostro cliente ha iniziato a esportare enormi serie di dati e ci siamo imbattuti nel timeout di 30 secondi del web worker.Esportazione CSV in Stream (da Django admin su Heroku)
Per ovviare a questo problema abbiamo pensato di eseguire lo streaming di csv al client invece di crearlo prima in memoria e inviarlo in un unico pezzo. Trigger era questa informazione:
Cedar supporta il polling lungo e le risposte di streaming. La tua app ha una finestra iniziale di 30 secondi per rispondere con un singolo byte al client. Dopo ogni byte inviato (ricevuto da> il client o inviato dall'applicazione) si resetta una finestra di 55 secondi a rotazione. Se nessun dato è> inviato durante la finestra di 55 secondi, la connessione verrà interrotta.
Abbiamo quindi implementato qualcosa di simile a questo per provarlo:
import cStringIO as StringIO
import csv, time
def csv(request):
csvfile = StringIO.StringIO()
csvwriter = csv.writer(csvfile)
def read_and_flush():
csvfile.seek(0)
data = csvfile.read()
csvfile.seek(0)
csvfile.truncate()
return data
def data():
for i in xrange(100000):
csvwriter.writerow([i,"a","b","c"])
time.sleep(1)
data = read_and_flush()
yield data
response = HttpResponse(data(), mimetype="text/csv")
response["Content-Disposition"] = "attachment; filename=test.csv"
return response
L'header HTTP del download si presenta così (da Firebug):
HTTP/1.1 200 OK
Cache-Control: max-age=0
Content-Disposition: attachment; filename=jobentity-job2.csv
Content-Type: text/csv
Date: Tue, 27 Nov 2012 13:56:42 GMT
Expires: Tue, 27 Nov 2012 13:56:41 GMT
Last-Modified: Tue, 27 Nov 2012 13:56:41 GMT
Server: gunicorn/0.14.6
Vary: Cookie
Transfer-Encoding: chunked
Connection: keep-alive
"Transfer-Encoding : Chunked "indica che Cedar sta effettivamente trasmettendo il contenuto in chunkwise che indoviniamo.
Il problema è che il download del CSV è ancora interrotta dopo 30 secondi con queste righe nel registro Heroku:
2012-11-27T13:00:24+00:00 app[web.1]: DEBUG: exporting tasks in csv-stream for job id: 56,
2012-11-27T13:00:54+00:00 app[web.1]: 2012-11-27 13:00:54 [2] [CRITICAL] WORKER TIMEOUT (pid:5)
2012-11-27T13:00:54+00:00 heroku[router]: at=info method=POST path=/admin/jobentity/ host=myapp.herokuapp.com fwd= dyno=web.1 queue=0 wait=0ms connect=2ms service=29480ms status=200 bytes=51092
2012-11-27T13:00:54+00:00 app[web.1]: 2012-11-27 13:00:54 [2] [CRITICAL] WORKER TIMEOUT (pid:5)
2012-11-27T13:00:54+00:00 app[web.1]: 2012-11-27 13:00:54 [12] [INFO] Booting worker with pid: 12
Questo dovrebbe funzionare concettualmente, giusto? C'è qualcosa che ci è mancato?
Abbiamo davvero apprezzato il vostro aiuto. Tom
Ma non dovrebbe la finestra di 30 secondi di timeout essere esteso perché abbiamo contenuto streaming, invece di aspettare fino a quando il csv è stato creato in memoria? Quindi ci sono byte trasmessi in questa finestra di 30 secondi e questo dovrebbe evitare il timeout in questo modo: Cedar supporta le caratteristiche HTTP 1.1 come polling a lungo e le risposte di streaming. Un'applicazione ha una finestra iniziale di 30 secondi per rispondere con un singolo byte al client. Tuttavia, ogni byte trasmesso successivamente ripristina una finestra di 55 secondi a rotazione. – Tom
Potrebbe darsi che Django abbia un timeout interno durante l'invio di una risposta? – Tom
Hai la tua richiesta web che funziona per più di 30 secondi - questo è un dato di fatto, e Heroku ha un timeout predefinito di 30 secondi per qualsiasi richiesta web nella sua configurazione del server http. Suppongo che il tentativo di emulare la sessione keepalive non abbia esito positivo: è meglio prendere in considerazione lo spostamento dell'elaborazione di file lunghi in processo/daemon in background. – moonsly