Неправильная десериализация лямбда с Infinispan Cache и computeIfAbsent - PullRequest
0 голосов
/ 01 апреля 2019

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

Я в основном реализую общую карту, содержащую только одно целое число

Вот код моей службы

package sandbox.infinispan.test.service;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.infinispan.Cache;

@Named("useThisOne")
@ApplicationScoped
public class CounterService implements ICounterService {

    private static final String KEY = "key";

    @Inject
    private Cache<String, Integer> cache;

    @Override
    public void inc(final int amount) {
        this.cache.put(KEY, Integer.valueOf(this.get() + amount));
    }

    @Override
    public int get() {
        return this.cache.computeIfAbsent(KEY, k -> Integer.valueOf(0)).intValue();
    }
}

Кэш создается со следующими данными:

package sandbox.infinispan.test.config;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;

import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;

@Dependent
class CacheProvider {

    @Produces
    @ApplicationScoped
    private EmbeddedCacheManager defaultClusteredCacheManager() {
        final GlobalConfiguration g = new GlobalConfigurationBuilder() //
                .clusteredDefault() //
                .transport() //
                .nodeName(this.getNodeName()) //
                .clusterName("infinispanTestCluster") //
                .build();
        final Configuration cfg = new ConfigurationBuilder() //
                .clustering() //
                .cacheMode(CacheMode.REPL_SYNC) ///
                .build();
        return new DefaultCacheManager(g, cfg);
    }
}

Если в кластере есть хотя бы два сервера, computeIfAbsent завершается неудачно с

15:48:50,253 ERROR [org.infinispan.interceptors.impl.InvocationContextInterceptor] (jgroups-7,myhostname-14393) ISPN000136: Error executing command ComputeIfAbsentCommand, writing keys [key]: org.infinispan.remoting.RemoteException: ISPN000217: Received exception from otherhostname-44445, see cause for remote stack trace

, который детализируется до:

Caused by: java.lang.NoSuchMethodException: sandbox.infinispan.test.service.CounterService.$deserializeLambda$(java.lang.invoke.SerializedLambda)

и, наконец, до:

Caused by: java.lang.IllegalArgumentException: Invalid lambda deserialization
        at sandbox.infinispan.test.service.CounterService.$deserializeLambda$(CounterService.java:10)
            ... 68 more
Caused by: an exception which occurred:
        in object of type java.lang.invoke.SerializedLambda

Если я переписываю свою симпатичнуюхороший модный код для уродливого последователя, он работает.

@Override
public int get() {
    Integer value = this.cache.get(KEY);
    if (value == null) {
        value = Integer.valueOf(0);
        this.cache.put(KEY, value);
    }
    return value.intValue();
}

Как я могу использовать прекрасный computeIfAbsent способ делать вещи в наше время?


Eclipse 2018-12, WildFly 14, java 10 on члена кластера, CentOs 7, OpenJdk 10, WildFly 14 на члене удаленного кластера.

Спасибо за вашу помощь


Решено (вроде)

Благодаря полученной здесь помощи я превратил лямбду во внутренний классs:

static class OhWell implements Serializable {

    static Integer zero(final String t) {
        return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, OhWell::zero).intValue();
}

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


Дальнейшие результаты:

Следующее static внутренний класс с static метод работает

static class StaticOhWell implements Serializable {

    static Integer apply(final String t) {
        return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, StaticOhWell::apply).intValue();
}

Следующий не статичный внутренний класс с не статичный метод терпит неудачу:

class NotStaticOhWell implements SerializableFunction<String, Integer> {

    @Override
    public Integer apply(final String t) {
            return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, new NotStaticOhWell()::apply).intValue();
}

Он завершается ошибкой с этим сообщением об ошибке NotSerializableException: org.infinispan.cache.impl.EncoderCache :

13:41:29,221 ERROR [org.infinispan.interceptors.impl.InvocationContextInterceptor] (default task-1) ISPN000136: Error executing command ComputeIfAbsentCommand, writing keys [value]: org.infinispan.commons.marshall.NotSerializableException: org.infinispan.cache.impl.EncoderCache
    Caused by: an exception which occurred:
    in field sandbox.infinispan.test.service.CounterService.cache
    in object sandbox.infinispan.test.service.CounterService@4612a6c3
    in field sandbox.infinispan.test.service.CounterService$NotStaticOhWell.this$0
    in object sandbox.infinispan.test.service.CounterService$NotStaticOhWell@4effd362
    in field java.lang.invoke.SerializedLambda.capturedArgs
    in object java.lang.invoke.SerializedLambda@e62f08a
    in object sandbox.infinispan.test.service.CounterService$$Lambda$1195/1060417313@174a143b

Заключительные слова (?)

Использование «статической лямбды» (внутренний класс static , реализующий интерфейс SerializableFunction ) тоже работает

static class StaticSerializableFunction implements SerializableFunction<String, Integer> {

    @Override
    public Integer apply(final String t) {
        return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, new StaticSerializableFunction()::apply).intValue();
}


И победителем является ...

Создание класса, фактически сериализуемого с помощью « transienting », Cache позволяетпросто используйте метод этого класса.Не нужно создавать внутренний класс!

package sandbox.infinispan.test.service;

import java.io.Serializable;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.infinispan.Cache;

@Named("useThisOne")
@ApplicationScoped
public class CounterService implements ICounterService, Serializable {

    private static final String KEY = "value";

    @SuppressWarnings("cdi-ambiguous-dependency")
    @Inject
    private transient Cache<String, Integer> cache;

    @Override
    public void inc(final int amount) {
        this.cache.put(KEY, Integer.valueOf(this.get() + amount));
    }

    @Override
    public int get() {
        return this.cache.computeIfAbsent(KEY, this::zero).intValue();
    }

    private Integer zero(@SuppressWarnings("unused") final String unused) {
        return Integer.valueOf(0);
    }

    @Override
    public void reset() {
        this.cache.clear();
    }
}

Спасибо всем!

1 Ответ

3 голосов
/ 02 апреля 2019

Согласно Невозможно десериализовать лямбду десериализатору необходим фактический код, который будет доступен. Вы уверены, что ваше приложение уже запущено на всех других узлах кластера (точно такая же версия, включая вашу лямбду)?

computeIfAbsent() отправляет лямбду напрямую в данные (и, следовательно, обрабатывает операцию, используя один RPC, вместо того, чтобы сначала извлечь значение и затем записать его как «некрасивый код»). В WF ваше приложение находится в загрузчике классов (модуле), отличном от Infinispan, и это может вызвать проблему.

Не могли бы вы попытаться преобразовать вашу лямбду в класс и посмотреть, не возникнет ли у вас аналогичная проблема? Я не очень хорошо разбираюсь в WF, так что может быть смягчение для обычных классов, которых нет в лямбдах.

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