Запросы NDB превышают лимит частной памяти GAE Soft

В настоящее время у меня есть приложение, работающее в стандартной среде Google App Engine, которое, среди прочего, содержит большую базу данных данных о погоде и конечную точку внешнего интерфейса, которая генерирует график этих данных. База данных находится в Google Cloud Datastore, и приложение Python Flask получает к ней доступ через библиотеку NDB.

Моя проблема заключается в следующем: когда я пытаюсь создать графики для WeatherData, охватывающие более недели (данные сохраняются каждые 5 минут), мое приложение превышает предел мягкой частной памяти GAE и аварийно завершает работу. Однако в каждом из моих объектов WeatherData хранятся соответствующие поля, которые я хочу отобразить в виде графика, в дополнение к очень большой строке json, содержащей данные прогноза, которые мне не нужны для этого графического приложения. Таким образом, часть сущностей WeatherData, из-за которой мое приложение превышает лимит мягкой частной памяти, в этом приложении даже не нужна.

Таким образом, мой вопрос заключается в следующем: есть ли способ запросить только определенные свойства в сущности, например, для определенных столбцов в запросе в стиле SQL? Опять же, мне не нужна вся строка json прогноза для построения графика, только несколько других полей, хранящихся в объекте. Другой подход, который я пытался использовать, заключался в том, чтобы извлекать только пару сущностей за раз и разбивать запрос на несколько вызовов API, но в итоге это заняло так много времени, что время ожидания страницы истекло, и я не мог заставить его работать. правильно.

Ниже приведен мой код того, как он в настоящее время реализован и ломается. Любой вклад высоко ценится:

wDataCsv = 'Time,' + ','.join(wData.keys())
qry = WeatherData.time_ordered_query(ndb.Key('Location', loc),start=start_date,end=end_date)
for acct in qry.fetch():
    d = [acct.time.strftime(date_string)]
    for attr in wData.keys():
        d.append(str(acct.dict_access(attr)))
        wData[attr].append([acct.time.strftime(date_string),acct.dict_access(attr)])
    wDataCsv += '\\n' + ','.join(d)

# Children Entity - log of a weather at parent location
class WeatherData(ndb.Model):
    # model for data to save
    ...
    # Function for querying data below a given ancestor between two optional
    # times
    @classmethod
    def time_ordered_query(cls, ancestor_key, start=None, end=None):
        return cls.query(cls.time>=start, cls.time<=end,ancestor=ancestor_key).order(-cls.time)

РЕДАКТИРОВАТЬ: я попробовал итеративную стратегию выборки страниц, описанную в ">ссылка из ответа ниже. Мой код был обновлен до следующего:

wDataCsv = 'Time,' + ','.join(wData.keys())
qry = WeatherData.time_ordered_query(ndb.Key('Location', loc),start=start_date,end=end_date)
cursor = None
while True:
    gc.collect()
    fetched, next_cursor, more = qry.fetch_page(FETCHNUM, start_cursor=cursor)
    if fetched:
        for acct in fetched:
            d = [acct.time.strftime(date_string)]
            for attr in wData.keys():
                d.append(str(acct.dict_access(attr)))
                wData[attr].append([acct.time.strftime(date_string),acct.dict_access(attr)])
            wDataCsv += '\\n' + ','.join(d)
    if more and next_cursor:
        cursor = next_cursor
    else:
        break

где FETCHNUM=500. В этом случае я все еще превышаю ограничение на мягкую частную память для запросов той же длины, что и раньше, и выполнение запроса занимает гораздо больше времени. Я подозреваю, что проблема может заключаться в том, что сборщик мусора Python не удаляет уже использованную информацию, на которую ссылаются повторно, но даже когда я включаю gc.collect(), я не вижу улучшения.

РЕДАКТИРОВАТЬ:

Следуя приведенным ниже советам, я исправил проблему с помощью Projection Queries. Вместо того, чтобы иметь отдельную проекцию для каждого пользовательского запроса, я просто запускал одну и ту же проекцию каждый раз, а именно запрашивал все свойства объекта, за исключением строки JSON. Хотя это не идеально, поскольку каждый раз из базы данных по-прежнему извлекается ненужная информация, генерация отдельных запросов для каждого конкретного запроса не масштабируется из-за экспоненциального роста необходимых индексов. Для этого приложения, поскольку каждое дополнительное свойство является незначительной дополнительной памятью (кроме этой строки json), оно работает!


person AndyW    schedule 23.02.2018    source источник


Ответы (1)


Вы можете использовать проекционные запросы для получения только интересующих вас свойств. от каждого субъекта. Обратите внимание на ограничения. И это все еще не может масштабироваться бесконечно.

Вы можете разделить свои запросы на несколько запросов (более масштабируемо), но использовать более крупные фрагменты, а не только пару (вы можете получить 500 за раз) и курсоры. Ознакомьтесь с примерами в разделе Как удалить все записи. из хранилища данных Google?

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

Вы можете подготовить промежуточные результаты (также в хранилище данных) из больших сущностей заранее и использовать эти промежуточные предварительно вычисленные значения на заключительном этапе.

Наконец, вы можете попытаться создать и сохранить только части графиков и просто сшить их вместе в конце (только если до этого дойдет, я не уверен, как именно это будет сделано, я думаю, что это было бы нетривиально ).

person Dan Cornilescu    schedule 23.02.2018
comment
Большое спасибо! Проекционные запросы определенно отвечают на мой вопрос. Единственная проблема, с которой я столкнулся, заключается в том, что моя веб-страница работает как флажки, где пользователь может решить, какие свойства он хочет отобразить в виде графика. Поскольку имеется 21 флажок, это означает, что 21 выбор 1 + 21 выбор 2 + ... + 21 выбор 21 способа создания графиков, а для проекционных запросов индекс должен быть указан для каждого из этих способов. Есть ли способ создать гибкие индексы или как-то обойти это ограничение? Спасибо! - person AndyW; 24.02.2018
comment
Чтобы обойти эту проблему с индексами, я на самом деле написал быстрый скрипт для генерации всех этих комбинаций, но, как и ожидалось, поиск в этом несортированном списке индексов для выполнения запросов занимает слишком много времени, чтобы машина могла использоваться в этом случае. Я также попробовал итеративный подход, отредактированный в исходном вопросе. - person AndyW; 24.02.2018
comment
Свойства должны быть проиндексированы, но это не обязательно означает, что вам нужен отдельный составной индекс для каждого из них, могут обойти это. Это может представлять интерес: stackoverflow.com/questions/48388128/ - person Dan Cornilescu; 25.02.2018