Passing table buffer to newly created batch task

I am trying to multi-thread a batch process. Based on processing, I have populated a temporary table with a list of recIDs. Based on the number of recIDs and number of processing threads specified, I then break this list up, create batch tasks, and send each task a piece of the list to process.

The tasks are being created as I would expect, but they don’t have the list of records to process. I’m also sending a NoYesCombo that I have confirmed is being sent correclty. I have tried sending the records in an object of my temporary table and in a list. I suspect both have the same problem of being cleared out and not saved for processing.

Here is the run method for the class that does the creation of the batch tasks:

    void run()
    {
        BatchHeader                 batchHeader;
        CustomConfirmTask                       workOrderConfirmTask;
        CustomTemporaryTable                    workOrderListTmp, workOrderListBuffer;
        Integer                                 workOrderCounter, workOrdersPerTask;
        NoYesCombo                              skipExport;
    
        workOrderCounter = 0;

        // code here that sets skipExport and populates workOrderListTmp and workOrderCounter...tested and works correctly
        
        if(this.isInBatch() && numberOfProcessingThreads > 1)
        {
            batchHeader = BatchHeader::construct(this.parmCurrentBatch().BatchJobId);
            batchHeader.parmCaption('Multi thread work order confirm');

            workOrdersPerTask = roundup(workOrderCounter/numberOfProcessingThreads, 1);
            workOrderCounter = 0;

            while select workOrderListTmp
            {
                workOrderBuffer.WorkOrderRecId = workOrderListTmp.WorkOrderRecId;
                workOrderBuffer.insert();

                workOrderCounter++;
 
                if (workOrderCounter == workOrdersPerTask)
                {
                    workOrderConfirmTask = new CustomConfirmTask();
                    workOrderConfirmTask.parmWorkOrderList(workOrderBuffer);
                    workOrderConfirmTask.parmSkipExport(skipExport);
                    batchHeader.addTask(workOrderConfirmTask, this.parmCurrentBatch().RecId);
                    workOrderCounter = 0;
                    workOrderBuffer = null;
                }
            }

            if (workOrderCounter > 0)
            {
                workOrderConfirmTask = new CustomConfirmTask();
                workOrderConfirmTask.parmWorkOrderList(workOrderBuffer);
                batchHeader.addTask(workOrderConfirmTask, this.parmCurrentBatch().RecId);
            }
            
            batchHeader.save();
        }
    }

And here is the code for the class I’m making a task out of:

class CustomConfirmTask extends RunBaseBatch
{
    CustomTemporaryTable    workOrderList;
    NoYesCombo              skipExport;

    #DEFINE.CurrentVersion(1)
    #LOCALMACRO.CurrentList
        workOrderList,
        skipExport
    #ENDMACRO

