anchor.diff:  val hbaseContext = new HBaseContext(sc, config)
   
   val stagingFolder = ...
  +val rdd = sc.parallelize(Array(
  +      (Bytes.toBytes("1"),
  +        (Bytes.toBytes(columnFamily1), Bytes.toBytes("a"), Bytes.toBytes("foo1"))),
  +      (Bytes.toBytes("3"),
  +        (Bytes.toBytes(columnFamily1), Bytes.toBytes("b"), Bytes.toBytes("foo2.b"))), ...
   
   rdd.hbaseBulkLoad(TableName.valueOf(tableName),
     t => {
  
positive.code: 
  == Bulk Load
  
  Spark bulk load follows very closely to the MapReduce implementation of bulk
  load. In short, a partitioner partitions based on region splits and
  the row keys are sent to the reducers in order, so that HFiles can be written
  out. In Spark terms, the bulk load will be focused around a
  `repartitionAndSortWithinPartitions` followed by a `foreachPartition`.
  
  The only major difference with the Spark implementation compared to the
  MapReduce implementation is that the column qualifier is included in the shuffle
  ordering process. This was done because the MapReduce bulk load implementation
  would have memory issues with loading rows with a large numbers of columns, as a
  result of the sorting of those columns being done in the memory of the reducer JVM.
  Instead, that ordering is done in the Spark Shuffle, so there should no longer
  be a limit to the number of columns in a row for bulk loading.
  
  .Bulk Loading Example
  ====
  
negative.code:   conn.getAdmin, table, conn.getRegionLocator(TableName.valueOf(tableName)))
  ----
  ====
  
  Now lets look at how you would call the thin record bulk load implementation
  
  .Using thin record bulk load
  ====
  
  [source, scala]
  ----
  val sc = new SparkContext("local", "test")
  val config = new HBaseConfiguration()
  
  val hbaseContext = new HBaseContext(sc, config)
  
  val stagingFolder = ...
  val rdd = sc.parallelize(Array(
        ("1",
          (Bytes.toBytes(columnFamily1), Bytes.toBytes("a"), Bytes.toBytes("foo1"))),
        ("3",
          (Bytes.toBytes(columnFamily1), Bytes.toBytes("b"), Bytes.toBytes("foo2.b"))), ...
  
  rdd.hbaseBulkLoadThinRows(hbaseContext,
        TableName.valueOf(tableName),
        t => {
          val rowKey = t._1
  
          val familyQualifiersValues = new FamiliesQualifiersValues
          t._2.foreach(f => {
            val family:Array[Byte] = f._1
            val qualifier = f._2
            val value:Array[Byte] = f._3
  
            familyQualifiersValues +=(family, qualifier, value)
          })
          (new ByteArrayWrapper(Bytes.toBytes(rowKey)), familyQualifiersValues)
        },
        stagingFolder.getPath,
        new java.util.HashMap[Array[Byte], FamilyHFileWriteOptions],
        compactionExclude = false,
        20)
  
  val load = new LoadIncrementalHFiles(config)
  load.doBulkLoad(new Path(stagingFolder.getPath),
    conn.getAdmin, table, conn.getRegionLocator(TableName.valueOf(tableName)))
  ----
  ====
  
  Note that the big difference in using bulk load for thin rows is the function
  returns a tuple with the first value being the row key and the second value
  being an object of FamiliesQualifiersValues, which will contain all the
  values for this row for all column families.
  
  
  == SparkSQL/DataFrames
  
  