Как реализовать атомарную перекрестную JVM-операцию вычисления, если-нужно-и-записи в Redis с использованием RedisTemplate? - PullRequest
0 голосов
/ 30 апреля 2019

Мне нужно реализовать кэширование запрошенных данных (с моим собственным TTL - см. Переменную expirationTimeInSeconds; по этой причине я не использую абстракцию @Cacheable из Spring) в Redis, используя Spring RedisTemplate.

Итак, я не хочу делать чрезмерные звонки (computer.compute (..) в фрагменте ниже), если данные уже есть. Проблема в том, что у меня есть несколько экземпляров в центре обработки данных, а не в одной JVM. Следовательно, параллелизм Java здесь не поможет, только на одном уровне JVM.

Таким образом, в моем решении ниже я всегда получаю пустые данные (пустую карту, см. "// Always True = (" комментарий) между открытием и закрытием Redis transaction, т.е. после operations.multi(), но между operations.exec().

String multiKey = compoundKey(channelId + "." + sourceId);
        try {
            redisTemplate.execute(new SessionCallback<HashOperations<String, Object, Object>>() {
                @Override
                public HashOperations<String, Object, Object> execute(RedisOperations operations) throws DataAccessException {
                    operations.multi();

                    HashOperations<String, Object, Object> hashOperations = operations.opsForHash();
                    Map<Object, Object> values = hashOperations.entries(multiKey);
                    // Always True =(
                    if (CollectionUtils.isEmpty(values)) {
                        dataHolder.setValue(computer.apply(channelId, sourceId));
                        if (dataHolder.getValue() != null) {
                            MyData data = dataHolder.getValue();
                            Map<String, String> data = new HashMap<>();
                            data.put(CHANNEL_NAME_KEY, data.getChannelName());
                            data.put(CHANNEL_LABEL_KEY, data.getChannelLabel());
                            data.put(SOURCE_NAME_KEY, data.getSourceName());

                            hashOperations.putAll(multiKey, data);
                            operations.expire(multiKey, expirationTimeInSeconds, TimeUnit.SECONDS);
                        }
                    } else {
                        dataHolder.setValue(new MyData(values.get(CHANNEL_NAME_KEY).toString(),
                                values.get(CHANNEL_LABEL_KEY).toString(), values.get(SOURCE_NAME_KEY).toString()));
                    }

                    operations.exec();
                    return null;
                }
            });
        } catch (Exception e) {
            log.error("Error while obtaining MyData backed by Redis for channelId {}, sourceId {}: {}",
                    channelId, sourceId, e.getMessage(), e);
        }

Почему это так? Может кто-нибудь объяснить, что я делаю неправильно, и что может быть возможным здесь исправить?

...