    container pack()
    {
        return [#CurrentVersion,#CurrentList];
    }

    public boolean unpack(container _packedClass)
    {
        int version = conPeek(_packedClass,1);
   
        switch (version)
        {
            case #CurrentVersion:
                [version,#CurrentList] = _packedClass;
                break;
            default:
                return false;
        }
        return true;
    }

    public void parmWorkOrderList(CustomTemporaryTable _workOrderList)
    {
        workOrderList = _workOrderList;
    }

    public void parmSkipExport(NoYesCombo _skipExport)
    {
        skipExport = _skipExport;
    }

    public void run()
    {
        try
        {
            while (workOrderList)
            {
                // processing the work orders happens here
            }
        }
        catch
        {
            info(strFmt("%1",xSession::xppCallStack()));
        }
    }
}

Thanks in advance for any help!

You’re assuming that storing a buffer in class variable of your task will also preserves the pointer to a temporary data set, but it’s not the case. With InMemory tables, it’s completely impossible, because the task may be executed on a different AOS than where you created the task, and the other AOS doesn’t have such data in memory. It doesn’t work with TempDB tables either - the table is dropped as soon as all references to it gets out of scope.

All you can get from workOrderList variable are values of its fields.

Therefore you must replace your temporary table with something else. Using a List (or a Set) packed to container is a viable option; I can’t say what problem you had with the List, but it’s definitely a different case than using temporary tables.

Thanks, Martin. I figured that was the problem after I coded it. I changed my code to send a list and got the same results. I may just be doing something wrong with the way I’m using the list.

Here is the run method of the batch class that does the task creation…this time with a list:

    void run()
    {
        BatchHeader                             batchHeader;
        CustomConfirmTask                       workOrderConfirmTask;
        CustomTemporaryTable                    workOrderListTmp;
        Integer                                 workOrderCounter, workOrdersPerTask;
        NoYesCombo                              skipExport;
        List                                    workOrderBuffer = new List(types::int64);
    
        workOrderCounter = 0;

        // code here that sets skipExport and populates workOrderListTmp and workOrderCounter...tested and works correctly
        
        if(this.isInBatch() && numberOfProcessingThreads > 1)
        {
            batchHeader = BatchHeader::construct(this.parmCurrentBatch().BatchJobId);
            batchHeader.parmCaption('Multi thread work order confirm');

            workOrdersPerTask = roundup(workOrderCounter/numberOfProcessingThreads, 1);
            workOrderCounter = 0;

            while select workOrderListTmp
            {
                workOrderBuffer.addEnd(workOrderListTmp.WorkOrderRecId);
                
                workOrderCounter++;
 
                if (workOrderCounter == workOrdersPerTask)
                {
                    // I put a loop here to iterate through list and confirm it contains correct data. It does.
                    workOrderConfirmTask = new CustomConfirmTask();
                    workOrderConfirmTask.parmWorkOrderList(workOrderBuffer);
                    workOrderConfirmTask.parmSkipExport(skipExport);
                    batchHeader.addTask(workOrderConfirmTask, this.parmCurrentBatch().RecId);
                    
                    workOrderCounter = 0;
                    workOrderBuffer = new List(Types::int64);
                }
            }

            if (workOrderCounter > 0)
            {
                workOrderConfirmTask = new CustomConfirmTask();
                workOrderConfirmTask.parmWorkOrderList(workOrderBuffer);
                batchHeader.addTask(workOrderConfirmTask, this.parmCurrentBatch().RecId);
            }
            
            batchHeader.save();
        }
    }

And here is the code for the class that is the created task…with a list:

class CustomConfirmTask extends RunBaseBatch
{
    List                    workOrderList = new List(Types::Int64);
    NoYesCombo              skipExport;

    #DEFINE.CurrentVersion(1)
    #LOCALMACRO.CurrentList
        // workOrderList, <--Removed list from pack/unpack because you can't have objects of type list
        skipExport
    #ENDMACRO

    container pack()
    {
        return [#CurrentVersion,#CurrentList];
    }

    public boolean unpack(container _packedClass)
    {
        int version = conPeek(_packedClass,1);
   
        switch (version)
        {
            case #CurrentVersion:
                [version,#CurrentList] = _packedClass;
                break;
            default:
                return false;
        }
        return true;
    }

    public void parmWorkOrderList(List _workOrderList)
    {
        workOrderList = _workOrderList;
    }

    public void parmSkipExport(NoYesCombo _skipExport)
    {
        skipExport = _skipExport;
    }

    public void run()
    {
        ListIterator    iterator;
        try
        {
            iterator = new ListIterator(workOrderList);
            while (iterator.more())
            {
                // processing the work orders happens here
                
                iterator.next();
            }
        }
        catch
        {
            info(strFmt("%1",xSession::xppCallStack()));
        }
    }
}

Thanks again for the help!

Now you’re missing code to packing the list, therefore the collection of record IDs won’t be stored and it won’t be available when the task is executed. It won’t happen magically by itself; you must have code doing that.

Do something like this:

container pack()
{
    return [#CurrentVersion, #CurrentList, workOrderList.pack()];
}

public boolean unpack(container _packedClass)
{
    int version = conPeek(_packedClass,1);
    container packedList;

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList, packedList] = _packedClass;
            workOrderList = List::create(packedList);
            break;
        default:
            return false;
    }
    return true;
}

I wrote the code here, not in the IDE - sorry if I don’t remember method names correctly, or something.

Thanks, Martin. I couldn’t figure out how to get a list in my pack/unpack because it didn’t let me put them in #CurrentList. Your solution works perfectly.