Can batchable custom iterables support null values?

Background

We’re trying to do a type of pseudo-parallel processing mechanism, where a record will be processed twice. The thought was to make an overlapping series of values that look like this:

|------- PHASE 1 -------|------- PHASE 2 --------|
null null null null null|0    1    2    3    4    // First Batch
0    1    2    3    4   |5    6    7    8    9    // Second Batch
5    6    7    8    9   |null null null null null // Third Batch

I’m accomplishing this pattern with a custom iterator that looks like this:

public class OverlapBatch implements Database.Batchable<Integer>, Iterable<Integer>, Iterator<Integer> {
    Integer[] values;

    public Iterator<Integer> iterator() {
        return this;
    }

    public Boolean hasNext() {
        return !values.isEmpty();
    }

    public Integer next() {
        return values.remove(0);
    }

    public Iterable<Integer> start(Database.BatchableContext bc) {
        values = new Integer[0];

        Integer[] temp = new Integer[0];

        for(Integer index = 0; index < 5; index++) {
            values.add(null);
        }

        for(Integer index = 0; index < 10; index++) {
            temp.add(index);
            if(temp.size() == 5) {
                values.addAll(temp);
                values.addAll(temp);
                temp.clear();
            }
        }

        for(Integer index = 0; index < 5; index++) {
            values.add(null);
        }
        return this;
    }

    public void execute(Database.BatchableContext bc, Integer[] scope) {
        System.debug(LoggingLevel.ERROR, scope.size());
        System.debug(LoggingLevel.ERROR, scope);
    }

    public void finish(Database.BatchableContext bc) {

    }
}

The only difference is that we’re querying records and trying to overlay Id values instead, but even this code shows the problem we’re having. The code is called like this:

Database.executeBatch(new OverlayBatch(), 10);

Executing the code, one would expect there to be three batches, but instead I only get two. Checking the logs for both of the batches, the logs show that null values are ignored:

// Batch 1
16:21:59.040 (40836245)|USER_DEBUG|[41]|ERROR|10
16:21:59.040 (40881446)|USER_DEBUG|[42]|ERROR|(0, 1, 2, 3, 4, 0, 1, 2, 3, 4)
// Batch 2
16:21:59.037 (37841546)|USER_DEBUG|[41]|ERROR|10
16:21:59.037 (37894244)|USER_DEBUG|[42]|ERROR|(5, 6, 7, 8, 9, 5, 6, 7, 8, 9)

The nulls disappear, and I’m left only with non-null values. This behavior seems undocumented, and is actually undesirable, since we’re trying to process a two-phase system. Of course, I could use other techniques, but is there a clean way to just use nulls in this scenario?

Answer

The only way I was able to get a null Iterable was to decorate it with an actual class.

I made a simple custom Decorator class like this one.

global class BatchableInteger
{
    Integer BatachIntegerValue { get; private set; }
    public BatchableInteger(Integer integerInBatch) { BatachIntegerValue = integerInBatch; }
}

Then I used it like your example.

public class BatchOverlay implements Database.Batchable<BatchableInteger>, Iterable<BatchableInteger>, Iterator<BatchableInteger> 
{
    List<BatchableInteger> BatchIterables { get; private set; }

    public Iterator<BatchableInteger> iterator() { return this; }

    public Boolean hasNext() { return !BatchIterables.isEmpty(); }

    public BatchableInteger next() { return BatchIterables.remove(0); }

    public Iterable<BatchableInteger> start(Database.BatchableContext batchContext) 
    {
        BatchIterables = new List<BatchableInteger>();

        List<BatchableInteger> temporaryIntegerList = new List<BatchableInteger>();

        for(Integer index = 0; index < 5; index++) 
            BatchIterables.add(new BatchableInteger(null));

        for(Integer index = 0; index < 10; index++) 
        {
            temporaryIntegerList.add(new BatchableInteger(index));
            if(temporaryIntegerList.size() == 5) 
            {
                BatchIterables.addAll(temporaryIntegerList);
                BatchIterables.addAll(temporaryIntegerList);
                temporaryIntegerList.clear();
            }
        }

        for(Integer index = 0; index < 5; index++)
            BatchIterables.add(new BatchableInteger(null));

        return this;
    }

    public void execute(Database.BatchableContext batchConext, List<BatchableInteger> scope) 
    {
        System.debug(LoggingLevel.ERROR, scope.size());
        System.debug(LoggingLevel.ERROR, scope);
    }

    public void finish(Database.BatchableContext batchContext) { }
}

I imagine the loss of the null value has to do with the serialization process. It happens with JSON serialization too, so I imagine there is something similar going on.

Attribution
Source : Link , Question Author : sfdcfox , Answer Author : Programmable Medley

Leave a Comment