Большое количество вызовов RPC в GAE, это нормально?

#google-app-engine #google-cloud-datastore

# #google-app-engine #google-облако-хранилище данных

Вопрос:

У меня есть несколько вопросов, которые, надеюсь, помогут укрепить мое понимание «за сценами» GAE. В настоящее время в моем приложении мне необходимо получить набор данных размером 258 объектов. Я установил indexed=False для соответствующих свойств класса модели этого объекта. Я использую метод сериализации / десериализации сущности, описанный в блоге Ника Джонсона. Я сделал код универсальным для примера:

 def serialize_entities(models):
    if not models:
        return None
    elif isinstance(models, db.Model):
        return db.model_to_protobuf(models).Encode()
    else:
        return [db.model_to_protobuf(x).Encode() for x in models]

def deserialize_entities(data):
    if not data:
        return None
    elif isinstance(data, str):
        return db.model_from_protobuf(entity_pb.EntityProto(data))
    else:
        return [db.model_from_protobuf(entity_pb.EntityProto(x)) for x in data]

class BatchProcessor(object):
    kind = None
    filters = []
    def get_query(self):
        q = self.kind.all()
        for prop, value in self.filters:
            q.filter("%s" % prop, value)
        q.order("__key__")
        return q

    def run(self, batch_size=100):
        q = self.get_query()
        entities = q.fetch(batch_size)
        while entities:
            yield entities
            q = self.get_query()
            q.filter("__key__ >",entities[-1].key())
            entities = q.fetch(batch_size)

class MyKindHandler(webapp2.RequestHandler):
    def fill_cache(self, my_cache):
        entities = []
        if not my_cache:
            # retry in case of failure to retrieve data
            while not my_cache:
                batch_processor = BatchProcessor()
                batch_processor.kind = models.MyKind
                batch = batch_processor.run()
                my_cache = []

                for b in batch:
                    my_cache.extend(b)                    
            # Cache entities for 10 minutes
            memcache.set('MyKindEntityCache', serialize_entities(my_cache), 
                         600)
        for v in my_cache:
            # There is actually around 10 properties for this entity.
            entities.append({'one': v.one, 'two': v.two})

        mykind_json_str = simplejson.dumps({'entities':entities})
        # Don't set expiration - will be refreshed automatically when 
        # entity cache is.
        memcache.set('MyKindJsonCache', mykind_json_str)
        return mykind_json_str

    def get(self):
        my_cache = deserialize_entities(memcache.get('MyKindEntityCache'))
        if not my_cache:
            # Create entity amp; json cache
            self.fill_cache(my_cache)

        mykind_json_str = memcache.get('MyKindJsonCache')
        if not mykind_json_str:
            mykind_json_str = self.fill_cache(my_cache)
        self.response.write(mykind_json_str)
 

Когда я смотрю на at appstats, он показывает, что вызов ajax обработчику, извлекающему эти данные, занимает наибольшее количество времени. При вызове этого обработчика выполняется 434 вызова RPC в то время, когда он должен обновлять данные из хранилища данных (каждые 10 минут истекает срок действия кэша объектов), в противном случае существует 2 RPC, вызываемых для извлечения каждого из memcaches. Appstats для обновления данных:

реальный = 10 295 мс процессор = 13544 мс api = 5956 мс накладные расходы = 137 мс (434 RPC)
datastore_v3.Получаем 428
memcache.Получаем 2
memcache.Установите 2
datastore_v3.RunQuery 2

Я думаю, я понимаю, что по большей части делает каждый из этих вызовов, но хранилище данных v3.Вызовы Get, связанные с datastore_v3.RunQuery, сбивают меня с толку, если они представляют то, что я думаю, что они делают. Означают ли они, что это отдельные вызовы .get() для хранилища данных? Я подумал, что когда вы звоните.извлеките (batch_size), что вы возвращаете «пакет» с 1 вызовом … и не только это, почему количество этих вызовов должно быть на 150 больше, чем количество объектов в хранилище данных для данного типа?

В качестве примечания, я написал класс BatchProcessor до того, как узнал о курсорах запросов, и попытался поменять их местами, чтобы посмотреть, улучшилась ли производительность, но это не только остается прежним, но и увеличивает количество RPC из-за хранения курсора в memcache.

Любое понимание чего-то, что я, возможно, упускаю из виду, очень ценится!

Редактировать — Более подробная информация из appstats:

При тестировании этого и более тщательном изучении appstats я обнаружил, что мои вызовы .parent() для каждого MyKind при создании строки ответа json значительно увеличивают RPC, наряду с наличием 2 ReferenceProperties для каждого MyKind, которые могут иметь или не иметь значения. Я использую этот вызов .parent() для того, чтобы вернуть название компании в ответе с данными MyKind, и если у ReferenceProperties есть значения, я также получаю их имена. Я не понимал, что вызов .parent().name , .refprop1.name , и .refprop2.name выполняет отдельный datastore_V3.Get из хранилища данных для каждого из них. Я думал, что данные родительского и ссылочного свойства были возвращены вместе с исходным запросом для объекта MyKind. Есть ли способ добиться этого, или мне нужно будет создать 3 других свойства в MyKind, чтобы иметь эффективный доступ к этим свойствам имен?

Комментарии:

1. Я не вижу никаких ошибок в приведенном выше коде. Можете ли вы включить код serialize_entities и deserialize_entities?

2. Не задает вопросов. fetch() выполнить a .проникнуть за кулисы?

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

4. С каждым записанным RPC в appstats связана трассировка стека. Что показывают те, что для звонков get?

5. Я отредактировал свой вопрос с более подробной информацией. Основываясь на упоминании Дрю о ссылочных свойствах и моем сканировании appstats с помощью мелкозубой расчески после комментария Ника, моя правка дает результаты!