同様の問題に直面しました。Sinatra アプリには基本的に「一括ダウンロード」ページがあり、クライアント アプリはこれを呼び出して、ブラウザーのローカル webSQL データベースにデータをインポートします。
ダウンロード ページ (「GET /download」と呼びます) は、CouchDB データベースにクエリを実行して、指定されたビュー内のすべてのドキュメントを取得します。プロセスのこの部分 (クエリと同様) に長い時間がかかります。
Sinatra のストリーミング API を使用して、この問題を回避することができました。前述のように、Heroku の時計は、30 秒が経過する前に応答で少なくとも 1 つの by を送信することで「リセット」できます。これにより、クロックがさらに 55 秒間リセットされます (さらに送信するバイトごとにクロックが再度リセットされます)。これにより、データを送信している間、接続を無期限に開いたままにすることができます。
シナトラでは、これを置き換えることができました:
get '/download' do
body = db.view 'data/all'
end
..次のようなもので:
get '/download' do
# stream is a Sinatra helper that effectively does 'chunked transfer encoding'
stream do |out|
# Long query starts here
db.view 'data/all' do |row|
# As each row comes back from the db, stream it to the front-end
out << row
end
end
end
「最初のバイトまでの時間」(つまり、dbクエリが最初の行を返すのにかかる時間は30秒の制限をはるかに下回っているため、これは私にとってはうまく機能します。
唯一の欠点は、以前はすべての結果をデータベースから Sinatra アプリに戻してから、結果全体の MD5 合計を計算して etag ヘッダーとして使用していたことです。クライアント アプリはこれを使用して、条件付きの HTTP get を実行できます (つまり、再度ダウンロードしようとしてデータが変更されていない場合は、302 Not Modified を送信できます)。さらに、受信したデータの独自のチェックサムと比較できます (転送中に破損/変更されていないことを確認するため)。
応答でデータのストリーミングを開始すると、HTTP ヘッダーとして送信するコンテンツの MD5 合計を計算できなくなります (まだすべてのデータを取得していないためです。本文の送信を開始しました)。
これをある種のページ分割された複数の AJAX 呼び出し、ソリューションに変更することを考えています。Zenphが上で提案したように。または、ある種のワーカー プロセス (Resque、DelayedJob など) を使用してクエリをオフロードします。しかし、データを取得する準備ができたときにクライアントに通知する方法がわかりません。
お役に立てれば。