Обнаружение мертвого замка - Gemfire 8
Я определил регион GemFire с помощью RegionShortcut.PARTITION и setRedundantCopies(1). В этом регионе используются 3 приложения, работающие на 3 разных виртуальных машинах.
Кажется, что у меня есть мертвая блокировка, когда я закрываю виртуальную машину приложения, которое только что вставило элемент в кеш ("владелец" элемента):
* Заблокированный процесс: заблокирован до region.put.
* Процесс блокировки: кажется, заблокирован после попытки удалить старые записи из региона. Я подозреваю, что эта операция вызывается механизмом уничтожения, предоставленным CacheListenerAdapter.
Я прочитал некоторую документацию по этой проблеме по следующим ссылкам: API-интерфейс CacheListener и этот блог, в котором виноваты в основном слушатели.
Тем не менее, кажется, что эти проблемы были решены и исправлены в версиях GemFire 6.x [например: здесь и здесь]
Итак, я хотел бы спросить:
1) Сообщается ли об этих проблемах для Gemfire 8? или 7?
2) Каков рекомендуемый способ решения этой проблемы? 3 различных обходных пути упомянуты здесь. Есть ли другие? Любой предпочтительный?
Для справки: дамп потока блокирующего процесса выглядит следующим образом:
Owner stack trace: java.lang.Throwable
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lockInterruptibly(Unknown Source)
at com.gemstone.gemfire.internal.cache.BucketRegion.lockPrimaryStateReadLock(BucketRegion.java:780)
at com.gemstone.gemfire.internal.cache.BucketRegion.doLockForPrimary(BucketRegion.java:719)
at com.gemstone.gemfire.internal.cache.BucketRegion.beginLocalWrite(BucketRegion.java:704)
at com.gemstone.gemfire.internal.cache.BucketRegion.basicDestroy(BucketRegion.java:1105)
at com.gemstone.gemfire.internal.cache.PartitionedRegionDataStore.destroyLocally(PartitionedRegionDataStore.java:1511)
at com.gemstone.gemfire.internal.cache.PartitionedRegion.destroyInBucket(PartitionedRegion.java:5440)
at com.gemstone.gemfire.internal.cache.PartitionedRegionDataView.destroyExistingEntry(PartitionedRegionDataView.java:45)
at com.gemstone.gemfire.internal.cache.PartitionedRegion.basicDestroy(PartitionedRegion.java:5317)
at com.gemstone.gemfire.internal.cache.LocalRegion.validatedDestroy(LocalRegion.java:1330)
at com.gemstone.gemfire.internal.cache.LocalRegion.destroy(LocalRegion.java:1317)
at com.gemstone.gemfire.internal.cache.AbstractRegion.destroy(AbstractRegion.java:282)
at com.gemstone.gemfire.internal.cache.LocalRegion.remove(LocalRegion.java:9513)
1 ответ
Возможны взаимоблокировки с использованием CacheListeners. Поведение задокументировано в последнем (GemFire 8.1) javadoc. Вот соответствующая выдержка из документации CacheListener:
Как избежать риска тупика
Методы в CacheListener вызываются, удерживая блокировку записи, описанной EntryEvent, в результате, если метод слушателя занимает много времени для выполнения, то это приведет к тому, что вызвавшая его операция займет много времени. Кроме того, код слушателя, который вызывает методы Region, может привести к тупику. Например, в afterUpdate(EntryEvent) для ключа входа k1, put(k2, someVal) вызывается одновременно с afterUpdate(EntryEvent) для вызовов ключа k2 вызовов put(k1, someVal) может привести к взаимоблокировке. Этот пример зависимости вспомогательного ключа может быть расширен до зависимости вспомогательного региона, где код прослушивателя в области "A" выполняет операции области для "B", а код прослушивателя в области "B" выполняет операции области для "A". В зависимости от конфигурации региона взаимные блокировки могут быть либо на уровне Java, либо в виде распределенных мертвых блокировок на несколько виртуальных машин Чтобы быть уверенным в отсутствии взаимоблокировок, код прослушивателя должен вызывать доступ к региону другого потока и не должен ждать, пока этот поток завершит задачу.