standard prices google flexible engine docs costs app google-app-engine memcached google-cloud-datastore

google-app-engine - prices - google app engine launcher



¿Cuál es la mejor manera de hacer AppEngine Model Memcaching? (3)

Actualmente mi aplicación almacena en caché modelos en Memcache de esta manera:

memcache.set("somekey", aModel)

Pero la publicación de Nicks en http://blog.notdot.net/2009/9/Efficient-model-memcaching sugiere que convertirlo primero en protobuffers es mucho más eficiente. Pero después de ejecutar algunas pruebas descubrí que, de hecho, es de menor tamaño, pero en realidad es más lento (~ 10%).

¿Los demás tienen la misma experiencia o estoy haciendo algo mal?

Resultados de la prueba: 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()


La llamada de Memcache aún conserva el objeto con o sin el uso de protobuf. Pickle es más rápido con un objeto protobuf ya que tiene un modelo muy simple

Los objetos de pickle simples son más grandes que los objetos protobuf + pickle, por lo tanto, ahorran tiempo en Memcache, pero hay más tiempo de procesador en la conversión de protobuf

Por lo tanto, en general, cualquiera de los dos métodos funciona igual ... pero

La razón por la que debe usar protobuf es que puede manejar los cambios entre las versiones de los modelos, mientras que Pickle tendrá un error. Este problema te morderá algún día, así que es mejor que lo hagas antes


Tanto Pickle como Protobufs son lentos en App Engine ya que están implementados en Python puro. He descubierto que escribir mi propio código de serialización simple usando métodos como str.join tiende a ser más rápido ya que la mayoría del trabajo se realiza en C. Pero eso solo funciona para tipos de datos simples.


Una forma de hacerlo más rápido es convertir su modelo en un diccionario y utilizar la función nativa eval / repr como sus (de) serializadores, con precaución, por supuesto, como siempre con la evaluación evil, pero debería ser seguro aquí dado que no hay un paso externo.

A continuación se muestra un ejemplo de una clase Fake_entity implementando exactamente eso. Primero crea su diccionario a través de fake = Fake_entity(entity) luego puede simplemente almacenar sus datos a través de memcache.set(key, fake.serialize()) . Serialize () es una simple llamada al método del diccionario nativo de repr, con algunas adiciones si es necesario (por ejemplo, agregar un identificador al principio de la cadena).

Para recuperarlo, simplemente use fake = Fake_entity(memcache.get(key)) . El objeto Fake_entity es un diccionario simple cuyas claves también son accesibles como atributos. Puede acceder a las propiedades de su entidad normalmente, excepto que las propiedades de referencia dan claves en lugar de ir a buscar el objeto (lo que en realidad es bastante útil). También puede obtener () la entidad real con fake.get (), o más, cambiarla y luego guardarla con fake.put ().

No funciona con listas (si obtiene varias entidades de una consulta), pero podría ajustarse fácilmente con funciones de combinación / división utilizando un identificador como ''### FAKE MODEL ENTITY ###'' como separador. Usar con db.Model solamente, necesitaría pequeños ajustes para 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

Me encantaría que las pruebas de velocidad en esto, supongo que esto es bastante más rápido que los otros enfoques. Además, no tiene ningún riesgo si sus modelos han cambiado de alguna manera mientras tanto.

Debajo hay un ejemplo de cómo se ve la entidad falsa serializada. Eche un vistazo particular a datetime (creado) así como a las propiedades de referencia (subdominio):

### FAKE MODEL ENTITY ###
{''estado'': 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] '','' clave '':'' agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw '','' session_ref '': None,'' _cls '':'' models.Login '','' groups '': [],'' email___password_hash '': u''[email protected]+ a9993e364706816aba3e25717000000000000000 '','' subdominio '': tipos_datostore.Key.from_path (u''Subdomain'', 229L, _app = u''jeregle ''),'' permitido '': [],'' permisos '': []}


Personalmente, también utilizo variables estáticas (más rápidas que Memcache) para almacenar en caché mis entidades a corto plazo, y obtengo el almacén de datos cuando el servidor ha cambiado o su memoria ha sido limpiada por alguna razón (lo que sucede muy a menudo de hecho).