OptimisticConcurrencyException: несколько приложений на основе EF, использующих общий кеш AppFabric и одну и ту же базу данных

Я использую веб-приложение и службу Windows на том же компьютере, что и Appfabric. Оба приложения повторно используют один и тот же код DAL (dll), основанный на EF (Entity Framework) Code-First и обращающийся к одному и тому же кешу в Appfabric. Код в службе Windows реализован как задание как часть Quartz.Net

Веб-приложение должно поддерживать несколько запросов, а Windows обслуживает несколько потоков (планировщик и события). В обоих случаях общая dll-библиотека DAL создает объект DbContext для каждого сеанса http и ContextID потока или просто ContextID потока для более поздних версий. DAL использует EFCachingProviders из здесь. Кроме того, в моем решении EF используется оптимистический параллелизм со столбцами временных меток и IsRowVersion в сопоставлении.

Как указано здесь, преимущество наличия кеша 2-го уровня состоит в том, чтобы иметь доступ к представлению исходного состояния между процессами! Но это, похоже, не работает для меня, я получаю «OptimisticConcurrencyException» в моем случае использования следующим образом:

  1. перезапустите кластер кеша, перезапустите службу Windows, перезапустите iis -> с чистого листа :)
  2. Используя веб-приложение (firefox), я вставляю новый объект A со ссылкой на существующий объект B. Я вижу новую строку в базе данных. Все ок.
  3. Используя webapp в другом браузере (chrome) = новый сеанс, я вижу новый объект.
  4. Затем служба Windows пытается выполнить некоторую фоновую обработку и пытается обновить объект B. Это приводит к исключению OptimisticConcurrencyException. По-видимому, процесс в службе Windows содержит версию объекта B с устаревшей версией строки.
  5. Если я перезапущу службу Windows, она снова попробует ту же логику и будет работать без исключения ....

Таким образом, оба приложения являются многопоточными, используют один и тот же код DAL, подключаются к одной базе данных, к одному и тому же кластеру кеша и к одному и тому же кешу. Я ожидал, что обновление и вставка будут в кеше appfabric. Я ожидал, что контекст EF службы Windows будет использовать новейшую информацию. Почему-то кажется, что это кеш 1-го уровня, хранящий старую информацию ... или что-то еще идет не так.

Пожалуйста посоветуй...

Обновить

Хорошо, покопавшись, я исправил проблему с обновлением моей службы Windows. Каждый объект Manager с запросами к DAL использует DbContext, привязанный к его идентификатору процесса + идентификатору потока. Итак, в функции Execute моего Quartz Job все менеджеры (с разными типами объектов) должны использовать один и тот же DbContext, который создается первым менеджером.

Проблема заключалась в том, что после завершения функции DbContext не был удален (что происходит автоматически в диспетчере DbContext на основе сеанса HTTP). Таким образом, в следующий раз, когда задание было выполнено, был найден и использован тот же DbContext, который к тому времени уже был датирован (старый кеш первого уровня ???). Кэш 2-го уровня не должен быть проблемой, потому что он является общим и ДОЛЖЕН содержать новейшие объекты ... если они есть.

Итак, эта часть исправлена.

Новая проблема

Таким образом, веб-приложение создает новый объект A, обновляет существующий объект B, служба Windows теперь работает и может без проблем обновлять существующий (измененный) объект B.

Проблема: когда я обновляю веб-приложение, оно не видит изменений (выполненных службой Windows) объекта B ....

Итак, если веб-приложение изменило счет на 5, через 10 минут служба Windows изменит счет на 6, и я открываю веб-приложение в том же или новом окне / браузере, я все равно вижу 5, а не 6!

Перезапуск веб-приложения (iis) не помогает, также iisreset не помогает. Когда я делаю Restart-CacheCluster .... он работает и показывает 6 ....

Похоже, что элемент находится в кеше. Служба Windows обновляет его, но не делает недействительным элемент, который является старым и используется веб-приложением ....

Или ... хотя это тот же объект, веб-приложение имеет свою собственную запись в кеше, а приложение win имеет свою собственную запись (которая становится недействительной) ....

Который из?

Решение

Я решил это сам. Похоже, что оболочка EF использует строку запроса в качестве ключа для хранения элементов в кеше. Таким образом, два разных запроса (неважно, исходят ли они из двух разных приложений, использующих один и тот же распределенный кеш или одно и то же приложение), ссылающиеся на одни и те же данные в базе данных, будут иметь разные ключи (разные строки запроса) и поэтому разные места в кеше. Может, дело не в этом черно-белом, а в чем-то вроде этого ...

Я не думаю, что внутри используется какой-то алгоритм, чтобы проверить, касается ли запрос существующих кешированных объектов.

Это вызывает мою проблему, когда моя служба Windows выполняет обновление, а веб-приложение по-прежнему видит старую версию из кеша, которую можно решить только с помощью команды Restart-CacheCluster.

Итак, как я это исправил: Моя служба Windows - это пакетное задание, запускаемое планировщиком Quartz. После этого очищаю весь кеш:

private void InvalidateCache()
    {
        try
        {
            DataCache myCache = ...
            foreach (String region in myCache.GetSystemRegions())
            {
                myCache.ClearRegion(region);
            }
        }
        catch (Exception ex)
        {
            eventLog.WriteEntry("InvalidateCache exception : " + ex.Message);
        }
    }

person atam    schedule 01.05.2013    source источник


Ответы (1)


У меня нет ответа, но я надеюсь, что приведенные ниже мысли могут указать вам правильное направление.

Если это проблема только с обновлениями, я бы прочитал свежий экземпляр записи при каждом обновлении из базы данных и обновил его. Это позволит избежать ошибок оптимистичного параллелизма. Обратите внимание, что DbContext не является потокобезопасным - я не знаю, вызовет ли это проблему, но чтение каждый раз, когда new, решит эту проблему.

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

person gabnaim    schedule 01.05.2013
comment
Спасибо за ваш комментарий. Что касается обновлений, я добавил, как я это исправил. Кроме того, чтение нового экземпляра перед его обновлением противоречит идее оптимистичного параллелизма. Тогда столбец версии строки не будет иметь дополнительных преимуществ. Вы хотите обновить объект только в том случае, если никто другой не обновлял его за это время ... Что касается текущих проблем с чтением, насколько я знаю, используемая оболочка кэширования AppFabric не имеет дополнительных настроек. Предполагается, что при обновлении кэшированные объекты становятся недействительными .... - person atam; 02.05.2013