Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spark: broadcasting jackson ObjectMapper

I have a spark application which reads lines from a files and tries to deserialize them using jackson. To get this code to work, I needed to define the ObjectMapper inside the Map operation (otherwise I got a NullPointerException).

I have the following code which is working:

val alertsData = sc.textFile(rawlines).map(alertStr => {
      val mapper = new ObjectMapper()
      mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
      mapper.registerModule(DefaultScalaModule)
      broadcastVar.value.readValue(alertStr, classOf[Alert])
    })

However, If I define the mapper outside the map and broadcast it, it fails with a NullPointerException.

This code fails:

val mapper = new ObjectMapper()
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    mapper.registerModule(DefaultScalaModule)
    val broadcastVar = sc.broadcast(mapper)

    val alertsData = sc.textFile(rawlines).map(alertStr => {
      broadcastVar.value.readValue(alertStr, classOf[Alert])
    })

What am I missing here?

Thanks, Aliza

like image 240
Aliza Avatar asked Jul 16 '26 17:07

Aliza


2 Answers

It turns out you can broadcast the mapper. The problematic part was mapper.registerModule(DefaultScalaModule) which needs to be execute on each slave (executor) machine and not only on the driver.

so this code works:

val mapper = new ObjectMapper()
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val broadcastVar = sc.broadcast(mapper)

val alertsData = sc.textFile(rawlines).map(alertStr => {
      broadcastVar.value.registerModule(DefaultScalaModule)
      broadcastVar.value.readValue(alertStr, classOf[Alert])
})

I further optimised the code by running registerModule only once per partition (and not for each element in the RDD).

val mapper = new ObjectMapper()
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

val broadcastVar = sc.broadcast(mapper)
val alertsRawData = sc.textFile(rawlines)

val alertsData = alertsRawData.mapPartitions({ iter: Iterator[String] => broadcastVar.value.registerModule(DefaultScalaModule)
      for (i <- iter) yield broadcastVar.value.readValue(i, classOf[Alert]) })

Aliza

like image 68
Aliza Avatar answered Jul 19 '26 15:07

Aliza


Indeed, objectMapper isn't really suitable for broadcast. It is inherently not serializable and not a value class anyway. I would suggest to broadcast DeserializationConfig instead and pass that to ObjectMapper's constructor from the braodcast variable in your map operation.

like image 44
Erik Schmiegelow Avatar answered Jul 19 '26 14:07

Erik Schmiegelow