How do you handle errors and recover from failures in an Akka Streams application? Explain the different strategies available with examples.

In Akka Streams, error handling and recovery are achieved through supervision strategies. There are three primary strategies: Resume, Restart, and Stop.

1. Resume : Continues processing after an error, skipping the failed element. Useful for non-critical errors.

Example :
val decider: Supervision.Decider = {
  case _: ArithmeticException => Supervision.Resume
  case _                       => Supervision.Stop
}?

2. Restart : Resets the stage’s internal state and continues processing from the next element. Suitable for transient errors.

Example :
val decider: Supervision.Decider = {
  case _: IllegalStateException => Supervision.Restart
  case _                         => Supervision.Stop
}?

3. Stop : Terminates the stream upon encountering an error. Appropriate for critical failures.

Example :
val decider: Supervision.Decider = {
  case _: IllegalArgumentException => Supervision.Stop
  case _                           => Supervision.Resume
}?

To apply a strategy, use withAttributes on a Flow or Sink :
val flowWithSupervision = myFlow.withAttributes(ActorAttributes.supervisionStrategy(decider))?