mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? Change Deployment Version SA delimiter from `.` to `:`. ## Why? New delimiter is used in: - `TemporalWorkerDeploymentVersion` SA - `BuildIds` SA, for the `pinned:<deployment name>:<build id>` format - Deployment Version workflow ID Old delimiter is used in: - v31 version string values in external API - version string values in internal entity workflows and APIs We'll refactor code to clean up usage of old delimiter and string version fields later. For now, the priority is to make public preview external APIs consistent. ## How did you test it? - [ ] built - [ ] run locally and tested manually - [x] covered by existing tests - [ ] added new unit test(s) - [ ] added new functional test(s) ## Potential risks If Versioning V0.31 pre-release users directly use these SAs, they wont be able to find old or new wfs if they filter using only one of the delimiters. (This affects "Go to Workflow" links in UI until UI is updated)
162 lines
5.4 KiB
Go
162 lines
5.4 KiB
Go
package deployment
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
deploymentpb "go.temporal.io/api/deployment/v1"
|
|
enumspb "go.temporal.io/api/enums/v1"
|
|
"go.temporal.io/server/common/cache"
|
|
"go.temporal.io/server/common/metrics"
|
|
"go.temporal.io/server/common/namespace"
|
|
"go.temporal.io/server/common/persistence/visibility/manager"
|
|
"go.temporal.io/server/common/searchattribute"
|
|
"go.temporal.io/server/common/worker_versioning"
|
|
)
|
|
|
|
const (
|
|
reachabilityCacheOpenWFsTTL = time.Minute
|
|
reachabilityCacheClosedWFsTTL = 10 * time.Minute
|
|
reachabilityCacheMaxSize = 10000
|
|
)
|
|
|
|
// We update the reachability search attribute on workflow task completion and when there is
|
|
// an UpdateWorkflowExecutionOptions request that changes the workflow's effective behavior
|
|
// or deployment. We do not update the reachability search attribute if the search attribute
|
|
// has not changed. We also do not update the reachability search attribute if a workflow has
|
|
// started transitioning to a new deployment, because only on task completion do we know whether
|
|
// the transition has succeeded or failed.
|
|
func getDeploymentReachability(
|
|
ctx context.Context,
|
|
nsId, nsName, seriesName, buildId string,
|
|
isCurrent bool,
|
|
rc reachabilityCache,
|
|
) (enumspb.DeploymentReachability, time.Time, error) {
|
|
// 1a. Reachable by new unpinned workflows
|
|
if isCurrent {
|
|
// TODO (carly) part 2: still return reachable if the deployment just became not current, but workflows started on it are not yet in reachability
|
|
return enumspb.DEPLOYMENT_REACHABILITY_REACHABLE, time.Now(), nil
|
|
}
|
|
|
|
// 2a. Reachable by open pinned workflows
|
|
countRequest := makeCountRequest(nsId, nsName, seriesName, buildId, true)
|
|
exists, lastUpdateTime, err := rc.Get(ctx, countRequest, true)
|
|
if err != nil {
|
|
return enumspb.DEPLOYMENT_REACHABILITY_UNSPECIFIED, time.Time{}, err
|
|
}
|
|
if exists {
|
|
return enumspb.DEPLOYMENT_REACHABILITY_REACHABLE, lastUpdateTime, nil
|
|
}
|
|
|
|
// 3. Reachable by closed pinned workflows
|
|
countRequest = makeCountRequest(nsId, nsName, seriesName, buildId, false)
|
|
exists, lastUpdateTime, err = rc.Get(ctx, countRequest, false)
|
|
if err != nil {
|
|
return enumspb.DEPLOYMENT_REACHABILITY_UNSPECIFIED, time.Time{}, err
|
|
}
|
|
if exists {
|
|
return enumspb.DEPLOYMENT_REACHABILITY_CLOSED_WORKFLOWS_ONLY, lastUpdateTime, nil
|
|
}
|
|
|
|
// 4. Unreachable
|
|
return enumspb.DEPLOYMENT_REACHABILITY_UNREACHABLE, time.Now(), nil
|
|
}
|
|
|
|
func makeCountRequest(
|
|
namespaceId, namespaceName, seriesName, buildId string, open bool,
|
|
) manager.CountWorkflowExecutionsRequest {
|
|
return manager.CountWorkflowExecutionsRequest{
|
|
NamespaceID: namespace.ID(namespaceId),
|
|
Namespace: namespace.Name(namespaceName),
|
|
Query: makeDeploymentQuery(seriesName, buildId, open),
|
|
}
|
|
}
|
|
|
|
func makeDeploymentQuery(seriesName, buildID string, open bool) string {
|
|
var statusFilter string
|
|
deploymentFilter := fmt.Sprintf("= '%s'", worker_versioning.PinnedBuildIdSearchAttribute(
|
|
worker_versioning.ExternalWorkerDeploymentVersionToString(&deploymentpb.WorkerDeploymentVersion{
|
|
DeploymentName: seriesName,
|
|
BuildId: buildID,
|
|
})))
|
|
if open {
|
|
statusFilter = "= 'Running'"
|
|
} else {
|
|
statusFilter = "!= 'Running'"
|
|
}
|
|
// todo (carly) part 2: handle null search attribute / unversioned deployment
|
|
return fmt.Sprintf("%s %s AND %s %s", searchattribute.BuildIds, deploymentFilter, searchattribute.ExecutionStatus, statusFilter)
|
|
}
|
|
|
|
/*
|
|
In-memory Reachability Cache of Visibility Queries and Results
|
|
*/
|
|
|
|
type reachabilityCache struct {
|
|
openWFCache cache.Cache
|
|
closedWFCache cache.Cache // these are separate due to allow for different TTL
|
|
metricsHandler metrics.Handler
|
|
visibilityMgr manager.VisibilityManager
|
|
}
|
|
|
|
type reachabilityCacheValue struct {
|
|
exists bool
|
|
lastUpdateTime time.Time
|
|
}
|
|
|
|
func newReachabilityCache(
|
|
handler metrics.Handler,
|
|
visibilityMgr manager.VisibilityManager,
|
|
reachabilityCacheOpenWFExecutionTTL,
|
|
reachabilityCacheClosedWFExecutionTTL time.Duration,
|
|
) reachabilityCache {
|
|
return reachabilityCache{
|
|
openWFCache: cache.New(reachabilityCacheMaxSize, &cache.Options{TTL: reachabilityCacheOpenWFExecutionTTL}),
|
|
closedWFCache: cache.New(reachabilityCacheMaxSize, &cache.Options{TTL: reachabilityCacheClosedWFExecutionTTL}),
|
|
metricsHandler: handler,
|
|
visibilityMgr: visibilityMgr,
|
|
}
|
|
}
|
|
|
|
// Get retrieves the Workflow Count existence value and update time based on the query-string key.
|
|
func (c *reachabilityCache) Get(
|
|
ctx context.Context,
|
|
countRequest manager.CountWorkflowExecutionsRequest,
|
|
open bool,
|
|
) (exists bool, lastUpdateTime time.Time, err error) {
|
|
// try cache
|
|
var result interface{}
|
|
if open {
|
|
result = c.openWFCache.Get(countRequest)
|
|
} else {
|
|
result = c.closedWFCache.Get(countRequest)
|
|
}
|
|
if result != nil {
|
|
// there's no reason that the cache would ever contain a non-reachabilityCacheValue, but just in case, treat non-bool as a miss
|
|
val, ok := result.(reachabilityCacheValue)
|
|
if ok {
|
|
return val.exists, val.lastUpdateTime, nil
|
|
}
|
|
}
|
|
|
|
// cache was cold, ask visibility and put result in cache
|
|
countResponse, err := c.visibilityMgr.CountWorkflowExecutions(ctx, &countRequest)
|
|
if err != nil {
|
|
return false, time.Time{}, err
|
|
}
|
|
exists = countResponse.Count > 0
|
|
lastUpdateTime = time.Now()
|
|
c.Put(countRequest, reachabilityCacheValue{exists, lastUpdateTime}, open)
|
|
return exists, lastUpdateTime, nil
|
|
}
|
|
|
|
// Put adds an element to the cache.
|
|
func (c *reachabilityCache) Put(key manager.CountWorkflowExecutionsRequest, val reachabilityCacheValue, open bool) {
|
|
if open {
|
|
c.openWFCache.Put(key, val)
|
|
} else {
|
|
c.closedWFCache.Put(key, val)
|
|
}
|
|
}
|