Files
tasks/chasm/lib/callback/request.go
b701b9bae2 Add CHASM callback into mutable state (#8582)
## What changed?
Integrated chasm/lib/callback into MutableState and updated
callback-related APIs to use CHASM implementation. Added integration
tests following the scheduler pattern.

## Why?
Porting callback functionality from HSM to CHASM as part of the ongoing
HSM-to-CHASM migration effort.

## How did you test it?
- [X] built
- [X] run locally and tested manually
- [X] covered by existing tests
- [ ] added new unit test(s)
- [X] added new functional test(s)

## Potential risks
This will need to be tested more, but is not being turned on with this
PR

---

> [!NOTE]
> Integrates CHASM-based callbacks into mutable state and history APIs,
adds configs and error types, updates routing/state transitions, and
includes migration tests.
> 
> - **CHASM Callback Integration**:
> - Add CHASM callback module (FX wiring, HTTP caller provider, request
routing) and metrics shared with HSM implementation.
> - Implement `ToAPICallback`, invocation flow changes, and refined
error wrapping.
> - **History/MutableState**:
> - Expose `ChasmEnabled`, `ChasmWorkflowComponent{,ReadOnly}`; use
CHASM for adding/processing completion callbacks when enabled.
> - Describe API now builds callback info from both CHASM and HSM trees.
> - Continue-as-new/retry path aggregates callbacks from both
implementations.
> - **State Machine/Executors**:
> - Switch to `queues/errors` types; move `NamespaceIDAndDestination` to
`queues/common`.
> - Adjust transitions: generate `BackoffTask` with scheduled time;
include destination on reschedule; no tasks on success/fail.
> - Simplify retry result (policy passed separately) and validation
signatures.
> - **Config**:
> - Add `EnableCHASMCallbacks` and `MaxCHASMCallbacksPerWorkflow`; wire
through history configs.
> - **Tests**:
> - Add migration and CHASM-enabled functional tests; update unit tests
to new error/types and behaviors.
> 
> <sup>Written by [Cursor
Bugbot]() for commit
e3b51a931d3ea2dd7f18fb2f9c55fc880ba828d8. This will update automatically
on new commits. Configure
[here]().</sup>

---------

Co-authored-by: Roey Berman <[email protected]>
2025-11-24 12:31:44 -07:00

72 lines
2.4 KiB
Go

package callback
import (
"net/http"
"go.temporal.io/server/common"
"go.temporal.io/server/common/cluster"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/log/tag"
"go.temporal.io/server/common/nexus"
)
// Header key used to identify callbacks that originate from and target the same cluster.
// Note: this is the nexusoperations.NexusCallbackSourceHeader stripped of Nexus-Callback-
const callbackSourceHeader = "source"
func routeRequest(
r *http.Request,
clusterMetadata cluster.Metadata,
httpClientCache *cluster.FrontendHTTPClientCache,
defaultClient *http.Client,
localClient *common.FrontendHTTPClient,
logger log.Logger,
) (*http.Response, error) {
// This source header is populated in nexusoperations/executors (via the ClientProvider) for worker targets
// if this header is not populated then we assume it's and external target.
if r.Header == nil || r.Header.Get(callbackSourceHeader) == "" {
return defaultClient.Do(r)
}
// If we got here, we assume that the endpoint in the original call was a worker target, and we should route
// internally, either to a local frontend, or one of the other connected clusters' frontends.
var frontendClient *common.FrontendHTTPClient
callbackSource := r.Header.Get(callbackSourceHeader)
for clusterName, clusterInfo := range clusterMetadata.GetAllClusterInfo() {
if callbackSource == clusterInfo.ClusterID {
if clusterMetadata.GetCurrentClusterName() == clusterName {
frontendClient = localClient
} else {
fec, err := httpClientCache.Get(clusterName)
if err != nil {
logger.Warn(
"HTTPCallerProvider unable to get FrontendHTTPClient for callback target cluster. Using local HTTP Client.",
tag.SourceCluster(clusterMetadata.GetCurrentClusterName()),
tag.TargetCluster(clusterName),
tag.Error(err),
)
frontendClient = localClient
} else {
frontendClient = fec
}
}
break
}
}
if frontendClient == nil {
// This can happen when a cluster is disconnected.
logger.Warn(
"HTTPCallerProvider unable to find the target cluster. Using local HTTP Client.",
tag.SourceCluster(clusterMetadata.GetCurrentClusterName()),
)
frontendClient = localClient
}
if r.URL.String() == nexus.SystemCallbackURL {
r.URL.Path = nexus.PathCompletionCallbackNoIdentifier
}
r.URL.Scheme = frontendClient.Scheme
r.URL.Host = frontendClient.Address
r.Host = frontendClient.Address
return frontendClient.Do(r)
}