Uploaded image for project: 'Spark'
  1. Spark
  2. SPARK-22465

Cogroup of two disproportionate RDDs could lead into 2G limit BUG

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Critical
    • Resolution: Fixed
    • Affects Version/s: 1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, 1.2.0, 1.2.1, 1.2.2, 1.3.0, 1.3.1, 1.4.0, 1.4.1, 1.5.0, 1.5.1, 1.5.2, 1.6.0, 1.6.1, 1.6.2, 1.6.3, 2.0.0, 2.0.1, 2.0.2, 2.1.0, 2.1.1, 2.1.2, 2.2.0
    • Fix Version/s: 2.3.0
    • Component/s: Spark Core
    • Labels:
      None

      Description

      While running my spark pipeline, it failed with the following exception

      2017-11-03 04:49:09,776 [Executor task launch worker for task 58670] ERROR org.apache.spark.executor.Executor  - Exception in task 630.0 in stage 28.0 (TID 58670)
      java.lang.IllegalArgumentException: Size exceeds Integer.MAX_VALUE
      	at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:869)
      	at org.apache.spark.storage.DiskStore$$anonfun$getBytes$2.apply(DiskStore.scala:103)
      	at org.apache.spark.storage.DiskStore$$anonfun$getBytes$2.apply(DiskStore.scala:91)
      	at org.apache.spark.util.Utils$.tryWithSafeFinally(Utils.scala:1303)
      	at org.apache.spark.storage.DiskStore.getBytes(DiskStore.scala:105)
      	at org.apache.spark.storage.BlockManager.getLocalValues(BlockManager.scala:469)
      	at org.apache.spark.storage.BlockManager.getOrElseUpdate(BlockManager.scala:705)
      	at org.apache.spark.rdd.RDD.getOrCompute(RDD.scala:334)
      	at org.apache.spark.rdd.RDD.iterator(RDD.scala:285)
      	at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
      	at org.apache.spark.scheduler.Task.run(Task.scala:99)
      	at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:324)
      	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      	at java.lang.Thread.run(Thread.java:745)
      

      After debugging I found that the issue lies with how spark handles cogroup of two RDDs.
      Here is the relevant code from apache spark

       /**
         * For each key k in `this` or `other`, return a resulting RDD that contains a tuple with the
         * list of values for that key in `this` as well as `other`.
         */
        def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))] = self.withScope {
          cogroup(other, defaultPartitioner(self, other))
        }
      
      
      /**
         * Choose a partitioner to use for a cogroup-like operation between a number of RDDs.
         *
         * If any of the RDDs already has a partitioner, choose that one.
         *
         * Otherwise, we use a default HashPartitioner. For the number of partitions, if
         * spark.default.parallelism is set, then we'll use the value from SparkContext
         * defaultParallelism, otherwise we'll use the max number of upstream partitions.
         *
         * Unless spark.default.parallelism is set, the number of partitions will be the
         * same as the number of partitions in the largest upstream RDD, as this should
         * be least likely to cause out-of-memory errors.
         *
         * We use two method parameters (rdd, others) to enforce callers passing at least 1 RDD.
         */
        def defaultPartitioner(rdd: RDD[_], others: RDD[_]*): Partitioner = {
          val rdds = (Seq(rdd) ++ others)
          val hasPartitioner = rdds.filter(_.partitioner.exists(_.numPartitions > 0))
          if (hasPartitioner.nonEmpty) {
            hasPartitioner.maxBy(_.partitions.length).partitioner.get
          } else {
            if (rdd.context.conf.contains("spark.default.parallelism")) {
              new HashPartitioner(rdd.context.defaultParallelism)
            } else {
              new HashPartitioner(rdds.map(_.partitions.length).max)
            }
          }
        }
      
      

      Given this suppose we have two pair RDDs.
      RDD1 : A small RDD which fewer data and partitions
      RDD2: A huge RDD which has loads of data and partitions

      Now in the code if we were to have a cogroup

      val RDD3 = RDD1.cogroup(RDD2)
      

      there is a case where this could lead to the SPARK-6235 Bug which is If RDD1 has a partitioner when it is being called into a cogroup. This is because the cogroups partitions are then decided by the partitioner and could lead to the huge RDD2 being shuffled into a small number of partitions.

      One way is probably to add a safety check here that would ignore the partitioner if the number of partitions on the two RDDs are very different in magnitude.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              amitkumar Amit Kumar
              Shepherd:
              Thomas Graves
            • Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: