Pregunta

Me pregunto cuál sería la mejor manera de diseñar una aplicación social donde los miembros realicen actividades y sigan las actividades de otros miembros utilizando Google AppEngine.

Para ser más específicos, supongamos que tenemos estas entidades:

  • Usuarios que tienen amigos
  • Actividades que representan acciones realizadas por los usuarios (digamos que cada una tiene un mensaje de cadena y una Propiedad de referencia para su usuario propietario, o puede usar la asociación principal a través de la clave del anexo)

La parte difícil es seguir las actividades de tus amigos, lo que significa agregar las últimas actividades de todos tus amigos. Normalmente, eso sería una unión entre la tabla de Actividades y su lista de amigos, pero ese no es un diseño viable en un complemento ya que no hay una unión que simule que requerirá activar N consultas (donde N es el número de amigos) y luego fusionarse en la memoria. muy caro y probablemente excederá el plazo de solicitud ...)

Actualmente estoy pensando en implementar esto usando colas de la bandeja de entrada donde la creación de una nueva Actividad activará un proceso en segundo plano que colocará la clave de la nueva actividad en la bandeja de entrada " de cada usuario siguiente:

  • Obteniendo " Todos los usuarios que siguen a X " es una posible consulta de complemento
  • No es una entrada por lotes muy costosa en una nueva "Bandeja de entrada" entidad que básicamente almacena tuplas (usuario, clave de actividad).

Estaré encantado de escuchar pensamientos sobre este diseño o sugerencias alternativas, etc.

¿Fue útil?

Solución

Eche un vistazo a Creación de aplicaciones complejas y escalables en App Engine ( pdf ), a charla fascinante dada en Google I / O por Brett Slatkin. Aborda el problema de construir un servicio de mensajería escalable como Twitter.

Aquí está su solución usando una propiedad de lista:

class Message(db.Model):
    sender = db.StringProperty()
    body = db.TextProperty()

class MessageIndex(db.Model):
    #parent = a message
    receivers = db.StringListProperty()

indexes = MessageIndex.all(keys_only = True).filter('receivers = ', user_id)
keys = [k.parent() for k in indexes)
messages = db.get(keys)

Esta consulta de solo clave encuentra los índices de mensaje con un receptor igual al que especificó sin deserializar y serializar la lista de receptores. Luego, utiliza estos índices para capturar solo los mensajes que desee.

Aquí está de manera incorrecta para hacerlo:

class Message(db.Model):
    sender = db.StringProperty()
    receivers = db.StringListProperty()
    body = db.TextProperty()

messages = Message.all().filter('receivers =', user_id)

Esto es ineficiente porque las consultas tienen que desempaquetar todos los resultados devueltos por su consulta. Entonces, si devolvió 100 mensajes con 1,000 usuarios en cada lista de receptores, tendría que deserializar 100,000 (100 x 1000) valores de propiedades de la lista. Demasiado caro en latencia de almacenamiento de datos y CPU.

Al principio estaba bastante confundido, así que escribí un breve tutorial sobre el uso de la propiedad de lista . Disfruta :)

Otros consejos

No sé si es el mejor diseño para una aplicación social, pero jaiku se se transfirió a App Engine por su creador original cuando Google adquirió la empresa, por lo que debería ser razonable.

Consulte la sección Actores y tigres y osos, ¡Oh, Dios mío! en design_funument.txt . Las entidades se definen en common / models.py y las consultas están en common / api.py .

Robert, sobre tu solución propuesta:

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])

Creo que ndb.TextProperty " body " no se puede usar con proyecciones porque no está indexado. Las proyecciones solo admiten propiedades indexadas. La solución válida sería mantener las 2 tablas: Message y MessageIndex.

Creo que esto ahora se puede resolver con las nuevas consultas de proyección en NDB.

class Message(ndb.Model):
    sender = ndb.StringProperty()
    receivers = ndb.StringProperty(repeated=True)
    body = ndb.TextProperty()

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])

Ahora no tiene que lidiar con el costoso costo de deserializar la propiedad de la lista.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top