Grails promises losing data - seemingly not waiting for each other to complete

50 Views Asked by At

The following randomly calls getParentCustomers or getAccountManagers first. When it does it works fine. However, whichever one gets called second passes a null into it. Neither of these methods mutates the incoming values in any way. I am guessing there is something about the context that these are called from that the original pointer to response.salesChannels gets lost between tasks.

Map response = [
    salesChannels:   null,
    accountManagers: null,
    parentCustomers: null,
    isrs:            null,
    operatingUnits:  null,
    businessUnits:   null
]

def t1 = task {
    response.salesChannels = salesChannelApiService.get(salesChannel)

    def t1a = task {
        response.parentCustomers = salesChannelTransformService.getParentCustomers(response.salesChannels)
    }
    def t1b = task {
        response.accountManagers = salesChannelTransformService.getAccountManagers(response.salesChannels)
    }

    waitAll([t1a, t1b])
}

def t2 = task {
    //... other stuff
}

def t3 = task {
    //... other stuff
}

waitAll([t1, t2, t3])

return response

I even tried to modify the internals to leverage onComplete instead.

...
onComplete([task {
    return salesChannelApiService.get(salesChannel)
}], { salesChannels ->
    response.salesChannels = salesChannels

    def t1a = task {
        response.parentCustomers = salesChannelTransformService.getParentCustomers(salesChannels)
    }
    def t1b = task {
        response.accountManagers = salesChannelTransformService.getAccountManagers(salesChannels)
    }

    waitAll([t1a, t1b])
})
...

However, I still end up with the same result.

NOTE: This is random too. Sometimes it works fine - passing the same list to both methods. But when it breaks, it is always whichever one that fires second.

Any thoughts on this?

2

There are 2 best solutions below

0
On BEST ANSWER

So I think threading was acting up a bit on rare occasions, so I removed the separate threads between getParentCustomers and getAccountManagers since they aren't making external calls anyway.

I think using response.salesChannels simultaneously across threads was messing with memory allocation of response.salesChannels - and it was throwing a null pointer exception randomly every few hundred calls.

This seems to be much more reliable.

def t1 = createPromise()

task {
    response.salesChannels = salesChannelApiService.get(salesChannel)

    def t1a = createPromise()

    task {
        response.parentCustomers = salesChannelTransformService.getParentCustomers(response.salesChannels)
        response.accountManagers = salesChannelTransformService.getAccountManagers(response.salesChannels)
        t1a.accept()
    }

    onComplete([t1a], { a -> t1.accept() })
}
0
On

The below solution is working, for now. However, I have no idea why neither of the above code blocks don't.

Any thoughts?

def t1 = createPromise()

task {
    response.salesChannels = salesChannelApiService.get(salesChannel)

    def t1a = createPromise()
    def t1b = createPromise()

    task {
        response.parentCustomers = salesChannelTransformService.getParentCustomers(response.salesChannels)
        t1a.accept()
    }
    task {
        response.accountManagers = salesChannelTransformService.getAccountManagers(response.salesChannels)
        t1b.accept()
    }

    onComplete([t1a, t1b], { a, b -> t1.accept() })
}