All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.azure.cosmos.implementation.circuitBreaker.ConsecutiveExceptionBasedCircuitBreaker Maven / Gradle / Ivy

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.cosmos.implementation.circuitBreaker;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ConsecutiveExceptionBasedCircuitBreaker {

    private static final Logger logger = LoggerFactory.getLogger(ConsecutiveExceptionBasedCircuitBreaker.class);
    private final PartitionLevelCircuitBreakerConfig partitionLevelCircuitBreakerConfig;

    public ConsecutiveExceptionBasedCircuitBreaker(PartitionLevelCircuitBreakerConfig partitionLevelCircuitBreakerConfig) {
        this.partitionLevelCircuitBreakerConfig = partitionLevelCircuitBreakerConfig;
    }

    public LocationSpecificHealthContext handleException(
        LocationSpecificHealthContext locationSpecificHealthContext,
        PartitionKeyRangeWrapper partitionKeyRangeWrapper,
        String regionWithException,
        boolean isReadOnlyRequest) {

        int exceptionCountAfterHandling
            = (isReadOnlyRequest) ? locationSpecificHealthContext.getExceptionCountForReadForCircuitBreaking() : locationSpecificHealthContext.getExceptionCountForWriteForCircuitBreaking();

        LocationHealthStatus locationHealthStatus = locationSpecificHealthContext.getLocationHealthStatus();

        switch (locationHealthStatus) {
            case Healthy:
                return locationSpecificHealthContext;
            case HealthyWithFailures:
            case HealthyTentative:

                exceptionCountAfterHandling++;
                int successCountAfterHandling = 0;

                LocationSpecificHealthContext.Builder builder = new LocationSpecificHealthContext.Builder()
                    .withUnavailableSince(locationSpecificHealthContext.getUnavailableSince())
                    .withLocationHealthStatus(locationSpecificHealthContext.getLocationHealthStatus())
                    .withExceptionThresholdBreached(locationSpecificHealthContext.isExceptionThresholdBreached());

                if (isReadOnlyRequest) {

                    return builder
                        .withSuccessCountForWriteForRecovery(locationSpecificHealthContext.getSuccessCountForWriteForRecovery())
                        .withExceptionCountForWriteForCircuitBreaking(locationSpecificHealthContext.getExceptionCountForWriteForCircuitBreaking())
                        .withSuccessCountForReadForRecovery(successCountAfterHandling)
                        .withExceptionCountForReadForCircuitBreaking(exceptionCountAfterHandling)
                        .build();

                } else {

                    return builder
                        .withSuccessCountForWriteForRecovery(successCountAfterHandling)
                        .withExceptionCountForWriteForCircuitBreaking(exceptionCountAfterHandling)
                        .withSuccessCountForReadForRecovery(locationSpecificHealthContext.getSuccessCountForReadForRecovery())
                        .withExceptionCountForReadForCircuitBreaking(locationSpecificHealthContext.getExceptionCountForReadForCircuitBreaking())
                        .build();
                }
            case Unavailable:
                // the tests done so far view this as an unreachable piece of code - but not failing the operation
                // with IllegalStateException and simply logging that a presumed unreachable code path seems to make sense for now
                logger.warn("Region {} should not be handling failures in {} health status for partition key range : {} and collection RID : {}",
                    regionWithException,
                    locationHealthStatus.getStringifiedLocationHealthStatus(),
                    partitionKeyRangeWrapper.getPartitionKeyRange().getMinInclusive() + "-" + partitionKeyRangeWrapper.getPartitionKeyRange().getMinInclusive(),
                    partitionKeyRangeWrapper.getCollectionResourceId());
                return locationSpecificHealthContext;
            default:
                throw new IllegalArgumentException("Unsupported health status : " + locationHealthStatus);
        }
    }

    public LocationSpecificHealthContext handleSuccess(
        LocationSpecificHealthContext locationSpecificHealthContext,
        PartitionKeyRangeWrapper partitionKeyRangeWrapper,
        String regionWithSuccess,
        boolean isReadOnlyRequest) {

        int exceptionCountAfterHandling
            = (isReadOnlyRequest) ? locationSpecificHealthContext.getExceptionCountForReadForCircuitBreaking() : locationSpecificHealthContext.getExceptionCountForWriteForCircuitBreaking();

        int successCountAfterHandling
            = (isReadOnlyRequest) ? locationSpecificHealthContext.getSuccessCountForReadForRecovery() : locationSpecificHealthContext.getSuccessCountForWriteForRecovery();

        LocationHealthStatus locationHealthStatus = locationSpecificHealthContext.getLocationHealthStatus();

        switch (locationHealthStatus) {
            case Healthy:
                return locationSpecificHealthContext;
            case HealthyWithFailures:

                exceptionCountAfterHandling = 0;

                LocationSpecificHealthContext.Builder builder = new LocationSpecificHealthContext.Builder()
                    .withUnavailableSince(locationSpecificHealthContext.getUnavailableSince())
                    .withLocationHealthStatus(locationSpecificHealthContext.getLocationHealthStatus())
                    .withExceptionThresholdBreached(locationSpecificHealthContext.isExceptionThresholdBreached());

                if (isReadOnlyRequest) {

                    return builder
                        .withSuccessCountForWriteForRecovery(locationSpecificHealthContext.getSuccessCountForWriteForRecovery())
                        .withExceptionCountForWriteForCircuitBreaking(locationSpecificHealthContext.getExceptionCountForWriteForCircuitBreaking())
                        .withSuccessCountForReadForRecovery(locationSpecificHealthContext.getSuccessCountForReadForRecovery())
                        .withExceptionCountForReadForCircuitBreaking(exceptionCountAfterHandling)
                        .build();

                } else {

                    return builder
                        .withSuccessCountForWriteForRecovery(locationSpecificHealthContext.getSuccessCountForWriteForRecovery())
                        .withExceptionCountForWriteForCircuitBreaking(exceptionCountAfterHandling)
                        .withSuccessCountForReadForRecovery(locationSpecificHealthContext.getSuccessCountForReadForRecovery())
                        .withExceptionCountForReadForCircuitBreaking(locationSpecificHealthContext.getExceptionCountForReadForCircuitBreaking())
                        .build();
                }
            case HealthyTentative:

                successCountAfterHandling++;

                builder = new LocationSpecificHealthContext.Builder()
                    .withUnavailableSince(locationSpecificHealthContext.getUnavailableSince())
                    .withLocationHealthStatus(locationSpecificHealthContext.getLocationHealthStatus())
                    .withExceptionThresholdBreached(locationSpecificHealthContext.isExceptionThresholdBreached());

                if (isReadOnlyRequest) {

                    return builder
                        .withSuccessCountForWriteForRecovery(locationSpecificHealthContext.getSuccessCountForWriteForRecovery())
                        .withExceptionCountForWriteForCircuitBreaking(locationSpecificHealthContext.getExceptionCountForWriteForCircuitBreaking())
                        .withSuccessCountForReadForRecovery(successCountAfterHandling)
                        .withExceptionCountForReadForCircuitBreaking(exceptionCountAfterHandling)
                        .build();

                } else {

                    return builder
                        .withSuccessCountForWriteForRecovery(successCountAfterHandling)
                        .withExceptionCountForWriteForCircuitBreaking(exceptionCountAfterHandling)
                        .withSuccessCountForReadForRecovery(locationSpecificHealthContext.getSuccessCountForReadForRecovery())
                        .withExceptionCountForReadForCircuitBreaking(locationSpecificHealthContext.getExceptionCountForReadForCircuitBreaking())
                        .build();

                }
            case Unavailable:
                // the tests done so far view this as an unreachable piece of code - but not failing the operation
                // and simply logging that a presumed unreachable code path seems to make sense for now
                logger.warn("Region {} should not be handling successes in {} health status for partition key range : {} and collection RID : {}",
                    regionWithSuccess,
                    locationHealthStatus.getStringifiedLocationHealthStatus(),
                    partitionKeyRangeWrapper.getPartitionKeyRange().getMinInclusive() + "-" + partitionKeyRangeWrapper.getPartitionKeyRange().getMinInclusive(),
                    partitionKeyRangeWrapper.getCollectionResourceId());
                return locationSpecificHealthContext;
            default:
                throw new IllegalArgumentException("Unsupported health status : " + locationHealthStatus);
        }
    }

    public boolean shouldHealthStatusBeDowngraded(LocationSpecificHealthContext locationSpecificHealthContext, boolean isReadOnlyRequest) {

        int exceptionCountActual
            = isReadOnlyRequest ? locationSpecificHealthContext.getExceptionCountForReadForCircuitBreaking() : locationSpecificHealthContext.getExceptionCountForWriteForCircuitBreaking();

        return (exceptionCountActual + 1) >= getAllowedExceptionCountToMaintainStatus(locationSpecificHealthContext.getLocationHealthStatus(), isReadOnlyRequest);
    }

    public boolean canHealthStatusBeUpgraded(LocationSpecificHealthContext locationSpecificHealthContext, boolean isReadOnlyRequest) {

        int successCountActual
            = isReadOnlyRequest ? locationSpecificHealthContext.getSuccessCountForReadForRecovery() : locationSpecificHealthContext.getSuccessCountForWriteForRecovery();

        LocationHealthStatus locationHealthStatus = locationSpecificHealthContext.getLocationHealthStatus();

        return successCountActual >= getMinimumSuccessCountForStatusUpgrade(locationHealthStatus, isReadOnlyRequest);
    }

    public int getAllowedExceptionCountToMaintainStatus(LocationHealthStatus status, boolean isReadOnlyRequest) {

        if (isReadOnlyRequest) {
            switch (status) {
                case HealthyWithFailures:
                    return this.partitionLevelCircuitBreakerConfig.getConsecutiveExceptionCountToleratedForReads();
                case HealthyTentative:
                    return this.partitionLevelCircuitBreakerConfig.getConsecutiveExceptionCountToleratedForReads() / 2;
                case Healthy:
                case Unavailable:
                    return 0;
                default:
                    throw new IllegalArgumentException("Unsupported health status: " + status);
            }
        } else {
            switch (status) {
                case HealthyWithFailures:
                    return this.partitionLevelCircuitBreakerConfig.getConsecutiveExceptionCountToleratedForWrites();
                case HealthyTentative:
                    return this.partitionLevelCircuitBreakerConfig.getConsecutiveExceptionCountToleratedForWrites() / 2;
                case Healthy:
                case Unavailable:
                    return 0;
                default:
                    throw new IllegalArgumentException("Unsupported health status: " + status);
            }
        }
    }

    public int getMinimumSuccessCountForStatusUpgrade(LocationHealthStatus status, boolean isReadOnlyRequest) {
        if (isReadOnlyRequest) {
            switch (status) {
                case HealthyTentative:
                    return this.partitionLevelCircuitBreakerConfig.getConsecutiveExceptionCountToleratedForReads();
                case Unavailable:
                case HealthyWithFailures:
                case Healthy:
                    return 0;
                default:
                    throw new IllegalArgumentException("Unsupported health status: " + status);
            }
        } else {
            switch (status) {
                case HealthyTentative:
                    return this.partitionLevelCircuitBreakerConfig.getConsecutiveExceptionCountToleratedForWrites();
                case Unavailable:
                case HealthyWithFailures:
                case Healthy:
                    return 0;
                default:
                    throw new IllegalArgumentException("Unsupported health status: " + status);
            }
        }
    }

    public boolean isPartitionLevelCircuitBreakerEnabled() {
        return this.partitionLevelCircuitBreakerConfig.isPartitionLevelCircuitBreakerEnabled();
    }

    public PartitionLevelCircuitBreakerConfig getPartitionLevelCircuitBreakerConfig() {
        return partitionLevelCircuitBreakerConfig;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy