2012-11-27 8 views
8

私たちは、Herokuで実行されるDjango adminのモデルからのデータを含むcsvファイルをエクスポートする必要があります。そのため、私たちはcsvを作成してレスポンスで返すアクションを作成しました。これは、クライアントが大量のデータをエクスポートし始めてから、Webワーカーの30秒のタイムアウトに達するまでうまくいった。ストリームでのCSVエクスポート(HerokuのDjango管理者から)

この問題を回避するために、私たちはcsvをメモリに最初に構築し、それを1つの部分に送信するのではなく、クライアントにストリーミングすることを考えました。トリガーはこの情報でした。

シーダーはロングポーリングとストリーミング応答をサポートしています。あなたのアプリは最初の30秒のウィンドウ を持っています。送信された各バイト(クライアントから受信されるか、またはアプリケーションによって送信される)の後、ローリング55秒のウィンドウをリセットします。 55秒間にデータが送信されなければ、接続は終了します。

したがって、我々はそれをテストするには、このようなものに実装:

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 

「転送エンコード:ダウンロードのHTTPヘッダーは、この(放火犯から)のように見えます

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 

を:chunked "は、Cedarが実際にコンテンツをチャンク単位でストリーミングしていることを示します。

問題は、csvファイルのダウンロードがまだHerokuのログに次の行と30秒後に中断されたということです。

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 

これは概念的に動作するはずです、右?私たちが見逃したことはありますか?

本当にありがとうございます。 Tom

答えて

6

問題の解決方法が見つかりました。それはHerokuタイムアウトではありません。そうでなければ、HerokuログにH12タイムアウトがあるからです(HerokuのCaioがそれを指摘してくれたため)。

問題は、Gunicornのデフォルトのタイムアウトが30秒だったことです。 Procfile(Gunicornの行にある)に--timeout 600を加えた後、問題はなくなりました。

Procfileは次のようになります。

web: gunicorn myapp.wsgi -b 0.0.0.0:$PORT --timeout 600 
celeryd: python manage.py celeryd -E -B --loglevel=INFO 
0

これはあなたのスクリプトの問題ではなく、30秒間のWebリクエストのデフォルトのHerokuタイムアウトの問題です。 あなたはこれを読むことができます: https://devcenter.heroku.com/articles/request-timeout とこの文書に従って - CSVエクスポートをバックグラウンドプロセスに移動します。

+0

しかし、私たちが代わりにCSVファイルをメモリ内に作成されるまで待機しているのコンテンツをストリーミングので、30秒のタイムアウトウィンドウが拡張されるべきではないのですか?したがって、この30秒のウィンドウで送信されるバイト数は、これに応じてタイムアウトを回避する必要があります。 Cedarはロングポーリングやストリーミング応答などのHTTP 1.1機能をサポートしています。アプリケーションには、最初の30秒のウィンドウがあり、クライアントに返す単一のバイトで応答します。しかし、その後に送信される各バイトはローリング55秒のウィンドウをリセットする。 – Tom

+0

応答を送信するときにDjangoが内部タイムアウトを持つことはありますか? – Tom

+0

あなたのウェブリクエストは30秒以上働いています。それは事実です。Herokuはhttpサーバーの設定で、どのWebリクエストに対しても30秒のデフォルトタイムアウトを持っています。私は、キープアライブセッションをエミュレートしようとすると成功しないだろうと考えています。長いファイル処理をバックグラウンドプロセス/デーモンに移行することを検討してください。 – moonsly

関連する問題