2010-12-11 15 views
6

Tengo un problema de diseño bastante común: necesito implementar un registro de historial (registro de auditoría) para los registros en Google App Engine. El registro de historial debe estar estructurado, es decir, no puedo unir todos los cambios en algún texto de forma libre y almacenarlo en el campo de cadena.Implementación de seguimiento de registro eficiente en Google App Engine - patrones de diseño

He considerado las siguientes opciones para el modelo de historial y, después de notar los problemas de rendimiento en la opción n. ° 1, he decidido implementar la opción n. ° 3. Pero todavía tenemos algunas dudas sobre si esta solución es eficiente y escalable. Por ejemplo: ¿existe el riesgo de que el rendimiento se deteriore significativamente con un mayor número de propiedades dinámicas en la opción n. ° 3?

¿Tiene algún conocimiento más profundo sobre los pros/contras de cada opción o podría sugerir otros patrones de diseño de seguimiento de auditoría aplicables para las características de las bases de datos de Google App Engine?

  1. uso clásico de SQL "maestro-detalle" relación
    • Pros
      • fácil de entender para los desarrolladores de bases de datos con el fondo de SQL
      • limpia: definición directa para el registro de la historia y sus propiedades
      • búsqueda rendimiento: búsqueda fácil a través de la historia (puede usar índices)
      • solución de problemas: fácil acceso por herramientas de administración (_ah/admin)
    • Contras
      • a menudo no se recomienda uno-a-muchas relaciones para ser implementado de esta manera en GAE DB
      • rendimiento
      • lectura: excesivo número de registro de operaciones de lectura para mostrar pista de auditoría de largo, por ejemplo, en el panel de detalles de una gran lista de registros.
  2. historia Almacenar en un campo BLOB (estructuras de pitón en escabeche)
    • Pros
      • simple de implementar y flexible rendimiento
      • lectura: muy eficiente
    • Contras
      • rendimiento de las consultas: no se puede buscar usando índices
      • solución de problemas: no puede inspeccionar los datos de espectador DB Admin (_ah/admin)
      • impuro: no es tan fácil de entender/aceptar para los desarrolladores de SQL (que consideran este feo)
  3. Historial de la tienda en las propiedades dinámicas de Expando. P.ej. para cada campo fieldName crear history_fieldName_n campos (donde n = < 0 ..N> es un número de registro de la historia)
    • Pros:
      • simple: fácil de implementar y entender
      • solución de problemas: puede leer todas las propiedades de historia a través de la interfaz de administración
      • rendimiento de lectura: manejo con una sola lectura a obtener el registro
    • Contras:
        rendimiento
      • de búsqueda: no se puede simplemente buscar a través de registros de historia (que HAV e diferente nombre)
      • no
      • demasiado limpio: número de propiedades puede ser confuso al primer vistazo
  4. historia Almacenar en un conjunto de campos de lista en el registro principal. P.ej. para cada fieldName crear un campo fieldName_history lista
    • Pros:
      • limpia: definición directa de propiedades historia
      • simple: fácil de entender para los desarrolladores de SQL
      • rendimiento
      • lectura: manejo con una sola lectura para obtener el registro
    • Contras:
        rendimiento
      • búsqueda: puede buscar usando índices o nly para registros que siempre tuvieron algún valor y no pueden buscar registros que tengan una combinación de valores en algún momento particular;
      • solución de problemas: la inspección de las listas es difícil en administrador espectador db

Respuesta

3

Si tuviera que elegir me quedo con la opción 1. La lee son como (si no más) para el performant otras opciones. Y todas las demás opciones solo tienen ventajas de velocidad en circunstancias específicas (conjuntos de cambios pequeños o muy grandes). También le brindará mucha flexibilidad (con más facilidad) como limpiar el historial después de x días o el historial de consultas en diferentes tipos de modelos. Asegúrese de crear las entidades de historial como elementos secundarios de la entidad modificada en la misma transacción para garantizar la coherencia. Podría terminar con uno de estos:

class HistoryEventFieldLevel(db.Model): 
    # parent, you don't have to define this 
    date = db.DateTime() 
    model = db.StringProperty() 
    property = db.StringProperty() # Name of changed property 
    action = db.EnumProperty(['insert', 'update', 'delete']) 
    old = db.PickleProperty() # Old value for field, empty on insert 
    new = db.PickleProperty() # New value for field, empty on delete 

class HistoryEventModelLevel(db.Model): 
    # parent, you don't have to define this 
    date = db.DateTime() 
    model = db.StringProperty() 
    action = db.EnumProperty(['insert', 'update', 'delete']) 
    change = db.PickleProperty() # Dictionary with changed fields as keys and tuples (old value, new value) as values