Google AppEngine で Python 2.7 を使用して、パッチを適用したバイナリ ファイルをクライアントに提供するには、次のようにします。
- BlobReader を使用して、ブロブストアから元のファイルを読み取る
- BlobReader からパッチ ポイントまでのデータを出力する
- パッチされたバイトを出力する
- 元のデータの残りを出力する
システムは動作しますが、リクエストのたびに、現在のインスタンスによって使用されるメモリが BLOB のサイズの 2 倍増加するため、どこかにメモリ リークがあるようです。たとえば、BLOB が 8 MB の場合、リクエスト後にインスタンスは 16 MB 増加します。その結果、そのようなリクエストが数回行われただけで、インスタンスは GAE によって強制的に終了され、「ソフト プライベート メモリ制限を超えました」というエラー メッセージが表示されます。
メモリ リークを生成するコードの短縮バージョンを次に示します。
class CurrentVer(db.Expando):
"singleton entity with reference to the blob and starting position of the patch"
patch_start = db.IntegerProperty()
blob = blobstore.BlobReferenceProperty()
def out_blob(handler):
# prepare a patch string
patch_string = "<CONFIG><A>A316</A></CONFIG>"
# read blob key and starting position of patch
CurrVer = CurrentVer.get_by_key_name("CurrentVer")
patch_start = CurrVer.patch_start
blob_key = str(CurrVer.blob.key())
CurrVer = None
# open BlobReader object with 1MB buffer, as shown in GAE docs:
blob_reader = blobstore.BlobReader(blob_key, buffer_size=1048576)
# read original data until patch point, and output it:
handler.response.out.write(blob_reader.read(patch_start))
# output patch bytes
handler.response.out.write(patch_string)
# skip the bytes that have been patched
blob_reader.seek(patch_start + len(patch_string))
# read remaining original data until the end, and output it
handler.response.out.write(blob_reader.read())
blob_reader.close()
blob_reader = None
gc.collect()
def gc_collect(webapp2.RequestHandler):
def get(self):
gc.collect()
self.response.out.write("<html><body>Called cg.collect()</body></html>")
class patch_blob(webapp2.RequestHandler):
def get(self):
out_blob(self)
app = webapp2.WSGIApplication([('/patch_blob.*', patch_blob),
('/gc_collect.*', gc_collect)],
debug=True)
コードが実行されると (URL = /patch_blob)、次のように、リクエストごとにインスタンスによって使用されるメモリが BLOB のサイズの 2 倍に増加します。
リクエスト 1: 55 MB
リクエスト 2: 73 MB
リクエスト 3: 90 MB
...など、ログに次のエラーが表示されるまで:
(c) 合計 5 つの要求を処理した後、140.504 MB でソフト プライベート メモリ制限を超えました
(w) このリクエストを処理した後、このリクエストを処理したプロセスがメモリを大量に使用していることが判明したため、終了しました。これにより、アプリケーションへの次のリクエストに新しいプロセスが使用される可能性があります。このメッセージが頻繁に表示される場合は、アプリケーションでメモリ リークが発生している可能性があります。
何が起こっているのか、そしてひどいメモリリークのように見えるものを回避する方法を誰か説明できますか? (幸いなことに、GAE はエラーが発生する前に最後のリクエストを処理しますが、これは望ましくないことだと確信しており、回避したいと考えています...)
ありがとう!!!