Files
tasks/service/history/transfer_queue_factory.go
1e31c76ed1 Versioning cherry-pick for OSS v1.30.2 (#9543)
## What changed?
Cherry-pick versioning PRs
- #9168
  - Cache for system protection
- #9262 
  - Cache for system protection
- #9239 
- Critical PR to enable sending `TargetVersionChanged` flag for
Upgrade-on-CaN feature
- #9147 
- Tracks version drainage properly when version receives workflows via
`VersioningOverride`. Needed for automated worker controllers to
correctly scale versioned workers that received workflows via
`VersioningOverride`.
- #9300 
- Needed for `approximate_backlog_count` metric to track Current and
Ramping version tasks correctly
- #9316 
- Needed for `approximate_backlog_count` metric to track Current and
Ramping version tasks correctly
- #8957 
- Contains minor metric improvement. Included because it adds a test
harness that is used in the two metrics PRs above
- #9250 
  - Bug fix of task rescheduling edge case during AutoUpgrade Transition

## Why?
For OSS v1.30.2

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





---

> [!NOTE]
> **Medium Risk**
> Touches history/matching worker-versioning paths, adding new caches
and changing workflow task/start handling and backlog metric emission;
incorrect caching or signaling could affect dispatch/upgrade behavior
and observability.
> 
> **Overview**
> Adds new worker-versioning protections and upgrade signaling: workflow
task started events now persist a
`workflow_task_target_worker_deployment_version_changed` flag (and emit
a new `workflow_target_version_changed_count` metric) under a new
`EnableSendTargetVersionChanged` dynamic config.
> 
> Introduces two new caches with metrics and dynamic config knobs: a
`RoutingInfoCache` to avoid repeated `GetTaskQueueUserData` lookups
during activity start/transition logic, and a `ReactivationSignalCache`
plus `EnableVersionReactivationSignals` to dedupe and asynchronously
send “reactivation” signals when workflows are pinned (via
start/signal-with-start/reset/update-options) to potentially
drained/inactive worker versions.
> 
> Extends matching backlog metrics to support version-attributed
reporting by adding `BacklogMetricsEmitInterval` and switching queue DB
emission to *physical* backlog gauges (`physical_approximate_backlog_*`)
when attribution is enabled, while keeping legacy gauges when disabled.
> 
> Adds frontend scaffolding for a new visibility RPC `CountSchedules`
(client plumbing, interception/metadata/quota wiring) but leaves the
frontend handler unimplemented, and bumps `go.temporal.io/api` to
`v1.62.2`.
> 
> <sup>Written by [Cursor
Bugbot]() for commit
cb8ae14c86666654c51dd66ae91cb225722e1ba7. This will update automatically
on new commits. Configure
[here]().</sup>

---------

Co-authored-by: Shahab Tajik <[email protected]>
Co-authored-by: Shivam <[email protected]>
2026-03-17 17:19:47 -05:00

200 lines
5.9 KiB
Go

package history
import (
"go.temporal.io/server/client"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/log/tag"
"go.temporal.io/server/common/metrics"
"go.temporal.io/server/common/persistence/visibility/manager"
"go.temporal.io/server/common/resource"
"go.temporal.io/server/common/sdk"
"go.temporal.io/server/common/telemetry"
"go.temporal.io/server/common/worker_versioning"
historyi "go.temporal.io/server/service/history/interfaces"
"go.temporal.io/server/service/history/queues"
"go.temporal.io/server/service/history/tasks"
"go.uber.org/fx"
)
const (
transferQueuePersistenceMaxRPSRatio = 0.3
)
type (
transferQueueFactoryParams struct {
fx.In
QueueFactoryBaseParams
ClientBean client.Bean
SdkClientFactory sdk.ClientFactory
HistoryRawClient resource.HistoryRawClient
MatchingRawClient resource.MatchingRawClient
VisibilityManager manager.VisibilityManager
VersionMembershipCache worker_versioning.VersionMembershipCache
}
transferQueueFactory struct {
transferQueueFactoryParams
QueueFactoryBase
}
)
func NewTransferQueueFactory(
params transferQueueFactoryParams,
) QueueFactory {
return &transferQueueFactory{
transferQueueFactoryParams: params,
QueueFactoryBase: QueueFactoryBase{
HostScheduler: queues.NewScheduler(
params.ClusterMetadata.GetCurrentClusterName(),
queues.SchedulerOptions{
WorkerCount: params.Config.TransferProcessorSchedulerWorkerCount,
ActiveNamespaceWeights: params.Config.TransferProcessorSchedulerActiveRoundRobinWeights,
StandbyNamespaceWeights: params.Config.TransferProcessorSchedulerStandbyRoundRobinWeights,
InactiveNamespaceDeletionDelay: params.Config.TaskSchedulerInactiveChannelDeletionDelay,
},
params.NamespaceRegistry,
params.Logger,
),
HostPriorityAssigner: queues.NewPriorityAssigner(
params.NamespaceRegistry,
params.ClusterMetadata.GetCurrentClusterName(),
),
HostReaderRateLimiter: queues.NewReaderPriorityRateLimiter(
NewHostRateLimiterRateFn(
params.Config.TransferProcessorMaxPollHostRPS,
params.Config.PersistenceMaxQPS,
transferQueuePersistenceMaxRPSRatio,
),
int64(params.Config.TransferQueueMaxReaderCount()),
),
Tracer: params.TracerProvider.Tracer(telemetry.ComponentQueueTransfer),
},
}
}
func (f *transferQueueFactory) CreateQueue(
shardContext historyi.ShardContext,
) queues.Queue {
logger := log.With(shardContext.GetLogger(), tag.ComponentTransferQueue)
metricsHandler := f.MetricsHandler.WithTags(metrics.OperationTag(metrics.OperationTransferQueueProcessorScope))
currentClusterName := f.ClusterMetadata.GetCurrentClusterName()
var shardScheduler = f.HostScheduler
if f.Config.TaskSchedulerEnableRateLimiter() {
shardScheduler = queues.NewRateLimitedScheduler(
f.HostScheduler,
queues.RateLimitedSchedulerOptions{
EnableShadowMode: f.Config.TaskSchedulerEnableRateLimiterShadowMode,
StartupDelay: f.Config.TaskSchedulerRateLimiterStartupDelay,
},
currentClusterName,
f.NamespaceRegistry,
f.SchedulerRateLimiter,
f.TimeSource,
f.ChasmRegistry,
logger,
metricsHandler,
)
}
rescheduler := queues.NewRescheduler(
shardScheduler,
shardContext.GetTimeSource(),
logger,
metricsHandler,
)
activeExecutor := newTransferQueueActiveTaskExecutor(
shardContext,
f.WorkflowCache,
f.SdkClientFactory,
logger,
f.MetricsHandler,
f.Config,
f.HistoryRawClient,
f.MatchingRawClient,
f.VisibilityManager,
f.ChasmEngine,
f.VersionMembershipCache,
)
standbyExecutor := newTransferQueueStandbyTaskExecutor(
shardContext,
f.WorkflowCache,
logger,
f.MetricsHandler,
currentClusterName,
f.HistoryRawClient,
f.MatchingRawClient,
f.VisibilityManager,
f.ChasmEngine,
f.ClientBean,
)
executor := queues.NewActiveStandbyExecutor(
currentClusterName,
f.NamespaceRegistry,
activeExecutor,
standbyExecutor,
logger,
)
if f.ExecutorWrapper != nil {
executor = f.ExecutorWrapper.Wrap(executor)
}
factory := queues.NewExecutableFactory(
executor,
shardScheduler,
rescheduler,
f.HostPriorityAssigner,
shardContext.GetTimeSource(),
shardContext.GetNamespaceRegistry(),
shardContext.GetClusterMetadata(),
f.ChasmRegistry,
queues.GetTaskTypeTagValue,
logger,
metricsHandler,
f.Tracer,
f.DLQWriter,
f.Config.TaskDLQEnabled,
f.Config.TaskDLQUnexpectedErrorAttempts,
f.Config.TaskDLQInternalErrors,
f.Config.TaskDLQErrorPattern,
)
return queues.NewImmediateQueue(
shardContext,
tasks.CategoryTransfer,
shardScheduler,
rescheduler,
&queues.Options{
ReaderOptions: queues.ReaderOptions{
BatchSize: f.Config.TransferTaskBatchSize,
MaxPendingTasksCount: f.Config.QueuePendingTaskMaxCount,
PollBackoffInterval: f.Config.TransferProcessorPollBackoffInterval,
MaxPredicateSize: f.Config.QueueMaxPredicateSize,
},
MonitorOptions: queues.MonitorOptions{
PendingTasksCriticalCount: f.Config.QueuePendingTaskCriticalCount,
ReaderStuckCriticalAttempts: f.Config.QueueReaderStuckCriticalAttempts,
SliceCountCriticalThreshold: f.Config.QueueCriticalSlicesCount,
},
MaxPollRPS: f.Config.TransferProcessorMaxPollRPS,
MaxPollInterval: f.Config.TransferProcessorMaxPollInterval,
MaxPollIntervalJitterCoefficient: f.Config.TransferProcessorMaxPollIntervalJitterCoefficient,
CheckpointInterval: f.Config.TransferProcessorUpdateAckInterval,
CheckpointIntervalJitterCoefficient: f.Config.TransferProcessorUpdateAckIntervalJitterCoefficient,
MaxReaderCount: f.Config.TransferQueueMaxReaderCount,
MoveGroupTaskCountBase: f.Config.QueueMoveGroupTaskCountBase,
MoveGroupTaskCountMultiplier: f.Config.QueueMoveGroupTaskCountMultiplier,
},
f.HostReaderRateLimiter,
queues.GrouperNamespaceID{},
logger,
metricsHandler,
factory,
)
}