Files
tasks/service/history/queue_factory_base_test.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

163 lines
4.6 KiB
Go

package history
import (
"sync/atomic"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
"go.temporal.io/server/chasm"
"go.temporal.io/server/client"
"go.temporal.io/server/common/clock"
"go.temporal.io/server/common/cluster"
"go.temporal.io/server/common/dynamicconfig"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/membership"
"go.temporal.io/server/common/metrics"
"go.temporal.io/server/common/namespace"
"go.temporal.io/server/common/persistence"
"go.temporal.io/server/common/persistence/serialization"
"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"
"go.temporal.io/server/service/history/archival"
"go.temporal.io/server/service/history/configs"
"go.temporal.io/server/service/history/replication/eventhandler"
"go.temporal.io/server/service/history/shard"
"go.temporal.io/server/service/history/tasks"
"go.temporal.io/server/service/history/workflow"
"go.temporal.io/server/service/history/workflow/cache"
"go.uber.org/fx"
"go.uber.org/mock/gomock"
)
// TestQueueModule_ArchivalQueue tests that the archival queue is created if and only if the task category exists.
func TestQueueModule_ArchivalQueue(t *testing.T) {
for _, c := range []moduleTestCase{
{
Name: "Archival disabled",
CategoryExists: false,
ExpectArchivalQueue: false,
},
{
Name: "Archival enabled",
CategoryExists: true,
ExpectArchivalQueue: true,
},
} {
c := c
t.Run(c.Name, c.Run)
}
}
// moduleTestCase is a test case for the QueueModule.
type moduleTestCase struct {
Name string
ExpectArchivalQueue bool
CategoryExists bool
}
// Run runs the test case.
func (c *moduleTestCase) Run(t *testing.T) {
controller := gomock.NewController(t)
dependencies := getModuleDependencies(controller, c)
var factories []QueueFactory
app := fx.New(
dependencies,
QueueModule,
fx.Invoke(func(params QueueFactoriesLifetimeHookParams) {
factories = params.Factories
}),
)
require.NoError(t, app.Err())
require.NotNil(t, factories)
var (
txq QueueFactory
tiq QueueFactory
viq QueueFactory
aq QueueFactory
)
for _, f := range factories {
switch f.(type) {
case *transferQueueFactory:
require.Nil(t, txq)
txq = f
case *timerQueueFactory:
require.Nil(t, tiq)
tiq = f
case *visibilityQueueFactory:
require.Nil(t, viq)
viq = f
case *archivalQueueFactory:
require.Nil(t, aq)
aq = f
}
}
require.NotNil(t, txq)
require.NotNil(t, tiq)
require.NotNil(t, viq)
if c.ExpectArchivalQueue {
require.NotNil(t, aq)
} else {
require.Nil(t, aq)
}
}
// getModuleDependencies returns an fx.Option that provides all the dependencies needed for the queue module.
func getModuleDependencies(controller *gomock.Controller, c *moduleTestCase) fx.Option {
cfg := configs.NewConfig(
dynamicconfig.NewNoopCollection(),
1,
)
clusterMetadata := cluster.NewMockMetadata(controller)
clusterMetadata.EXPECT().GetCurrentClusterName().Return("module-test-cluster-name").AnyTimes()
lazyLoadedOwnershipBasedQuotaScaler := shard.LazyLoadedOwnershipBasedQuotaScaler{
Value: &atomic.Value{},
}
registry := tasks.NewDefaultTaskCategoryRegistry()
if c.CategoryExists {
registry.AddCategory(tasks.CategoryArchival)
}
serializer := serialization.NewSerializer()
historyFetcher := eventhandler.NewMockHistoryPaginatedFetcher(controller)
return fx.Supply(
unusedDependencies{},
cfg,
fx.Annotate(registry, fx.As(new(tasks.TaskCategoryRegistry))),
fx.Annotate(metrics.NoopMetricsHandler, fx.As(new(metrics.Handler))),
fx.Annotate(log.NewTestLogger(), fx.As(new(log.SnTaggedLogger))),
fx.Annotate(clusterMetadata, fx.As(new(cluster.Metadata))),
lazyLoadedOwnershipBasedQuotaScaler,
fx.Annotate(serializer, fx.As(new(serialization.Serializer))),
fx.Annotate(historyFetcher, fx.As(new(eventhandler.HistoryPaginatedFetcher))),
fx.Annotate(telemetry.NoopTracerProvider, fx.As(new(trace.TracerProvider))),
)
}
// This is a struct that provides nil implementations of all the dependencies needed for the queue module that are not
// actually used.
type unusedDependencies struct {
fx.Out
clock.TimeSource
membership.ServiceResolver
namespace.Registry
client.Bean
sdk.ClientFactory
resource.MatchingRawClient
resource.HistoryRawClient
manager.VisibilityManager
archival.Archiver
workflow.RelocatableAttributesFetcher
persistence.HistoryTaskQueueManager
cache.Cache
chasm.Engine
ChasmRegistry *chasm.Registry
worker_versioning.VersionMembershipCache
}