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).