У нас есть несколько экземпляров одного и того же приложения, развернутого за ELB (Load Balancer). Всякий раз, когда выполняется определенная работа, мы подсчитываем некоторые элементы и затем хотим увеличить значение счетчика.
Мы используем ElastiCache для хранения этих метрик в памяти. Мы настроили его как экземпляры Cluster of Redis.
У меня возникают проблемы с пониманием того, как правильно взаимодействовать с ElastiCache, чтобы счетчик никогда не пропускал никакого приращения (то есть операция atomi c). Я знаю, что INCRBY
, кажется, путь к go, но я не уверен, как настроить Spring Data, чтобы я мог выполнить команду Redis для моего Master
. На самом деле, наш подход даже не ориентирован на многопоточность, но вот код:
@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)));
}
}
Для моего Repository
кажется, что единственные релевантные операции, которые я могу выполнить, это эквиваленты get
и set
. Как мне настроить мой код так, чтобы я мог выдавать фактическую команду Redis для моего Кластера, таким образом используя преимущества атома c природы примитивов (здесь INCRBY
), которые я хочу использовать?