Подтвердить что ты не робот

Каков наилучший способ сделать AppEngine Model Memcaching?

В настоящее время мое приложение кэширует модели в memcache следующим образом:

memcache.set("somekey", aModel)

Но сообщение Никса в http://blog.notdot.net/2009/9/Efficient-model-memcaching предполагает, что первое преобразование его в protobuffers намного более эффективно. Но после запуска некоторых тестов я обнаружил, что он действительно меньше по размеру, но на самом деле медленнее (~ 10%).

У других есть такой же опыт, или я делаю что-то неправильно?

Результаты тестов: http://1.latest.sofatest.appspot.com/?times=1000

import pickle
import time
import uuid

from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp import util
from google.appengine.datastore import entity_pb
from google.appengine.api import memcache

class Person(db.Model):
 name = db.StringProperty()

times = 10000

class MainHandler(webapp.RequestHandler):

 def get(self):

  self.response.headers['Content-Type'] = 'text/plain'

  m = Person(name='Koen Bok')

  t1 = time.time()

  for i in xrange(int(self.request.get('times', 1))):
   key = uuid.uuid4().hex
   memcache.set(key, m)
   r = memcache.get(key)

  self.response.out.write('Pickle took: %.2f' % (time.time() - t1))


  t1 = time.time()

  for i in xrange(int(self.request.get('times', 1))):
   key = uuid.uuid4().hex
   memcache.set(key, db.model_to_protobuf(m).Encode())
   r = db.model_from_protobuf(entity_pb.EntityProto(memcache.get(key)))


  self.response.out.write('Proto took: %.2f' % (time.time() - t1))


def main():
 application = webapp.WSGIApplication([('/', MainHandler)], debug=True)
 util.run_wsgi_app(application)


if __name__ == '__main__':
 main()
4b9b3361

Ответ 1

Вызов Memcache все еще разжигает объект с использованием или без использования protobuf. Pickle быстрее с объектом protobuf, так как он имеет очень простую модель

Обычные объекты рассола больше, чем объекты protobuf + pickle, поэтому они экономят время на Memcache, но время выполнения протобуфа больше времени процессора

Поэтому, как правило, любой из методов работает примерно одинаково... но

Причина, по которой вы должны использовать protobuf, может обрабатывать изменения между версиями моделей, тогда как Pickle будет ошибочной. Эта проблема укусит вас однажды, поэтому лучше всего справиться с ней раньше

Ответ 2

Оба pickle и protobufs медленны в App Engine, поскольку они реализованы в чистом Python. Я обнаружил, что создание собственного простого кода сериализации с использованием таких методов, как str.join, имеет тенденцию быть быстрее, поскольку большая часть работы выполняется на C. Но это работает только для простых типов данных.

Ответ 3

Один из способов сделать это быстрее - превратить вашу модель в словарь и использовать встроенную функцию eval/repr в качестве сериализаторов (de) с осторожностью, как всегда со злым eval, но это должно быть что здесь нет внешнего шага.

Ниже пример класса Fake_entity, реализующего именно это. Сначала вы создаете словарь через fake = Fake_entity(entity), тогда вы можете просто сохранить свои данные через memcache.set(key, fake.serialize()). Сериализация() является простым вызовом метода родного словаря, с некоторыми дополнениями, если вам нужно (например, добавить идентификатор в начале строки).

Чтобы вернуть его, просто используйте fake = Fake_entity(memcache.get(key)). Объект Fake_entity - это простой словарь, ключи которого также доступны как атрибуты. Обычно вы можете получить доступ к своим объектам, за исключением того, что referenceProperties дает ключи вместо того, чтобы извлекать объект (что на самом деле очень полезно). Вы также можете получить() фактический объект с помощью fake.get() или более интересно, изменить его, а затем сохранить с помощью fake.put().

Он не работает со списками (если вы извлекаете несколько объектов из запроса), но их можно легко отрегулировать с помощью функций соединения/разделения с использованием идентификатора типа "### FAKE MODEL ENTITY ###" в качестве разделителя. Используйте только с db.Model, вам потребуется небольшая настройка для Expando.

class Fake_entity(dict):
    def __init__(self, record):
        # simple case: a string, we eval it to rebuild our fake entity
        if isinstance(record, basestring):
            import datetime # <----- put all relevant eval imports here
            from google.appengine.api import datastore_types
            self.update( eval(record) ) # careful with external sources, eval is evil
            return None

        # serious case: we build the instance from the actual entity
        for prop_name, prop_ref in record.__class__.properties().items():
            self[prop_name] = prop_ref.get_value_for_datastore(record) # to avoid fetching entities
        self['_cls'] = record.__class__.__module__ + '.' + record.__class__.__name__
        try:
            self['key'] = str(record.key())
        except Exception: # the key may not exist if the entity has not been stored
            pass

    def __getattr__(self, k):
        return self[k]

    def __setattr__(self, k, v):
        self[k] = v

    def key(self):
        from google.appengine.ext import db
        return db.Key(self['key'])

    def get(self):
        from google.appengine.ext import db
        return db.get(self['key'])

    def put(self):
        _cls = self.pop('_cls') # gets and removes the class name form the passed arguments
        # import xxxxxxx ---> put your model imports here if necessary
        Cls = eval(_cls) # make sure that your models declarations are in the scope here
        real_entity = Cls(**self) # creates the entity
        real_entity.put() # self explanatory
        self['_cls'] = _cls # puts back the class name afterwards
        return real_entity

    def serialize(self):
        return '### FAKE MODEL ENTITY ###\n' + repr(self)
        # or simply repr, but I use the initial identifier to test and eval directly when getting from memcache

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

Ниже пример того, как выглядит сериализованный поддельный объект. Обратите особое внимание на datetime (created), а также на эталонные свойства (субдомен):

### FAKE MODEL ENTITY ###
{'status': u'admin ',' session_expiry ': None,' first_name ': u'Louis', 'last_name': u'Le Sieur ',' modified_by ': None,' password_hash ': u'a9993e364706816aba3e25717000000000000000', 'language': u'fr ',' created ': datetime.datetime(2010, 7, 18, 21, 50, 11, 750000),' modified ': None,' created_by ': None,' email ': u' [email protected] ',' key ':' agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw ',' session_ref ': None,' _cls ':' models.Login ',' groups ': [],' email___password_hash ': u'[email protected]+ a9993e364706816aba3e25717000000000000000 ',' subdomain ': datastore_types.Key.from_path (u'Subdomain', 229L, _app = u'jeregle '),' allowed ': [],' permissions ': []}


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