В Google App Engine, как уменьшить потребление памяти, когда я записываю файл в хранилище, а не превышать предел мягкой памяти? - PullRequest
9 голосов
/ 03 февраля 2012

Я использую blobstore для резервного копирования и восстановления объектов в формате csv.Процесс работает хорошо для всех моих маленьких моделей.Однако, как только я начинаю работать на моделях с более чем 2K объектами, у меня превышен предел мягкой памяти.Я получаю только 50 сущностей за раз, а затем записываю результаты в blobstore, так что мне не ясно, почему будет увеличиваться использование моей памяти.Я могу надежно отключить метод, просто увеличив значение «limit», переданное ниже, что приводит к тому, что метод работает немного дольше, чтобы экспортировать еще несколько сущностей.

  1. Есть ли какие-либо рекомендации по оптимизации этого процесса для сокращения потребления памяти?

  2. Кроме того, размер создаваемых файлов будет <500 КБ.Зачем процессу использовать 140 МБ памяти?</p>

Упрощенный пример:

file_name = files.blobstore.create(mime_type='application/octet-stream')
with files.open(file_name, 'a') as f:
    writer = csv.DictWriter(f, fieldnames=properties)
    for entity in models.Player.all():
      row = backup.get_dict_for_entity(entity)
      writer.writerow(row)

Выдает ошибку: Превышено ограничение мягкой частной памяти с 150,957 МБ после обслуживания всего 7 запросов

Упрощенный пример 2:

Проблема, похоже, связана с использованием файлов и оператором with в Python 2.5.Не обращая внимания на материал csv, я могу воспроизвести почти ту же ошибку, просто пытаясь записать текстовый файл размером 4000 строк в blobstore.

from __future__ import with_statement
from google.appengine.api import files
from google.appengine.ext.blobstore import blobstore
file_name = files.blobstore.create(mime_type='application/octet-stream')   
myBuffer = StringIO.StringIO()

#Put 4000 lines of text in myBuffer

with files.open(file_name, 'a') as f:
    for line in myBuffer.getvalue().splitlies():
        f.write(line)

files.finalize(file_name)  
blob_key = files.blobstore.get_blob_key(file_name)

Выдает ошибку: Превышен предел мягкой частной памяти с 154,977 МБ после обслуживания всего 24 запросов

Оригинал:

def backup_model_to_blobstore(model, limit=None, batch_size=None):
    file_name = files.blobstore.create(mime_type='application/octet-stream')
    # Open the file and write to it
    with files.open(file_name, 'a') as f:
      #Get the fieldnames for the csv file.
      query = model.all().fetch(1)
      entity = query[0]
      properties = entity.__class__.properties()
      #Add ID as a property
      properties['ID'] = entity.key().id()

      #For debugging rather than try and catch
      if True:
        writer = csv.DictWriter(f, fieldnames=properties)
        #Write out a header row
        headers = dict( (n,n) for n in properties )
        writer.writerow(headers)

        numBatches = int(limit/batch_size)
        if numBatches == 0:
            numBatches = 1

        for x in range(numBatches):
          logging.info("************** querying with offset %s and limit %s", x*batch_size, batch_size)
          query = model.all().fetch(limit=batch_size, offset=x*batch_size)
          for entity in query:
            #This just returns a small dictionary with the key-value pairs
            row = get_dict_for_entity(entity)
            #write out a row for each entity.
            writer.writerow(row)

    # Finalize the file. Do this before attempting to read it.
    files.finalize(file_name)

    blob_key = files.blobstore.get_blob_key(file_name)
    return blob_key

Ошибка выглядит следующим образомбревна

......
2012-02-02 21:59:19.063
************** querying with offset 2050 and limit 50
I 2012-02-02 21:59:20.076
************** querying with offset 2100 and limit 50
I 2012-02-02 21:59:20.781
************** querying with offset 2150 and limit 50
I 2012-02-02 21:59:21.508
Exception for: Chris (202.161.57.167)

err:
Traceback (most recent call last):
  .....
    blob_key = backup_model_to_blobstore(model, limit=limit, batch_size=batch_size)
  File "/base/data/home/apps/singpath/163.356548765202135434/singpath/backup.py", line 125, in backup_model_to_blobstore
    writer.writerow(row)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/files/file.py", line 281, in __exit__
    self.close()
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/files/file.py", line 275, in close
    self._make_rpc_call_with_retry('Close', request, response)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/files/file.py", line 388, in _make_rpc_call_with_retry
    _make_call(method, request, response)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/files/file.py", line 236, in _make_call
    _raise_app_error(e)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/api/files/file.py", line 179, in _raise_app_error
    raise FileNotOpenedError()
FileNotOpenedError

C 2012-02-02 21:59:23.009
Exceeded soft private memory limit with 149.426 MB after servicing 14 requests total

Ответы [ 4 ]

3 голосов
/ 10 февраля 2012

In Как правильно записать в Blobstore Google App Engine в виде файла в Python 2.5 была обнаружена похожая проблема.В ответе предлагается, чтобы вы иногда пытались вставлять вызовы gc.collect ().Учитывая то, что я знаю о реализации API файлов, я думаю, что это точно.Попробуйте!

3 голосов
/ 04 февраля 2012

Вам лучше не делать пакетирование самостоятельно, а просто повторять запрос. Итератор выберет размер пакета (вероятно, 20), который должен быть адекватным:

q = model.all()
for entity in q:
    row = get_dict_for_entity(entity)
    writer.writerow(row)

Это позволяет избежать повторного выполнения запроса с постоянно увеличивающимся смещением, которое является медленным и вызывает квадратичное поведение в хранилище данных.

Часто упускаемый из виду факт использования памяти заключается в том, что представление объекта в памяти может использовать ОЗУ в 30-50 раз по сравнению с сериализованной формой объекта; например объект объемом 3 КБ на диске может использовать 100 КБ в ОЗУ. (Точный фактор увеличения зависит от многих факторов; хуже, если у вас много свойств с длинными именами и небольшими значениями, и еще хуже для повторных свойств с длинными именами.)

2 голосов
/ 04 февраля 2012

Это может быть ошибка превышения времени из-за ограничения запроса до 30 секунд. В моей реализации, чтобы обойти это вместо того, чтобы иметь обработчик webapp для операции, я запускаю событие в очереди по умолчанию. Крутая вещь в очереди состоит в том, что для ее вызова требуется одна строка кода, у нее есть 10-минутное ограничение по времени, и если задача не выполняется, она повторяется раньше установленного срока. Я не совсем уверен, решит ли это вашу проблему, но стоит попробовать.

from google.appengine.api import taskqueue
...
taskqueue.add("the url that invokes your method")

Вы можете найти больше информации об очередях здесь .

Или рассмотрите возможность использования бэкэнда для серьезных вычислений и файловых операций.

1 голос
/ 03 февраля 2012

Я не могу говорить об использовании памяти в Python, но, учитывая ваше сообщение об ошибке, ошибка, скорее всего, связана с тем, что файл в хранилище BLOB-хранилища в GAE не может быть открыт более 30 секунд , поэтому вам придется периодически закрывать и открывать его, если ваша обработка занимает больше времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...