HazelcastSerializationException для IMap InternalKey - как отлаживать или регистрировать - PullRequest
0 голосов
/ 23 марта 2020

Мы используем класс, который реализует Cache<K, V>. Кэшируется в Hazelcast IMap<InternalKey<K, C>, V>. Есть логика «удаления» c, которая удаляет на InternalKey из <String, String>.

K, C и V - все строки.

public class HazelcastMapJCacheAdapter<K, C, V> implements Cache<K, V> {
    private IMap<InternalKey<K, C>, V> cachedData;

    public void removeEntriesByKeyAndContext(BiPredicate<K, C> removeCondition) {
        Predicate<InternalKey<K, C>, V> predicate = (Predicate<InternalKey<K, C>, V> & Serializable)
            mapEntry -> (removeCondition.test(mapEntry.getKey().getOriginalKey(), mapEntry.getKey().getContext()));
        cachedData.removeAll(predicate);
    }
}

Я продолжаю получать HazelcastSerializationException, который я не могу понять. Что может go не так с сериализацией String? Как правильно зарегистрировать этот сценарий, чтобы получить больше данных (аргумент, который я имею в виду, это предикат, который является интерфейсом функции ...)? Я не могу воспроизвести этот сценарий в своей среде разработки, поэтому отладка - это проблема.

Спасибо

2020-03-23 02:07:34 WARN | [10.212.179.245]:5701 [MyApp] [3.12.5] Error while logging processing event
com.hazelcast.nio.serialization.HazelcastSerializationException: Failed to serialize 'com.hazelcast.spi.impl.operationservice.impl.operations.PartitionIteratingOperation'
    at com.hazelcast.internal.serialization.impl.SerializationUtil.handleSerializeException(SerializationUtil.java:82) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toBytes(AbstractSerializationService.java:157) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toBytes(AbstractSerializationService.java:133) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toBytes(AbstractSerializationService.java:124) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.OutboundOperationHandler.send(OutboundOperationHandler.java:56) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.Invocation.doInvokeRemote(Invocation.java:656) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.Invocation.doInvoke(Invocation.java:631) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.Invocation.invoke0(Invocation.java:592) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.Invocation.invoke(Invocation.java:256) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.InvocationBuilderImpl.invoke(InvocationBuilderImpl.java:61) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.InvokeOnPartitions.invokeOnAllPartitions(InvokeOnPartitions.java:121) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.InvokeOnPartitions.invokeAsync(InvokeOnPartitions.java:99) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.InvokeOnPartitions.invoke(InvokeOnPartitions.java:88) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl.invokeOnAllPartitions(OperationServiceImpl.java:385) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.map.impl.proxy.MapProxySupport.removeAllInternal(MapProxySupport.java:618) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.map.impl.proxy.NearCachedMapProxyImpl.removeAllInternal(NearCachedMapProxyImpl.java:330) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.hazelcast.map.impl.proxy.MapProxyImpl.removeAll(MapProxyImpl.java:285) ~[hazelcast-3.12.5.jar:3.12.5]
    at com.myapp.cache.impl.HazelcastMapJCacheAdapter.removeEntriesByKeyAndContext(HazelcastMapJCacheAdapter.java:578) ~[myapp-distributed-cache-impl.jar:?]

Ответы [ 2 ]

1 голос
/ 24 марта 2020

Вот сводка требований для removeAll (и любого другого вида обработки на стороне сервера, например executeOnKey et c):

  • Ключ и значение должны быть сериализуемыми, а их классы должны быть доступны на сервере classpath
  • Predicate (или EntryProcessor et c) должны быть сериализуемыми и доступны на сервере classpath

Я предполагаю, что в вашем случае BiPredicate<K, C> removeCondition не сериализуем; однако он должен быть сериализован как часть лямбды, которую вы передаете removeAll. Например, рассмотрим этот код:

class Scratch {
    public static void main(String[] args) {
        remove((x, y) -> true);
    }

    private static void remove(BiPredicate<String, String> predicate) {
        HazelcastInstance hz = Hazelcast.newHazelcastInstance();
        // put 100 items in the map
        IMap<String, String> map = hz.getMap("map");
        for (int i = 0; i < 100; i++) {
            map.put("" + i, "" + i);
        }

        map.removeAll((Predicate<String, String> & Serializable)
                entry -> predicate.test(entry.getKey(), entry.getValue()));

        // now size is 0
        System.out.println("Map size after removeAll " + map.size());
    }
}

Приведенный выше код будет выполнен без каких-либо проблем. Как только вы добавите еще один HazelcastInstance в кластер (просто добавьте Hazelcast.newHazelcastInstance() в remove тело метода), экземпляр Predicate должен быть сериализован и отправлен по сети между двумя членами кластера. Это терпит неудачу, потому что аргумент predicate не является Serializable, но является частью лямбды Predicate, передаваемой в качестве аргумента removeAll. Решение состоит в том, чтобы убедиться, что все ссылки в вашей лямбде являются сериализуемыми. Таким образом, в приведенном выше примере исправление заключается в обновлении метода main следующим образом:

public static void main(String[] args) {
    remove((BiPredicate<String, String> & Serializable) (x, y) -> true);
}

В общем, соблюдайте осторожность при использовании лямбда-выражений, потому что легко случайно захватить поля внешнего класса и в этом случае вам нужно будет обратиться к сериализуемости содержащего класса, и размер сериализованной формы вашей лямбды неожиданно увеличится. Цитирование https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#serialization "Вы можете сериализовать лямбда-выражение, если его целевой тип и захваченные аргументы сериализуемы. Однако, как и для внутренних классов, сериализация лямбда-выражений настоятельно не рекомендуется."

1 голос
/ 24 марта 2020

Вам необходимо установить определение класса InternalKey на пути к классам сервера, так как записи десериализуются для предикатов.

Немного вне контекста, вы также можете попробовать установить in-memory-format в OBJECT для повышения производительности при выполнении предикатов.

...