У меня есть несколько вопросов, которые, надеюсь, помогут укрепить мое понимание «закулисных» GAE. В настоящее время в моем приложении я должен получить набор данных размером 258 объектов. Я установил indexed = False для соответствующих свойств класса Model этой сущности. Я использую сериализованную / десериализованную технику сущностей, описанную в блоге Ника Джонсона blog . Я создал общий код для примера:
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 & 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)
Когда я смотрю на appstats, это показывает, что ajax-вызов обработчику, получающему эти данные, занимает больше всего времени. Когда этот обработчик вызывается, когда выполняется обновление данных из хранилища данных (каждые 10 минут истекает срок действия кэша сущностей), выполняется 434 вызова RPC, в противном случае существует 2 RPC, вызываемых для получения каждой из кэшей памяти. Appstats для обновления данных:
real=<b>10295ms</b> cpu=<b>13544ms</b> api=<b>5956ms</b> overhead=<b>137ms</b> (<b>434 RPCs</b>)
datastore_v3.Get 428
memcache.Get 2
memcache.Set 2
datastore_v3.RunQuery 2
Мне кажется, я понимаю, что каждый из этих вызовов делает по большей части, но вызовы datastore_v3.Get, связанные с datastore_v3.RunQuery, смущают меня, если они представляют то, что, по моему мнению, они делают. Они означают, что это отдельные вызовы .get () в хранилище данных? Я думал, что когда вы вызываете .fetch (batch_size), вы получаете «пакет» с 1 вызовом ... не только это, почему число этих вызовов будет на 150+ больше, чем число объектов в хранилище данных для данный вид?
В качестве примечания я написал класс BatchProcessor, прежде чем узнавать о курсорах запросов, и попытался поменять их местами, чтобы посмотреть, не повысилась ли производительность, но она не только осталась прежней, но и увеличила количество RPC из-за сохранения курсор в memcache.
Любое понимание того, что я могу пропустить, очень ценится!
Редактировать - Более подробная информация из appstats:
При тестировании этого и проверке приложений ближе, я обнаружил, что мои вызовы .parent () для каждого MyKind при создании строки ответа json значительно увеличивают RPC, наряду с наличием 2 ReferenceProperties на MyKind, которые по-разному имеют или не имеет значения. Я использую этот вызов .parent (), чтобы вернуть имя компании в ответе с данными MyKind, и если у ReferenceProperties есть значения, я также получу их имена. Я не осознавал, что вызов .parent (). Name, .refprop1.name и .refprop2.name выполняет отдельный datastore_V3.Get из хранилища данных для каждого из них. Я думал, что данные родительского и ссылочного свойства были возвращены с исходным запросом для объекта MyKind. Есть ли способ сделать это, или мне нужно будет создать 3 других свойства в MyKind, чтобы иметь эффективный доступ к этим свойствам имени?