fieldenms / tg

Trident Genesis
MIT License
14 stars 7 forks source link

Continuations: communication of intent between a callee and a caller. #2253

Closed 01es closed 4 months ago

01es commented 4 months ago

Description

The original issue #602, where support for continuations was developed, did not envisage situations where a context in which the request for "more data", expressed by throwing exception NeedMoreDate, may not be satisfiable. In practice such situations do occur and need to be handled ad hoc for each specific case. However, due to the ease with which NeedMoreDate can be introduced, potentially breaking an application in many places, demands a systemic approach that would:

  1. Provide a way for a caller to notify a callee that "more data" can be satisfied.
  2. Provide a way for a callee to check whether "more data" can be satisfied in a given context before throwing NeedMoreData.

This is not an automagical solution where context that support "more data" can be identified automatically. Instead, additional API needs to be developed that would need to be used explicitly when developing with continuations. With the sensible defaults this should not introduce too much overhead, but would give more control to developers over the situation.

It should be sufficient to provide 2 methods as part of CommonEntityDao:

So far, there are 2 practical contexts in which "more data" can be provided:

  1. Direct saving of entities from their masters, which is equivalent to calling co.save(entity) on entity companions, but invoked from a web service layer.

    This is a context in which co.setContinuationSuported(true) can be used by default in EntityResourceContinuationsHelper.saveWithContinuations before invoking co.save(entity). In other words, "more data" is always satisfiable for entities that are being saved directly from their master.

  2. Invocation of co.save(entity) in tests that need to test the "more data" explicitly.

    This is a context where developers can catch NeedMoreData and assigned "more data" as expected by their tests.

All other current cases, which means invocations of co.save() in the context of other co.save for other companions cannot satisfy "more data". Therefore, companion instances should defensively consider that continuations are not supported by default because a context of their execution is not known ahead of time. And so, a typical use case should now look like this:

// if more data can be provided, but was not yet provided -- request it
if (isContinuationSupported() && !moreData("keyForMyData").isPresent()) {
    throw new NeedMoreData("My data is needed.", MyData.class, "keyForMyData");
}
// otherwise, if more data is  present then use it
else if (moreData("keyForMyData").isPresent()) {
    final MyData data = this.<MyData> moreData("keyForMyData").get();
    // do something with data
}    

However, there can be situations where "more data" is required and saving should not succeed without that data. In such cases simply don't check support for continuations and request "more data" regardless.

// if more data is not yet provided -- request it
if (!moreData("keyForMyData").isPresent()) {
    throw new NeedMoreData("My data is needed.", MyData.class, "keyForMyData");
}
// otherwise, more data is present and it can be used
else {
    final MyData data = this.<MyData> moreData("keyForMyData").get();
    // do something with data
}    

Expected outcome

Improved safety of using continuations for "more data" with control over contexts in which "more data" can or cannot be satisfied.