Внедрение эффективного аудита изменений записей в Google App Engine - шаблоны проектирования - PullRequest
6 голосов
/ 11 декабря 2010

У меня довольно распространенная проблема с дизайном: мне нужно реализовать журнал истории (контрольный журнал) для записей в Google App Engine.Журнал истории должен быть структурирован, т.е. я не могу объединить все изменения в какой-либо текст произвольной формы и сохранить в строковом поле.

Я рассмотрел следующие параметры для модели истории и, заметив проблемы с производительностью в варианте № 1, решил использовать параметр № 3.Но все же есть некоторые сомнения, если это решение является эффективным и масштабируемым.Например: существует ли риск того, что производительность значительно снизится при увеличении количества динамических свойств в варианте № 3?

Есть ли у вас более глубокие знания о плюсах и минусах для каждого варианта или вы могли бы предложить другие шаблоны разработки контрольного журнала, применимые для характеристик БД Google App Engine?

  1. Использовать классический SQL "master-detail "отношение
    • Плюсы
      • просто для понимания разработчиками баз данных с фоном SQL
      • clean: прямое определение для записи истории и ее свойств
      • производительность поиска: легкий поиск по истории (можно использовать индексы)
      • устранение неполадок: легкий доступ с помощью инструментов администрирования (_ah / admin)
    • Минусы
      • one-to-многочисленные отношения часто не рекомендуется реализовывать таким образом в GAE DB
      • производительность чтения: чрезмерное количество операций чтения записей для отображения длинного контрольного журнала, например, в области сведений большого списка записей.
  2. Сохранение истории в поле BLOB (маринованные структуры python)
    • Плюсы
      • простота реализации и гибкость
      • производительность чтения: очень эффективная
    • Минусы
      • производительность запросов: невозможно выполнить поиск по индексам
      • устранение неполадок: невозможно проверить данные администраторомdb viewer (_ah / admin)
      • unclean: не так просто понять / принять для разработчиков SQL (они считают это уродливым)
  3. Storeистория в динамических свойствах Expando.Например, для каждого поля fieldName создать history_fieldName_n полей (где n = <0..N> - номер записи в истории)
    • Плюсы:
      • просто: легко реализовать и понять
      • устранение неполадок: можно прочитать все свойства истории через интерфейс администратора
      • производительность чтения: одна операция чтения для получения записи
    • Минусы:
      • производительность поиска: нельзя просто искать в записях истории (они имеют другое имя)
      • не слишком чисто: количество свойств может сбивать с толку с первого взгляда
  4. Хранить историю в некотором наборе полей списка в основной записи.Например.для каждого fieldName создайте fieldName_history поле списка
    • Плюсы:
      • clean: прямое определение свойств истории
      • просто: легко понять для разработчиков SQL
      • производительность чтения: одна операция чтения для получения записи
    • Минусы:
      • производительность поиска: можно искать по индексам только для записей, которые когда-либо имели какое-то значение и не могутпоиск записей, имеющих комбинацию значений в определенный момент времени;
      • Устранение неполадок: проверка списков затруднена в программе просмотра db администратора

1 Ответ

3 голосов
/ 12 декабря 2010

Если бы мне пришлось выбирать, я бы выбрал вариант 1. Чтения как (если не больше) соответствуют другим параметрам. И все другие варианты имеют преимущества в скорости только при определенных обстоятельствах (небольшие или очень большие наборы изменений). Это также даст вам большую гибкость (с большей легкостью), например, историю очистки после x дней или историю запросов для разных типов моделей. Убедитесь, что вы создаете объекты истории как дочерние элементы измененного объекта в той же транзакции, чтобы гарантировать согласованность. Вы могли бы в конечном итоге с одним из них:

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