temporalio / temporalio/features

Update API: support not writing history when calling external failure

Open
#257 12 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Go
Stars
32
Forks
28
Avg merge
1d 9h
Merged PRs (30d)
7

Description

Is your feature request related to a problem? Please describe.

Using “short update“ is a very important pattern of Update API feature. It acts like an RPC. Usually, the update handler may call an external system to perform an action, if successful, then mutate the workflow states. If calling external fails, then no mutation on the workflow.

Note that this “short update“ may be called frequently with back off retry on failures. In reality, the external system could be unreliable so the failure is out of control.

As the way it works today, Temporal will write the update failure event into history, even this event doesn’t have any value for this use case. Eventually, this failure event will blow up the history very quickly. Hence this will become a scalability issue to use with Temporal.

Describe the solution you'd like
Based on discussion with @mfateev , it was designed this way because throwing away the activity results means the cache would become invalid hence the workflow has to be replayed to rebuild the cache. This will put significant load on the service as every such rejection would require kicking the workflow out of cache and replaying it from the beginning.

An better idea to implement this is to only allow certain way to calling external system -- for example, using local activity only. In GoSDK, the update API will just change the UpdateHandlerOptions to become like this:

UpdateHandlerOptions struct {
    // Validator is an optional (i.e. can be left nil) func with exactly the
    // same type signature as the required update handler func but returning
    // only a single value of type error.  ....
    Validator interface{}

    // PreUpdateLocalActivity is an optional local activity to be execute after the Validator, 
    // If it succeeds, then invoke PreUpdateLocalActivityValidator if exists, or invoke the update handler
    // if it fails then return the error to Update API caller
    // Note that when this is set, the first parameter of update handler must be accepting the result of the local 
    // activity, and then the other parameters from caller
    PreUpdateLocalActivity interface{}

    // PreUpdateLocalActivityValidator is an optional func like Validator to validate the results of local activity
    // same requirement as Validator. 
    // If succeeds then invoke update handler
    // If fails then return error to Update API caller
    PreUpdateLocalActivityValidator interface{}
    
    // PreUpdateLocalActivityOptions is the options for PreUpdateLocalActivity
    PreUpdateLocalActivityOptions LocalActivityOptions
}

This feature will only need to implement in the SDK. And since we only update the UpdateHandlerOptions, it's backward compatible. Maxim has confirmed that this should work, see the thread in the context.

Essentially, Temporal doesn't assign any activityId(which means the state machine needs to change) or history eventId when invoking the local activity. So if it fails, nothing will be changed to the workflow states(cache). If it succeeds, the pass the results to the real update handler.

Describe alternatives you've considered
Another way to workaround is to ask user to call the external system, if succeeds then passing the results when calling Update API. However, user will have to handle a lot more edge cases, especially racing conditions (as when calling external APIs, workflow is also running, and the workflow states could be changed).

Additional context
https://temporalio.slack.com/archives/CTDTU3J4T/p1681187414101309

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the Go SDK's UpdateHandlerOptions and the update execution path. Review the linked Slack discussion and existing update and local-activity behavior before defining the API shape. Done means a backward-compatible SDK implementation that supports the proposed pre-update local activity flow without writing failure history.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.