All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sportradar.unifiedodds.sdk.impl.ProducerInfo Maven / Gradle / Ivy
/*
* Copyright (C) Sportradar AG. See LICENSE for full license governing this code
*/
package com.sportradar.unifiedodds.sdk.impl;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sportradar.unifiedodds.sdk.MessageInterest;
import com.sportradar.unifiedodds.sdk.ProducerScope;
import com.sportradar.unifiedodds.sdk.oddsentities.Producer;
import com.sportradar.unifiedodds.sdk.oddsentities.ProducerDownReason;
import com.sportradar.unifiedodds.sdk.oddsentities.ProducerStatusReason;
import com.sportradar.utils.URN;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A class used to access producer data & store additional recovery related information
*/
class ProducerInfo {
private static final Logger logger = LoggerFactory.getLogger(ProducerInfo.class);
private final int producerId;
private final SDKProducerManager producerManager;
private final Set interestsOfSnapshotComplete = Sets.newConcurrentHashSet();
private final Map eventRecoveries = Maps.newConcurrentMap();
private volatile int recoveryId = 0;
private volatile long recoveryStartedAt = 0;
private volatile RecoveryState recoveryState = RecoveryState.NotStarted;
private volatile long lastSystemAliveReceivedTimestamp = 0;
private volatile boolean firstRecoveryCompleted = false;
private volatile ProducerDownReason producerDownReason;
private volatile ProducerStatusReason producerStatusReason;
private volatile long lastUserSessionAliveReceivedTimestamp = 0;
private volatile long lastValidAliveGenTimestampInRecovery;
ProducerInfo(int producerId, SDKProducerManager producerManager) {
Preconditions.checkNotNull(producerManager);
Preconditions.checkArgument(producerId > 0);
this.producerId = producerId;
this.producerManager = producerManager;
}
void onFirstRecoveryCompleted() {
firstRecoveryCompleted = true;
}
void onEventRecoveryCompleted(long recoveryId) {
eventRecoveries.remove(recoveryId);
}
void onUserSessionAliveReceived(long aliveGenTimestamp) {
lastUserSessionAliveReceivedTimestamp = aliveGenTimestamp;
}
void onSystemAliveReceived(long receivedTimestamp, long aliveGenTimestamp) {
lastSystemAliveReceivedTimestamp = receivedTimestamp;
if (!isFlaggedDown()) {
producerManager.setLastAliveReceivedGenTimestamp(producerId, aliveGenTimestamp);
}
if (recoveryState == RecoveryState.Started) {
lastValidAliveGenTimestampInRecovery = aliveGenTimestamp;
}
}
// validation to ensure the dispatch of the producerUp event only once if we have more sessions
boolean validateSnapshotComplete(long recoveryId, MessageInterest messageInterest) {
if (!isPerformingRecovery()) {
return false;
}
if (this.recoveryId != recoveryId){
return false;
}
if (!isSnapshotValidationNeeded(messageInterest)) {
return true;
}
interestsOfSnapshotComplete.add(messageInterest);
return validateProducerSnapshotCompletes(interestsOfSnapshotComplete);
}
boolean validateEventSnapshotComplete(long recoveryId, MessageInterest messageInterest) {
if (!eventRecoveries.containsKey(recoveryId)) {
return false;
}
EventRecovery eventRecovery = eventRecoveries.get(recoveryId);
if (eventRecovery.getRecoveryId() != recoveryId) {
return false;
}
if (!isSnapshotValidationNeeded(messageInterest)) {
return true;
}
Set receivedSnapshotCompletes = eventRecovery.onSnapshotCompleteReceived(messageInterest);
return validateProducerSnapshotCompletes(receivedSnapshotCompletes);
}
boolean isKnownRecovery(long requestId) {
return recoveryId == requestId || eventRecoveries.containsKey(requestId);
}
boolean isPerformingRecovery() {
return recoveryState == RecoveryState.Started || recoveryState == RecoveryState.Interrupted;
}
boolean isDisabled() {
return !producerManager.isProducerEnabled(producerId);
}
boolean isFlaggedDown() {
return producerManager.isProducerDown(producerId);
}
boolean isFirstRecoveryCompleted() {
return firstRecoveryCompleted;
}
int getProducerId() { return producerId; }
RecoveryState getRecoveryState() {
return recoveryState;
}
int getCurrentRecoveryId() {
return recoveryId;
}
long getLastRecoveryStartedAt() {
return recoveryStartedAt;
}
EventRecovery getEventRecoveryData(long recoveryId) {
return eventRecoveries.get(recoveryId);
}
long getTimestampForRecovery() {
return producerManager.getProducer(producerId).getTimestampForRecovery();
}
long getLastSystemAliveReceivedTimestamp() {
return lastSystemAliveReceivedTimestamp;
}
long getLastMessageReceivedTimestamp() {
return producerManager.getProducer(producerId).getLastMessageTimestamp();
}
long getLastProcessedMessageGenTimestamp() {
return producerManager.getProducer(producerId).getLastProcessedMessageGenTimestamp();
}
String getProducerApiUrl() {
return producerManager.getProducer(producerId).getApiUrl();
}
ProducerDownReason getProducerDownReason() {
return producerDownReason;
}
ProducerStatusReason getProducerStatusReason() {
return producerStatusReason;
}
long getLastValidAliveGenTimestampInRecovery() {
return lastValidAliveGenTimestampInRecovery;
}
long getLastUserSessionAliveReceivedTimestamp() {
return lastUserSessionAliveReceivedTimestamp;
}
int getStatefulRecoveryWindowInMinutes() {
return producerManager.getProducer(producerId).getStatefulRecoveryWindowInMinutes();
}
void setProducerRecoveryState(int recoveryId, long recoveryStartedAt, RecoveryState recoveryState) {
logger.info("{} recovery state set to: recoveryId[{}], recoveryStartedAt[{}], recoveryState[{}]", this, recoveryId, recoveryStartedAt, recoveryState);
this.recoveryState = recoveryState;
if (recoveryState == RecoveryState.Interrupted) {
return;
}
this.recoveryId = recoveryId;
this.recoveryStartedAt = recoveryStartedAt;
}
void setProducerDown(boolean down, ProducerDownReason pdReason) {
producerManager.setProducerDown(producerId, down);
producerDownReason = pdReason;
if (down) {
eventRecoveries.clear();
}
}
void setProducerStatusReason(ProducerStatusReason reason) {
producerStatusReason = reason;
}
void setLastMessageReceivedTimestamp(long timestamp) {
producerManager.internalSetProducerLastMessageTimestamp(producerId, timestamp);
}
void setLastProcessedMessageGenTimestamp(long lastProcessedMessageGenTimestamp) {
producerManager.setLastProcessedMessageGenTimestamp(producerId, lastProcessedMessageGenTimestamp);
}
void setEventRecoveryState(URN eventId, long recoveryId, long recoveryStartedAt) {
if (recoveryId == 0 && recoveryStartedAt == 0) {
eventRecoveries.remove(recoveryId);
} else {
if (eventRecoveries.values().stream().anyMatch(r -> r.getEventId().equals(eventId))) {
logger.info("Requested event recovery, but the previous event recovery was still in progress(recovery restarted). Producer: {}, eventId: {}", this, eventId);
}
eventRecoveries.put(recoveryId, new EventRecovery(eventId, recoveryId, recoveryStartedAt));
}
}
private boolean isSnapshotValidationNeeded(MessageInterest messageInterest) {
// if the message interest is not from live || not from prematch it means its a ("high/low" or "AllMessages" setup),
// both of which handle snapshots by themselves
return messageInterest == MessageInterest.LiveMessagesOnly ||
messageInterest == MessageInterest.PrematchMessagesOnly ||
messageInterest == MessageInterest.VirtualSports;
}
private boolean validateProducerSnapshotCompletes(Set receivedSnapshotCompletes) {
Map validationMap =
producerManager.getProducer(producerId)
.getProducerScopes()
.stream()
.collect(Collectors.toMap(k -> k, v -> this.msgInterest2scopeValidation(receivedSnapshotCompletes, v)));
return !validationMap.containsValue(false);
}
private boolean msgInterest2scopeValidation(Set interestsOfSnapshotComplete, ProducerScope scope) {
Preconditions.checkNotNull(interestsOfSnapshotComplete);
Preconditions.checkNotNull(scope);
switch (scope) {
case Live:
return interestsOfSnapshotComplete.contains(MessageInterest.LiveMessagesOnly);
case Prematch:
return interestsOfSnapshotComplete.contains(MessageInterest.PrematchMessagesOnly);
case Virtuals:
return interestsOfSnapshotComplete.contains(MessageInterest.VirtualSports);
default:
return interestsOfSnapshotComplete.containsAll(
Lists.newArrayList(MessageInterest.LiveMessagesOnly, MessageInterest.PrematchMessagesOnly)
);
}
}
@Override
public String toString() {
Producer producer = producerManager.getProducer(producerId);
return String.format("Producer[%d %s]", producer.getId(), producer.getName());
}
/**
* A simple storage class used to cache the recovery requests state
*/
class EventRecovery {
private final URN eventId;
private final long recoveryId;
private final long recoveryStartedAt;
private final Set interestsOfSnapshotComplete = Sets.newConcurrentHashSet();
private EventRecovery(URN eventId, long recoveryId, long recoveryStartedAt) {
Preconditions.checkNotNull(eventId);
Preconditions.checkArgument(recoveryId > 0);
Preconditions.checkArgument(recoveryStartedAt > 0);
this.eventId = eventId;
this.recoveryId = recoveryId;
this.recoveryStartedAt = recoveryStartedAt;
}
URN getEventId() {
return eventId;
}
long getRecoveryId() {
return recoveryId;
}
long getRecoveryStartedAt() {
return recoveryStartedAt;
}
private Set onSnapshotCompleteReceived(MessageInterest msgInterest) {
Preconditions.checkNotNull(msgInterest);
interestsOfSnapshotComplete.add(msgInterest);
return interestsOfSnapshotComplete;
}
}
}