How to handle Spark write errors?

Viewed 747

this is a question about Spark error handling.
Specifically -- handling errors on writes to the target data storage.

The Situation

I'm writing into a non-transactional data storage that does not (in my case) support idempotent inserts — and want to implement error handling for write failures — to avoid inserting data multiple times.

So the scenario I'd like to handle is:

  1. created dataframe / DAG
  2. all executed, read data successfully, persisted within spark job (can be in memory)
  3. writing to the target — but that throws an exception / fails midway / is unavailable

In this scenario, Spark would retry the write — without the ability to roll back (due to the nature of the custom target data store) — and thus potentially duplicate data.

The Question. What is the proper approach in Spark to handle such cases ?

Option 1. Is there a way to add an exception handler at task level ? For a specific task ?

Option 2. Could set max retries to 1 so that the whole app would fail — and cleanup could be done externally — but would like to do better than that :)

Option 3. As an alternative — we could add an extra column to the dataframe, one that would be computed at runtime and be unique across retries (so we could, again, clean it all up externally later). The question is then — what would be the way to compute a column literal at runtime of the Spark job (and not during DAG creation) ?

So...

Given the question -- what options are there ?
If it's any of the three proposed -- how can it be implemented ?

Would appreciate very much any help on this matter!
Thanks in advance...

1 Answers

I would implement the error handling at the driver level, but this comes at the cost of additional queries (which you would need anyways). Basically you need to

  1. check if your (new) data contains duplicates
  2. your target table/datastore already contains this data

    val df : DataFrame = ???
    
    df.cache() // if that fits into memory
    
    val uniqueKeys = Seq("ID")
    val noDuplicates = df.groupBy(uniqueKeys.head,uniqueKeys.tail:_*).count().where($"count">0).isEmpty
    val notAlreadyExistsInTarget = spark.table(<targettable>).join(df,uniqueKeys,"leftsemi").isEmpty
    
    if(noDuplicates && notAlreadyExistsInTarget) {
      df.write // persist to your datastore
    } else {
      throw new Exception("df contains duplicates / already exists")
    }
    
Related