NHibernate ReadWriteCache, управление версиями и отображение родителя / потомка - PullRequest
2 голосов
/ 11 января 2012

Краткое описание: после того, как родитель и один из его дочерних элементов обновлены, транзакция зафиксирована, сеанс закрыт, следующий сеанс получает родительский элемент из кэша L2, но обновленный дочерний элемент загружается из базы данных. Родитель версирован, дети нет.

Подробное описание: как я уже говорил, у меня есть родитель с дочерней коллекцией. Родитель версионен, но дочерние не соответствуют моему приложению, и сопоставления гарантируют, что все обновления выполняются путем сохранения родителя. Что я вижу, что, когда я загружаю parent с потомками, parent и большинство потомков загружаются из кэша L2, но недавно обновленный child загружается из базы данных. Я провел некоторое исследование деталей ReadWriteCache и обнаружил, что после того, как я сохранил parent и обновил child, следующий код решает НЕ обновлять кэш:

public bool Put(CacheKey key, object value, long txTimestamp, object version, IComparer versionComparator, bool minimalPut)
{
    bool CS$1$0000;
    if (txTimestamp == -9223372036854775808L)
    {
        return false;
    }
    lock (this._lockObject)
    {
        if (log.IsDebugEnabled)
        {
            log.Debug("Caching: " + key);
        }
        try
        {
            this.cache.Lock(key);
            ILockable lockable = (ILockable) this.cache.Get(key);
            if ((lockable == null) || lockable.IsPuttable(txTimestamp, version, versionComparator))
            {
                this.cache.Put(key, new CachedItem(value, this.cache.NextTimestamp(), version));
                if (log.IsDebugEnabled)
                {
                    log.Debug("Cached: " + key);
                }
                return true;
            }
            if (log.IsDebugEnabled)
            {
                if (lockable.IsLock)
                {
                    log.Debug("Item was locked: " + key);
                }
                else
                {
                    log.Debug("Item was already cached: " + key);
                }
            }
            CS$1$0000 = false;
        }
        finally
        {
            this.cache.Unlock(key);
        }
    }
    return CS$1$0000;
}

В моем случае lockable - это не null, но он не может быть вставлен:

public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator)
{
    // we really could refresh the item if it  
    // is not a lock, but it might be slower
    //return freshTimestamp < txTimestamp
    return version != null && comparator.Compare(version, newVersion) < 0;
}

Как вы можете видеть из источника NHibernate, ранее решение IsPuttable было принято путем сравнения временных меток, но теперь версия вступает в игру.

Теперь вопрос: я должен версия КАЖДОЙ сущности в моей доменной модели? До того, как возникла проблема, я был уверен, что мне следует использовать только версию Aggregate Roots.

Я действительно рассчитываю найти другой способ решения этой проблемы, потому что моя иерархия родитель-потомок гораздо глубже (не спрашивайте почему :)), скажем, на 3 уровня (на самом деле она глубже): Root-> ляющих> внучат

В сценарии, когда обновлялся только GrandChild, я ожидал 2 оператора UPDATE: один для GrandChild и другой для Root, чтобы обновить свою версию. Но если я сделаю версию Child, то это будет стоить мне дополнительных обновлений базы данных и туда-обратно :( ... Кстати, если ping Ayende или кто-то из NH - ADO.NET, пакетирование для NH работает только для тех же сущностей, оно не будет вставлено Операторы пакетного обновления для GrandChild, Child и Root.

Ответы [ 2 ]

1 голос
/ 12 января 2012

Извините, ребята, что запутал вас. Кажется, теперь я понял. Давайте посмотрим на класс ReadWriteCache (который на самом деле является стратегией, а не сам кеш). Нас интересуют три метода: Put (я упоминал об этом в своем вопросе), AfterInsert и AfterUpdate:

  • AfterInsert решает, следует ли хранить вставленную сущность в кеше L2. Это похоже на «сквозное» поведение. Если вы используете генератор идентификаторов, он не будет вызываться: (
  • AfterUpdate решает, следует ли сохранить обновленную сущность в L2. В не параллельной среде это зависит от

    public bool IsCacheInvalidationRequired
    {
        get { return HasFormulaProperties || (!IsVersioned && (entityMetamodel.IsDynamicUpdate || TableSpan > 1)); }
    }
    
  • наконец, давайте поговорим о путе. Пут решает, следует ли хранить загруженный из базы данных в кеше L2. Может произойти следующее: a) если L2 не имеет этой (по идентификатору) сущности, мы всегда сохраняем загруженную сущность b) если L2 уже имеет эту сущность, то мы обновляем ее в L2 только , если сущность версионирована и его версия новее существующей.

Надеюсь, теперь я действительно понимаю, когда мои созданные или обновленные сущности появятся в кеше L2 и почему я вижу дополнительные операторы db select. Все эти правила применяются к ReadWriteCache. Может быть, кто-то использует NonStrictReadWriteCache, но это не вариант для меня.

1 голос
/ 11 января 2012

Вам также необходимо установить атрибут версии для ваших дочерних объектов, а не только для совокупных корней. У меня есть похожий вариант использования в моем текущем проекте, и именно так все и работает. Мы управляем этим путем реализации версии в базовом классе наших сущностей и базовом классе наших свободно распространяемых классов отображения nhibernate.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...