Search code examples
scalastack-overflowscala-collections

Scala StackOverflowError when getting value from Map


After some refactoring we suddenly see this happening at runtime:

java.lang.StackOverflowError: null
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)

We found similar issues but none of them had exactly this trace:


Solution

  • The mentioned issues point to the lazyness of MapLike.mapValues and after some further research we found the cause.

    We have some cleanup code that is periodically called and does something like this:

      case class EvictableValue(value: String, evictionTime: Instant)
    
      val startData = Map(
        "node1" -> Map(
          "test" -> EvictableValue("bar",  Instant.now().plusSeconds(1000))
        )
      )
    
      // every n seconds we do the below code
      // here simulated by the fold
      val newData = (1 to 20000).foldLeft(startData){(data, _) =>
        data.mapValues { value =>
          value.filter(_._2.evictionTime.isBefore(Instant.now()))
        }
      }
    
      // this stack overflows
      val result = newData.get("test")
    

    the solution was to switch to Map.transform

    data.transform { (_, value) =>
        value.filter(_._2.evictionTime.isBefore(Instant.now()))
    } 
    

    or to force the view as explained here

    data.mapValues{ value =>
        value.filter(_._2.evictionTime.isBefore(Instant.now()))
    }.view.force