Atomic increment with Spring Data for AWS ElastiCache (Redis)

不问归期 提交于 2020-03-25 19:16:07

问题


We have multiple instances of the same application deployed behind an ELB (Load Balancer). Whenever a certain job is done, we count some elements and then want to increment a counter's value.

We use ElastiCache to hold these metrics in memory. We have set it up as a Cluster of Redis instances.

I'm having trouble understanding how to properly interact with ElastiCache so that the counter never misses any increment (i.e. an atomic operation). I know INCRBY seems to be the way to go, but I'm not sure how to set up Spring Data so that I can issue a Redis command to my Master. As it is, our approach isn't even thread-safe, but here is the code:

@Slf4j
@Service
@RequiredArgsConstructor
public class MetricServiceImpl implements MetricService {

    private final IntegerMetricRepository integerMetricRepository;

    private static final BigInteger ZERO = BigInteger.ZERO;


    @Override
    public long countRealJobs(List<Job> newJobs) {
        return newJobs.stream()
                .filter(job -> !job.isFake())
                .count();
    }

    @Override
    public long countRealDrafts(List<Draft> drafts) {
        return drafts.stream()
                .filter(draft -> !draft.getString(JsonFields.TITLE.getValue())
                        .contains("FAKE"))
                .count();
    }

    @Override
    public IntegerMetric increment(IntegerMetricType integerMetricType, long amount) {
        IntegerMetric metric = getOrInitialize(integerMetricType);
        BigInteger newValue = metric.getValue().add(BigInteger.valueOf(amount));
        metric.setValue(newValue.max(ZERO)); // smallest possible value is 0
        return integerMetricRepository.save(metric);
    }

    @Override
    public BigInteger getValue(IntegerMetricType integerMetricType) {
        return getOrInitialize(integerMetricType).getValue();
    }

    @Override
    public IntegerMetric setValue(IntegerMetricType integerMetricType, long amount) {
        IntegerMetric metric = getOrInitialize(integerMetricType);

        if (amount < 0) { // negatives not allowed
            log.info("Tried to set a negative value for an IntegerMetric.");
            return metric;
        }

        metric.setValue(BigInteger.valueOf(amount));
        return integerMetricRepository.save(metric);
    }

    /**
     * @param integerMetricType the desired Entity
     * @return either the Entity which already existed, or a new one initialized to {@code ZERO}.
     */
    private IntegerMetric getOrInitialize(IntegerMetricType integerMetricType) {
        return integerMetricRepository.findById(integerMetricType).orElseGet(
                () -> integerMetricRepository.save(new IntegerMetric(integerMetricType, ZERO)));
    }
}

For my Repository, it seems like the only relevant operations I can issue are the equivalents of get and set. How do I set up my code so that I can issue actual Redis command to my Cluster, thus taking advantage of the atomic nature of the primitives (here, INCRBY) I want to use?


回答1:


The solution resides in the usage of RedisTemplate. With that class, it becomes possible to use the "AtomicCounter" that Redis natively supports (through operations such as INCRBY).



来源:https://stackoverflow.com/questions/60305483/atomic-increment-with-spring-data-for-aws-elasticache-redis

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!