Circuit breaker and business validations

scala

(Manoj Waikar) #1

Hi,

Since Lagom doesn’t allow a service to return any of the Container(s), like Option, Try or Either, my main question is how to handle business validations without triggering the circuit breaker?

In our case, when a POST request is being fired, we need to do some validations (e.g. duplicate record etc. or other business validations). Now currently, if the validation failed, I was throwing a BadRequest, however, if the same (validation failing) request is fired multiple times, it trips the circuit breaker. So to avoid tripping it, I used the following code in my application.conf file (as mentioned in the documentation) -

lagom.circuit-breaker {

  # Default configuration that is used if a configuration section
  # with the circuit breaker identifier is not defined.
  default {
    # Possibility to disable a given circuit breaker.
    enabled = on

    # Number of failures before opening the circuit.
    max-failures = 10

    # Duration of time after which to consider a call a failure.
    call-timeout = 10s

    # Duration of time in open state after which to attempt to close
    # the circuit, by first entering the half-open state.
    reset-timeout = 15s

    # A whitelist of fqcn of Exceptions that the CircuitBreaker
    # should not consider failures. By default all exceptions are
    # considered failures.
    exception-whitelist = ["com.lightbend.lagom.scaladsl.api.transport.BadRequest", "com.lightbend.lagom.scaladsl.api.transport.NotFound"]
  }
}

Please note that I’ve included BadRequest in the whitelist. Even after doing this, if I was firing the bad request 10 times (max-failures), the circuit breaker was getting tripped.

So, I included this in my Service class -

.withCircuitBreaker(
      CircuitBreaker.identifiedBy("default")
    )

Still it doesn’t help.

Finally, I created a ValidationFailed class, like -

object CustomTransportErrorCode {
  val BadData: TransportErrorCode = TransportErrorCode(600, -1003, "Invalid/Bad Data")
}

final class ValidationFailed(errorCode: TransportErrorCode, exceptionMessage: ExceptionMessage, cause: Throwable) extends TransportException(errorCode, exceptionMessage, cause) {
  def this(errorCode: TransportErrorCode, exceptionMessage: ExceptionMessage) = this(errorCode, exceptionMessage, null)
}

object ValidationFailed {
  val ErrorCode = CustomTransportErrorCode.BadData

  def apply(message: String) = new ValidationFailed(
    ErrorCode,
    new ExceptionMessage(classOf[ValidationFailed].getSimpleName, message), null
  )

  def apply(cause: Throwable) = new ValidationFailed(
    ErrorCode,
    new ExceptionMessage(classOf[ValidationFailed].getSimpleName, cause.getMessage), cause
  )
}

Please note, I’ve kept the Http code as 600 (because circuit breaker gets tripped by all HTTP 4xx and 5xx codes).

Even after doing all this, I am not able to avoid circuit breaker getting tripped due to business validation failures. Please let me know how to handle this?


(Alan Klikic) #2

@mmwaikar are you sure you are configuring CB on the right place (it has to be configured in the service that is using the api to initiate a call and not the one implementing it)? Can you try disabling CB to see what happens?


(Manoj Waikar) #3

Thanks a lot Alan for your suggestion.

I removed all configuration from the implementing services and put it inside the web-gateway (from where all the other services were being called), and the whitelisting works fine for BadRequest. I didn’t need to put any code in the service definition or create any custom exception.