)
// constant from initialization, no need for locks
return s.stringRepr
}
// constant from initialization, no need for locks
return s.shardID
}
func (s *ContextImpl) GetRangeID() int64 {
Atlas › Test
Exact test identity: go.temporal.io/server/temporaltest/TestBaseServerOptions
go.temporal.io/server/temporaltestTestBaseServerOptionsTestBaseServerOptionsExpand a file to inspect source; the > gutter marks covered lines.
)
// constant from initialization, no need for locks
return s.stringRepr
}
// constant from initialization, no need for locks
return s.shardID
}
func (s *ContextImpl) GetRangeID() int64 {
}
// constant from initialization, no need for locks
return s.executionManager
}
return []pingable.Check{
{
Name: s.String() + "-shard-lock",
// rwLock may be held for the duration of renewing shard rangeID, which are called with a
// timeout of shardIOTimeout.
Timeout: s.config.ShardIOTimeout() + 30*time.Second,
Ping: func() []pingable.Pingable {
// call rwLock.Lock directly to bypass metrics since this isn't a real request
s.rwLock.Lock()
//nolint:staticcheck // SA2001 just checking if we can acquire the lock
s.rwLock.Unlock()
return nil
},
MetricsName: metrics.DDShardLockLatency.Name(),
},
// of 10 sec.
Timeout: 10*time.Second + 30*time.Second,
_ = s.ioSemaphore.Acquire(context.Background(), locks.PriorityHigh, 1)
s.ioSemaphore.Release(1)
return nil
},
MetricsName: metrics.DDShardIOSemaphoreLatency.Name(),
},
func (s *ContextImpl) GetEngine(
ctx context.Context,
return s.engineFuture.Get(ctx)
}
func (s *ContextImpl) AssertOwnership(
ctx context.Context,
if err := s.ioSemaphoreAcquire(ctx); err != nil {
return err
}
s.wLock()
// timeout check should be done within the shard lock, in case of shard lock contention
ctx, cancel, err := s.newDetachedContext(ctx)
if err != nil {
s.wUnlock()
return err
}
if err := s.errorByState(); err != nil {
s.wUnlock()
return err
}
ShardID: s.shardID,
RangeID: s.getRangeIDLocked(),
}
s.wUnlock()
err = s.persistenceShardManager.AssertShardOwnership(ctx, request)
return s.handleWriteError(request.RangeID, err)
}
func (s *ContextImpl) GetQueueExclusiveHighReadWatermark(
category tasks.Category,
s.wLock()
defer s.wUnlock()
return s.taskKeyManager.getExclusiveReaderHighWatermark(category)
}
func (s *ContextImpl) GetQueueState(
category tasks.Category,
s.rLock()
defer s.rUnlock()
queueState, ok := s.shardInfo.QueueStates[int32(category.ID())]
if !ok {
}
// need to make a deep copy, in case UpdateReplicationQueueReaderState does a partial update
blob, _ := s.payloadSerializer.QueueStateToBlob(queueState)
ctx context.Context,
request *persistence.GetHistoryTasksRequest,
if err := s.errorByState(); err != nil {
return nil, err
}
return resp, s.handleReadError(err)
}
}
// constant from initialization, no need for locks
return s.config
}
// constant from initialization (except for tests), no need for locks
return s.eventsCache
}
// constant from initialization, no need for locks
return s.contextTaggedLogger
}
// constant from initialization, no need for locks
return s.throttledLogger
}
return s.shardInfo.GetRangeId()
}
s.stateLock.Lock()
defer s.stateLock.Unlock()
switch s.state {
case contextStateInitialized, contextStateAcquiring:
return ErrShardStatusUnknown
return nil
case contextStateStopping, contextStateStopped:
return s.newShardClosedErrorWithShardID()
}
// We must drain all in-flight requests before updating the rangeID.
// This is because requests are conditioned on rangeID, if rangeID
// is updated before draining them, those requests could fail.
// This also means renew rangeID will be the only in-flight request
// when it's issued, so it doesn't matter if semaphore is acquired or not
// before calling this method.
s.taskKeyManager.drainTaskRequests()
updatedShardInfo := trimShardInfo(s.config, s.clusterMetadata.GetAllClusterInfo(), s.copyShardInfo(s.shardInfo))
updatedShardInfo.RangeId++
if isStealing {
updatedShardInfo.StolenSinceRenew++
}
defer cancel()
previousRangeID := s.getRangeIDLocked()
err := s.persistenceShardManager.UpdateShard(ctx, &persistence.UpdateShardRequest{
ShardInfo: updatedShardInfo,
PreviousRangeID: previousRangeID,
})
if err != nil {
// Failure in updating shard to grab new RangeID
s.contextTaggedLogger.Error("Persistent store operation failure",
// Range is successfully updated in cassandra now update shard context to reflect new range
tag.ShardRangeID(updatedShardInfo.RangeId),
tag.PreviousShardRangeID(s.shardInfo.RangeId),
)
s.shardInfo = trimShardInfo(s.config, s.clusterMetadata.GetAllClusterInfo(), s.copyShardInfo(updatedShardInfo))
s.taskKeyManager.setRangeID(s.shardInfo.RangeId)
return nil
}
timer := time.NewTimer(queueMetricUpdateInterval)
defer timer.Stop()
done := s.lifecycleCtx.Done()
for {
select {
return
case <-timer.C:
s.emitShardInfoMetricsLogs()
}
if cluster != s.GetClusterMetadata().GetCurrentClusterName() {
s.wLock()
defer s.wUnlock()
return s.getOrUpdateRemoteClusterInfoLocked(cluster).CurrentTime
}
}
s.rLock()
defer s.rUnlock()
return s.lastUpdated
}
switch err.(type) {
return nil
case *persistence.ShardOwnershipLostError:
requestRangeID int64,
err error,
s.wLock()
defer s.wUnlock()
return s.handleWriteErrorLocked(requestRangeID, err)
}
func (s *ContextImpl) handleWriteErrorLocked(
requestRangeID int64,
err error,
if requestRangeID != s.getRangeIDLocked() {
return err
}
return err
}
// Persistence success: update max read level
return nil
case *persistence.AppendHistoryTimeoutError:
}
func (s *ContextImpl) maybeRecordShardAcquisitionLatency(ownershipChanged bool) {
context_impl.go
if ownershipChanged {
metrics.ShardContextAcquisitionLatency.With(s.GetMetricsHandler()).
Record(s.GetCurrentTime(s.GetClusterMetadata().GetCurrentClusterName()).Sub(s.getLastUpdatedTime()),
metrics.OperationTag(metrics.ShardInfoScope),
)
}
}
s.contextTaggedLogger.Info("", tag.LifeCycleStarting, tag.ComponentShardEngine)
engine := s.engineFactory.CreateEngine(s)
engine.Start()
s.contextTaggedLogger.Info("", tag.LifeCycleStarted, tag.ComponentShardEngine)
return engine
}
// start should only be called by the controller.
_ = s.transition(contextRequestAcquire{})
}
func (s *ContextImpl) UnloadForOwnershipLost() {
// FinishStop should only be called by the controller.
// After this returns, engineFuture.Set may not be called anymore, so if we don't get see
// an Engine here, we won't ever have one.
_ = s.transition(contextRequestFinishStop{})
// Use a context that we know is cancelled so that this doesn't block.
engine, _ := s.engineFuture.Get(s.lifecycleCtx)
// Stop the engine if it was running (outside the lock but before returning).
if engine != nil {
s.contextTaggedLogger.Info("", tag.LifeCycleStopping, tag.ComponentShardEngine)
context_impl.go
engine.Stop()
s.contextTaggedLogger.Info("", tag.LifeCycleStopped, tag.ComponentShardEngine)
}
// Run finalizer to cleanup any of the shard's associated resources that are registered.
}
}
s.stateLock.Lock()
defer s.stateLock.Unlock()
return s.state < contextStateStopping
}
func (s *ContextImpl) GetLifecycleContext() context.Context {
}
handler := s.metricsHandler.WithTags(metrics.OperationTag(metrics.ShardInfoScope))
metrics.LockRequests.With(handler).Record(1)
startTime := time.Now().UTC()
defer func() { metrics.LockLatency.With(handler).Record(time.Since(startTime)) }()
}
handler := s.metricsHandler.WithTags(metrics.OperationTag(metrics.ShardInfoScope))
metrics.LockRequests.With(handler).Record(1)
startTime := time.Now().UTC()
defer func() { metrics.LockLatency.With(handler).Record(time.Since(startTime)) }()
}
s.rwLock.Unlock()
}
s.rwLock.RUnlock()
}
func (s *ContextImpl) ioSemaphoreAcquire(
ctx context.Context,
priority := locks.PriorityHigh
callerInfo := headers.GetCallerInfo(ctx)
if callerInfo.CallerType == headers.CallerTypePreemptable {
priority = locks.PriorityLow
}
handler := s.metricsHandler.WithTags(metrics.OperationTag(metrics.ShardInfoScope), metrics.PriorityTag(priority))
context_impl.go
metrics.SemaphoreRequests.With(handler).Record(1)
startTime := time.Now().UTC()
defer func() {
metrics.SemaphoreLatency.With(handler).Record(time.Since(startTime))
if retErr != nil {
metrics.SemaphoreFailures.With(handler).Record(1)
}
}()
}
s.ioSemaphore.Release(1)
}
/* State transitions:
The normal pattern:
Initialized
controller calls start()
Acquiring
acquireShard gets the shard
Acquired
If we get a transient error from persistence:
Acquired
transient error: handleErrorLocked calls transition(contextRequestLost)
Acquiring
acquireShard gets the shard
Acquired
If we get shard ownership lost:
Acquired
ShardOwnershipLostError: handleErrorLocked calls transition(contextRequestStop)
Stopping
controller removes from map and calls FinishStop()
Stopped
Stopping can be triggered internally (if we get a ShardOwnershipLostError, or fail to acquire the rangeid
lock after several minutes) or externally (from controller, e.g. controller shutting down or admin force-
unload shard). If it's triggered internally, we transition to Stopping, then make an asynchronous callback
to controller, which will remove us from the map and call FinishStop(), which will transition to Stopped and
stop the engine. If it's triggered externally, we'll skip over Stopping and go straight to Stopped.
If we transition externally to Stopped, and the acquireShard goroutine is still running, we can't kill it,
but we should make sure that it can't do anything: the context it uses for persistence ops will be
canceled, and if it tries to transition states, it will fail.
Invariants:
- Once state is Stopping, it can only go to Stopped.
- Once state is Stopped, it can't go anywhere else.
- At the start of acquireShard, state must be Acquiring.
- By the end of acquireShard, state must not be Acquiring: either acquireShard set it to Acquired, or the
controller set it to Stopped.
- If state is Acquiring, acquireShard should be running in the background.
- Only acquireShard can use contextRequestAcquired (i.e. transition from Acquiring to Acquired).
- Once state has reached Acquired at least once, and not reached Stopped, engineFuture must be set.
- Only the controller may call start() and FinishStop().
- The controller must call FinishStop() for every ContextImpl it creates.
*/
s.stateLock.Lock()
defer s.stateLock.Unlock()
setStateAcquiring := func() {
s.contextTaggedLogger.Info("", tag.LifeCycleStarted, tag.ComponentShardContext)
go s.acquireShard()
}
s.state = contextStateStopping
s.stopReason = request.reason
}
s.contextTaggedLogger.Info("", tag.LifeCycleStopped, tag.ComponentShardContext)
// Do this again in case we skipped the stopping state, which could happen
// when calling CloseShardByID or the controller is shutting down.
s.lifecycleCancel()
}
switch request := request.(type) {
case contextRequestAcquire:
setStateAcquiring()
return nil
case contextRequestStop:
setStateStopping(request)
return nil
}
switch request := request.(type) {
case contextRequestAcquire:
return nil // nothing to do, already acquiring
s.state = contextStateAcquired
if request.engine != nil {
// Acquiring, so that other code (i.e. FinishStop) can know that after a state
// transition to Stopping/Stopped, engineFuture cannot be Set.
if s.engineFuture.Ready() {
// defensive check, this should never happen
s.contextTaggedLogger.Warn("transition to acquired with engine set twice")
return errInvalidTransition
}
}
// we should either have an engine from a previous transition, or set one now
s.contextTaggedLogger.Warn("transition to acquired but no engine set")
}
case contextRequestLost:
return nil // nothing to do, already acquiring
return nil
}
switch request := request.(type) {
case contextRequestAcquire:
return nil // nothing to do, already acquired
setStateStopping(request)
return nil
setStateStopped()
return nil
}
case contextStateStopping:
// notifyQueueProcessor sends notification to all queue processors for triggering a load
// NOTE: this method assumes engineFuture is already in a ready state.
// use a cancelled ctx so the method won't be blocked if engineFuture is not ready
cancelledCtx, cancel := context.WithCancel(context.Background())
cancel()
// we will get the engine when the Future is ready
engine, err := s.engineFuture.Get(cancelledCtx)
if err != nil {
s.contextTaggedLogger.Warn("tried to notify queue processor when engine is not ready")
return
}
fakeTasks := make(map[tasks.Category][]tasks.Task)
for _, category := range s.taskCategoryRegistry.GetCategories() {
fakeTasks[category] = []tasks.Task{tasks.NewFakeTask(definition.WorkflowKey{}, category, now)}
}
}
s.wLock()
if s.errorByState() != nil {
// if not in acquired state, this function will be called again
// later when shard is re-acquired.
}
s.handoverTracker.ResolvePendingTaskIDs(maxReplicationTaskID)
s.wUnlock()
s.notifyReplicationQueueProcessor(maxReplicationTaskID)
}
return s.taskKeyManager.getExclusiveReaderHighWatermark(tasks.CategoryReplication).TaskID - 1
}
// Replication ack level won't exceed the max taskID it received via task notification.
// Since here we want it's ack level to advance to at least the input taskID, we need to
// trigger an fake notification.
cancelledCtx, cancel := context.WithCancel(context.Background())
cancel()
engine, err := s.engineFuture.Get(cancelledCtx)
if err != nil {
s.contextTaggedLogger.Warn("tried to notify replication queue processor when engine is not ready")
return
}
fakeReplicationTask := tasks.NewFakeTask(definition.WorkflowKey{}, tasks.CategoryReplication, tasks.MinimumKey.FireTime)
context_impl.go
fakeReplicationTask.SetTaskID(taskID)
engine.NotifyNewTasks(map[tasks.Category][]tasks.Task{
tasks.CategoryReplication: {fakeReplicationTask},
})
}
// Only have to do this once, we can just re-acquire the rangeid lock after that
s.rLock()
if s.shardInfo != nil {
s.rUnlock()
return nil
}
// We don't have any shardInfo yet, load it (outside of context rwlock)
ctx, cancel := s.newIOContext()
defer cancel()
resp, err := s.persistenceShardManager.GetOrCreateShard(ctx, &persistence.GetOrCreateShardRequest{
ShardID: s.shardID,
LifecycleContext: s.lifecycleCtx,
})
if err != nil {
s.contextTaggedLogger.Error("Failed to load shard", tag.Error(err))
return err
}
shardInfo := trimShardInfo(s.config, s.clusterMetadata.GetAllClusterInfo(), s.copyShardInfo(resp.ShardInfo))
shardInfo.Owner = s.owner
// initialize the cluster current time to be the same as ack level
remoteClusterInfos := make(map[string]*remoteClusterInfo)
var taskMinScheduledTime time.Time
currentClusterName := s.GetClusterMetadata().GetCurrentClusterName()
taskCategories := s.taskCategoryRegistry.GetCategories()
for clusterName, info := range s.GetClusterMetadata().GetAllClusterInfo() {
if !info.Enabled {
continue
}
for categoryID, queueState := range shardInfo.QueueStates {
category, ok := taskCategories[int(categoryID)]
if !ok || category.Type() != tasks.CategoryTypeScheduled {
// Once we validate the rest of the code can worker correctly with higher precision, the code should simply be
// taskMinScheduledTime = util.MaxTime(taskMinScheduledTime, maxReadTime)
taskMinScheduledTime,
exclusiveMaxReadTime.Add(common.ScheduledTaskMinPrecision).Truncate(common.ScheduledTaskMinPrecision),
)
if clusterName != currentClusterName {
remoteClusterInfos[clusterName] = &remoteClusterInfo{
CurrentTime: exclusiveMaxReadTime,
}
defer s.wUnlock()
s.shardInfo = shardInfo
s.remoteClusterInfos = remoteClusterInfos
s.taskKeyManager.setTaskMinScheduledTime(taskMinScheduledTime)
return nil
}
}
// This is called in two contexts: initially acquiring the rangeid lock, and trying to
// re-acquire it after a persistence error. In both cases, we retry the acquire operation
// (renewRangeLocked) for 5 minutes. Each individual attempt uses shardIOTimeout (by default, 5s) as
// the timeout. This lets us handle a few minutes of persistence unavailability without
// dropping and reloading the whole shard context, which is relatively expensive (includes
// caches that would have to be refilled, etc.).
//
// We stop retrying on any of:
// 1. We succeed in acquiring the rangeid lock.
// 2. We get ShardOwnershipLostError or lifecycleCtx ended.
// 3. The state changes to Stopping or Stopped.
//
// If the shard controller sees that service resolver has assigned ownership to someone
// else, it will call FinishStop, which will trigger case 3 above, and also cancel
// lifecycleCtx. The persistence operations called here use lifecycleCtx as their context,
// so if we were blocked in any of them, they should return immediately with a context
// canceled error.
policy := s.acquireShardRetryPolicy
if policy == nil {
policy = backoff.NewExponentialRetryPolicy(1 * time.Second).WithExpirationInterval(5 * time.Minute)
context_impl.go
}
// Remember this value across attempts
op := func() error {
if !s.IsValid() {
return s.newShardClosedErrorWithShardID()
}
// Initial load of shard metadata
if err != nil {
return err
}
// in-flight requests before making the call. So it's guaranteed that the renew rangeID
// UpdateShard call is the only one in flight.
err = s.renewRangeLocked(true)
s.wUnlock()
if err != nil {
return err
}
// The first time we get the shard, we have to create the engine
var engine historyi.Engine
if !s.engineFuture.Ready() {
engine = s.createEngine()
}
// NOTE: engine is created & started before setting shard state to acquired.
// -> information for handover namespace is recorded before shard can servce traffic
// -> upon shard reload, no history api or task can go through for ns in handover state
if err != nil {
if engine != nil {
// We tried to set the engine but the context was already stopped
// we know engineFuture must be ready here, and we can notify queue processor
// to trigger a load as queue max level can be updated to a newer value
// This runs until the lifecycleCtx is cancelled, so we only need to start it once
s.queueMetricEmitter.Do(func() {
go s.monitorQueueMetrics()
})
return nil
}
// keep retrying except ShardOwnershipLostError or lifecycle context ended
defer func() {
s.contextTaggedLogger.Error(
return true
}
if err != nil {
// We got an non-retryable error, e.g. ShardOwnershipLostError
s.contextTaggedLogger.Error("Couldn't acquire shard", tag.Error(err))
endpointRegistry chasm.EndpointRegistry,
handoverTrackerFactory HandoverTrackerFactory,
hostIdentity := hostInfoProvider.HostInfo().Identity()
sequenceID := atomic.AddInt64(&shardContextSequenceID, 1)
lifecycleCtx, lifecycleCancel := context.WithCancel(context.Background())
ioConcurrency := historyConfig.ShardIOConcurrency()
if ioConcurrency != 1 && persistenceConfig.DataStores[persistenceConfig.DefaultStore].Cassandra != nil {
throttledLogger.Warn(
fmt.Sprintf("Cassandra persistence implementation only supports %v == 1", dynamicconfig.ShardIOConcurrency),
}
taggedLogger := log.With(logger, tag.ShardID(shardID), tag.Address(hostIdentity))
context_impl.go
shardContext := &ContextImpl{
state: contextStateInitialized,
shardID: shardID,
owner: fmt.Sprintf("%s-%v-%v", hostIdentity, sequenceID, uuid.NewString()),
stringRepr: fmt.Sprintf("Shard(%d)", shardID),
executionManager: persistenceExecutionManager,
metricsHandler: metricsHandler,
eventLogger: eventLogger,
closeCallback: closeCallback,
config: historyConfig,
finalizer: finalizer.New(taggedLogger, metricsHandler),
contextTaggedLogger: taggedLogger,
throttledLogger: log.With(throttledLogger, tag.ShardID(shardID), tag.Address(hostIdentity)),
engineFactory: factory,
persistenceShardManager: persistenceShardManager,
clientBean: clientBean,
historyClient: historyClient,
payloadSerializer: payloadSerializer,
timeSource: timeSource,
namespaceRegistry: namespaceRegistry,
saProvider: saProvider,
saMapperProvider: saMapperProvider,
clusterMetadata: clusterMetadata,
archivalMetadata: archivalMetadata,
hostInfoProvider: hostInfoProvider,
taskCategoryRegistry: taskCategoryRegistry,
lifecycleCtx: lifecycleCtx,
lifecycleCancel: lifecycleCancel,
engineFuture: future.NewFuture[historyi.Engine](),
queueMetricEmitter: sync.Once{},
ioSemaphore: locks.NewPrioritySemaphore(ioConcurrency),
stateMachineRegistry: stateMachineRegistry,
chasmRegistry: chasmRegistry,
chasmWorkflowRegistry: chasmWorkflowRegistry,
endpointRegistry: endpointRegistry,
businessIDRateLimiters: cache.New(
historyConfig.BusinessIDReuseLimiterCacheSize(),
&cache.Options{TTL: historyConfig.BusinessIDReuseLimiterCacheTTL()},
),
}
shardContext.taskKeyManager = newTaskKeyManager(
shardContext.taskCategoryRegistry,
timeSource,
historyConfig,
shardContext.GetLogger(),
func() error {
return shardContext.renewRangeLocked(false)
},
)
ClusterMetadata: clusterMetadata,
GetMaxReplicationTaskID: shardContext.getMaxReplicationTaskID,
ErrorByStateFn: shardContext.errorByState,
NotifyReplicationFn: shardContext.notifyReplicationQueueProcessor,
NamespaceRegistry: namespaceRegistry,
Logger: taggedLogger,
})
if shardContext.GetConfig().EnableHostLevelEventsCache() {
shardContext.eventsCache = eventsCache
shardContext.eventsCache = events.NewShardLevelEventsCache(
shardContext.executionManager,
shardContext.config,
shardContext.metricsHandler,
shardContext.contextTaggedLogger,
false,
)
}
shardContext.initLastUpdatesTime()
return shardContext, nil
}
// We need to set lastUpdate time to "now" - "wait between shard updates time" + "first update interval".
// This is done to make sure that first shard update` will happen around "first update interval" after "now".
// The idea is to allow queue to persist even in the case of (relativly) constantly
// moving shards between hosts.
// Note: it still may prevent queue from progressing if shard moving rate is too high
lastUpdated := s.timeSource.Now()
lastUpdated = lastUpdated.Add(-1 * s.config.ShardUpdateMinInterval())
lastUpdated = lastUpdated.Add(s.config.ShardFirstUpdateInterval())
s.lastUpdated = lastUpdated
}
// TODO: why do we need a deep copy here?
func (s *ContextImpl) copyShardInfo(shardInfo *persistencespb.ShardInfo) *persistencespb.ShardInfo {
context_impl.go
// need to ser/de to make a deep copy of queue state
queueStates := make(map[int32]*persistencespb.QueueState, len(shardInfo.QueueStates))
for k, v := range shardInfo.QueueStates {
blob, _ := s.payloadSerializer.QueueStateToBlob(v)
queueState, _ := s.payloadSerializer.QueueStateFromBlob(blob)
}
ShardId: shardInfo.ShardId,
Owner: shardInfo.Owner,
RangeId: shardInfo.RangeId,
StolenSinceRenew: shardInfo.StolenSinceRenew,
ReplicationDlqAckLevel: maps.Clone(shardInfo.ReplicationDlqAckLevel),
UpdateTime: shardInfo.UpdateTime,
QueueStates: queueStates,
}
}
}
return s.payloadSerializer
}
func (s *ContextImpl) GetHistoryClient() historyservice.HistoryServiceClient {
}
return s.metricsHandler
}
return s.timeSource
}
return s.namespaceRegistry
}
func (s *ContextImpl) GetSearchAttributesProvider() searchattribute.Provider {
context_impl.go
return s.saProvider
}
func (s *ContextImpl) GetSearchAttributesMapperProvider() searchattribute.MapperProvider {
context_impl.go
return s.saMapperProvider
}
return s.clusterMetadata
}
func (s *ContextImpl) GetArchivalMetadata() archiver.ArchivalMetadata {
}
return s.stateMachineRegistry
}
func (s *ContextImpl) ChasmRegistry() *chasm.Registry {
}
return s.chasmWorkflowRegistry
}
func (s *ContextImpl) EndpointRegistry() chasm.EndpointRegistry {
func (s *ContextImpl) newDetachedContext(
ctx context.Context,
if err := ctx.Err(); err != nil {
return nil, nil, err
}
var cancel context.CancelFunc
deadline, ok := ctx.Deadline()
if ok {
timeout := max(deadline.Sub(s.GetTimeSource().Now()), minContextTimeout)
detachedContext, cancel = context.WithTimeout(detachedContext, timeout)
}
}
ctx, cancel := context.WithTimeout(s.lifecycleCtx, s.config.ShardIOTimeout())
ctx = headers.SetCallerInfo(ctx, headers.SystemBackgroundHighCallerInfo)
return ctx, cancel
}
// newShardClosedErrorWithShardID when shard is closed and a req cannot be processed
allClusterInfo map[string]cluster.ClusterInformation,
shardInfo *persistencespb.ShardInfo,
if shardInfo.QueueStates != nil && shardInfo.QueueStates[int32(tasks.CategoryIDReplication)] != nil {
for readerID := range shardInfo.QueueStates[int32(tasks.CategoryIDReplication)].ReaderStates {
clusterID, _ := ReplicationReaderIDToClusterShardID(readerID)
fx.Provide(AuthorizationInterceptorProvider),
fx.Provide(NamespaceCheckerProvider),
fx.Provide(func(so GrpcServerOptions) *grpc.Server { return grpc.NewServer(so.Options...) }),
fx.go
fx.Provide(callbackValidatorProvider),
fx.Provide(HandlerProvider),
metricsHandler metrics.Handler,
membershipMonitor membership.Monitor,
return NewService(
serviceConfig,
server,
healthServer,
httpAPIServer,
handler,
adminHandler,
operatorHandler,
versionChecker,
visibilityMgr,
logger,
grpcListener,
metricsHandler,
membershipMonitor,
)
}
// GrpcServerOptions are the options to build the frontend gRPC server along
audienceGetter authorization.JWTAudienceMapper,
dc *dynamicconfig.Collection,
return authorization.NewInterceptor(
claimMapper,
authorizer,
metricsHandler,
logger,
namespaceChecker,
audienceGetter,
cfg.Global.Authorization.AuthHeaderName,
cfg.Global.Authorization.AuthExtraHeaderName,
serviceConfig.ExposeAuthorizerErrors,
dynamicconfig.EnableCrossNamespaceCommands.Get(dc),
dynamicconfig.EnablePrincipalPropagation.Get(dc),
dynamicconfig.DisableStreamingAuthorizer.Get(dc),
)
}
func NamespaceCheckerProvider(registry namespace.Registry) authorization.NamespaceChecker {
fx.go
return &namespaceChecker{r: registry}
}
// This will get called before the namespace state validation interceptor. We want to
// disable readthrough to avoid polluting the negative lookup cache, e.g. if this call is
// for RegisterNamespace and the namespace doesn't exist yet.
opts := namespace.GetNamespaceOptions{DisableReadthrough: true}
_, err := n.r.GetNamespaceWithOptions(name, opts)
return err
}
func GrpcServerOptionsProvider(
customStreamInterceptors []grpc.StreamServerInterceptor,
metricsHandler metrics.Handler,
kep := keepalive.EnforcementPolicy{
MinTime: serviceConfig.KeepAliveMinTime(),
PermitWithoutStream: serviceConfig.KeepAlivePermitWithoutStream(),
}
kp := keepalive.ServerParameters{
MaxConnectionIdle: serviceConfig.KeepAliveMaxConnectionIdle(),
MaxConnectionAge: serviceConfig.KeepAliveMaxConnectionAge(),
MaxConnectionAgeGrace: serviceConfig.KeepAliveMaxConnectionAgeGrace(),
Time: serviceConfig.KeepAliveTime(),
Timeout: serviceConfig.KeepAliveTimeout(),
}
var grpcServerOptions []grpc.ServerOption
var err error
switch serviceName {
case primitives.FrontendService:
grpcServerOptions, err = rpcFactory.GetFrontendGRPCServerOptions()
case primitives.InternalFrontendService:
grpcServerOptions, err = rpcFactory.GetInternodeGRPCServerOptions()
err = fmt.Errorf("unexpected frontend service name %q", serviceName)
}
logger.Fatal("creating gRPC server options failed", tag.Error(err))
}
// Order of interceptors is important
// Mask error interceptor should be the most outer interceptor since it handle the errors format
// Service Error Interceptor should be the next most outer interceptor on error handling
maskInternalErrorDetailsInterceptor.Intercept,
serviceErrorInterceptor.Intercept,
interceptor.NewFrontendServiceErrorInterceptor(logger),
// BusinessID interceptor extracts business ID and adds it to context for use, must be before any interceptor that touches namespaces (namespaceValidator, handoverInterceptor)
businessIDInterceptor.Intercept,
namespaceValidatorInterceptor.NamespaceValidateIntercept,
namespaceLogInterceptor.Intercept, // TODO: Deprecate this with a outer custom interceptor
metrics.NewServerMetricsContextInjectorInterceptor(),
authInterceptor.Intercept,
// Handover interceptor has to above redirection because the request will route to the correct cluster after handover completed.
// And retry cannot be performed before customInterceptors.
namespaceHandoverInterceptor.Intercept,
redirectionInterceptor.Intercept,
// Telemetry interceptor must be after redirection to ensure metrics are recorded in the correct cluster
telemetryInterceptor.UnaryIntercept,
healthInterceptor.Intercept,
namespaceValidatorInterceptor.StateValidationIntercept,
namespaceCountLimiterInterceptor.Intercept,
namespaceRateLimiterInterceptor.Intercept,
rateLimitInterceptor.Intercept,
sdkVersionInterceptor.Intercept,
callerInfoInterceptor.Intercept,
slowRequestLoggerInterceptor.Intercept,
chasmRequestVisibilityInterceptor.Intercept,
contextMetadataInterceptor.Intercept,
}
if len(customInterceptors) > 0 {
// TODO: Deprecate WithChainedFrontendGrpcInterceptors and provide a inner custom interceptor
unaryInterceptors = append(unaryInterceptors, customInterceptors...)
}
// retry interceptor should be the most inner interceptor
streamInterceptor := []grpc.StreamServerInterceptor{
authInterceptor.InterceptStream,
telemetryInterceptor.StreamIntercept,
}
if len(customStreamInterceptors) > 0 {
streamInterceptor = append(streamInterceptor, customStreamInterceptors...)
}
grpcServerOptions,
grpc.KeepaliveParams(kp),
grpc.KeepaliveEnforcementPolicy(kep),
grpc.ChainUnaryInterceptor(unaryInterceptors...),
grpc.ChainStreamInterceptor(streamInterceptor...),
)
multiStats := rpc.MultiStatsHandler{}
if traceStatsHandler != nil {
multiStats = append(multiStats, traceStatsHandler)
}
multiStats = append(multiStats, metricsStatsHandler)
}
if len(multiStats) > 0 {
grpcServerOptions = append(grpcServerOptions, grpc.StatsHandler(multiStats))
}
return GrpcServerOptions{Options: grpcServerOptions, UnaryInterceptors: unaryInterceptors}
}
dc *dynamicconfig.Collection,
persistenceConfig config.Persistence,
return NewConfig(
dc,
persistenceConfig.NumHistoryShards,
)
}
func ServiceErrorInterceptorProvider(
dc *dynamicconfig.Collection,
return interceptor.NewServiceErrorInterceptor(
dynamicconfig.MaxServiceErrorMessageLength.Get(dc),
)
}
func ThrottledLoggerRpsFnProvider(serviceConfig *Config) resource.ThrottledLoggerRpsFn {
fx.go
return func() float64 { return float64(serviceConfig.ThrottledLogRPS()) }
}
namespaceLogger resource.NamespaceLogger,
namespaceRegistry namespace.Registry,
return interceptor.NewNamespaceLogInterceptor(
namespaceRegistry,
namespaceLogger)
}
return interceptor.NewRetryableInterceptor(
common.CreateFrontendHandlerRetryPolicy(),
common.IsServiceHandlerRetryableError,
)
}
func RedirectionInterceptorProvider(
timeSource clock.TimeSource,
clusterMetadata cluster.Metadata,
return interceptor.NewRedirection(
configuration.EnableNamespaceNotActiveAutoForwarding,
configuration.ForceNamespaceSelectedAPIAutoForwarding,
namespaceCache,
policy,
logger,
clientBean,
metricsHandler,
timeSource,
clusterMetadata,
)
}
func BusinessIDInterceptorProvider(
extractor interceptor.RoutingKeyExtractor,
logger log.Logger,
return interceptor.NewRoutingKeyInterceptor(
[]interceptor.RoutingKeyExtractorFunc{
interceptor.WorkflowServiceExtractor(extractor),
},
logger,
)
}
type NamespaceHandoverInterceptorParams struct {
func NamespaceHandoverInterceptorProvider(
params NamespaceHandoverInterceptorParams,
return interceptor.NewNamespaceHandoverInterceptor(
params.DynamicConfig,
params.NamespaceRegistry,
params.MetricsHandler,
params.Logger,
params.TimeSource,
params.RequestErrorHandler,
params.AdditionalAllowedMethodsDuringHandover,
)
}
func ErrorHandlerProvider(
logger log.Logger,
serviceConfig *Config,
return interceptor.NewRequestErrorHandler(
logger,
serviceConfig.LogAllReqErrors,
)
}
func TelemetryInterceptorProvider(
serviceConfig *Config,
requestErrorHandler *interceptor.RequestErrorHandler,
return interceptor.NewTelemetryInterceptor(
namespaceRegistry,
metricsHandler,
logger,
serviceConfig.LogAllReqErrors,
requestErrorHandler,
)
}
func getRateFnWithMetrics(rateFn quotas.RateFn, handler metrics.Handler) quotas.RateFn {
fx.go
return func() float64 {
rate := rateFn()
metrics.HostRPSLimit.With(handler).Record(rate)
return rate
}
}
handler metrics.Handler,
logger log.SnTaggedLogger,
rateFn := calculator.NewLoggedCalculator(
calculator.ClusterAwareQuotaCalculator{
MemberCounter: frontendServiceResolver,
PerInstanceQuota: serviceConfig.RPS,
GlobalQuota: serviceConfig.GlobalRPS,
},
log.With(logger, tag.ComponentRPCHandler, tag.ScopeHost),
).GetQuota
rateFnWithMetrics := getRateFnWithMetrics(rateFn, handler)
namespaceReplicationInducingRateFn := func() float64 {
return float64(serviceConfig.NamespaceReplicationInducingAPIsRPS())
}
configs.NewRequestToRateLimiter(
quotas.NewDefaultIncomingRateBurst(rateFnWithMetrics),
quotas.NewDefaultIncomingRateBurst(rateFn),
quotas.NewDefaultIncomingRateBurst(namespaceReplicationInducingRateFn),
serviceConfig.OperatorRPSRatio,
),
map[string]int{
healthpb.Health_Check_FullMethodName: 0, // exclude health check requests from rate limiting.
adminservice.AdminService_DeepHealthCheck_FullMethodName: 0, // exclude deep health check requests from rate limiting.
},
)
}
logger log.Logger,
dc *dynamicconfig.Collection,
setTrailer := dynamicconfig.FrontendContextMetadataSetTrailer.Get(dc)()
return interceptor.NewContextMetadataInterceptor(setTrailer, logger)
}
func MaskInternalErrorDetailsInterceptorProvider(
serviceConfig *Config,
namespaceRegistry namespace.Registry,
return interceptor.NewMaskInternalErrorDetailsInterceptor(
serviceConfig.MaskInternalErrorDetails, namespaceRegistry, logger,
)
}
func NamespaceRateLimitInterceptorProvider(
metricsHandler metrics.Handler,
logger log.SnTaggedLogger,
var globalNamespaceRPS, globalNamespaceVisibilityRPS, globalNamespaceNamespaceReplicationInducingAPIsRPS dynamicconfig.IntPropertyFnWithNamespaceFilter
switch serviceName {
case primitives.FrontendService:
globalNamespaceRPS = serviceConfig.GlobalNamespaceRPS
globalNamespaceVisibilityRPS = serviceConfig.GlobalNamespaceVisibilityRPS
globalNamespaceNamespaceReplicationInducingAPIsRPS = serviceConfig.GlobalNamespaceNamespaceReplicationInducingAPIsRPS
case primitives.InternalFrontendService:
globalNamespaceRPS = serviceConfig.InternalFEGlobalNamespaceRPS
}
calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: frontendServiceResolver,
PerInstanceQuota: serviceConfig.MaxNamespaceRPSPerInstance,
GlobalQuota: globalNamespaceRPS,
},
log.With(logger, tag.ComponentRPCHandler, tag.ScopeNamespace),
).GetQuota
visibilityRateFn := calculator.NewLoggedNamespaceCalculator(
calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: frontendServiceResolver,
PerInstanceQuota: serviceConfig.MaxNamespaceVisibilityRPSPerInstance,
GlobalQuota: globalNamespaceVisibilityRPS,
},
log.With(logger, tag.ComponentVisibilityHandler, tag.ScopeNamespace),
).GetQuota
namespaceReplicationInducingRateFn := calculator.NewLoggedNamespaceCalculator(
calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: frontendServiceResolver,
PerInstanceQuota: serviceConfig.MaxNamespaceNamespaceReplicationInducingAPIsRPSPerInstance,
GlobalQuota: globalNamespaceNamespaceReplicationInducingAPIsRPS,
},
log.With(logger, tag.ComponentNamespaceReplication, tag.ScopeNamespace),
).GetQuota
namespaceRateLimiter := quotas.NewNamespaceRequestRateLimiter(
func(req quotas.Request) quotas.RequestRateLimiter {
return configs.NewRequestToRateLimiter(
quotas.NewNamespaceRateBurst(
},
)
namespaceRegistry,
namespaceRateLimiter,
map[string]int{}, // no token overrides
configs.PollTaskAPISet,
serviceConfig.PollWaitForNamespaceRateLimitToken,
metricsHandler,
)
}
serviceResolver membership.ServiceResolver,
logger log.SnTaggedLogger,
return interceptor.NewConcurrentRequestLimitInterceptor(
namespaceRegistry,
serviceResolver,
logger,
serviceConfig.MaxConcurrentLongRunningRequestsPerInstance,
serviceConfig.MaxGlobalConcurrentLongRunningRequests,
configs.ExecutionAPICountLimitOverride,
)
}
type NamespaceValidatorInterceptorParams struct {
func NamespaceValidatorInterceptorProvider(
params NamespaceValidatorInterceptorParams,
return interceptor.NewNamespaceValidatorInterceptor(
params.NamespaceRegistry,
params.ServiceConfig.EnableTokenNamespaceEnforcement,
params.ServiceConfig.MaxIDLengthLimit,
params.AdditionalAllowedMethodsDuringHandover,
)
}
return interceptor.NewSDKVersionInterceptor()
}
func CallerInfoInterceptorProvider(
namespaceRegistry namespace.Registry,
return interceptor.NewCallerInfoInterceptor(namespaceRegistry)
}
func SlowRequestLoggerInterceptorProvider(
logger log.Logger,
dc *dynamicconfig.Collection,
return interceptor.NewSlowRequestLoggerInterceptor(
logger,
dynamicconfig.SlowRequestLoggingThreshold.Get(dc),
)
}
func PersistenceRateLimitingParamsProvider(
persistenceLazyLoadedServiceResolver service.PersistenceLazyLoadedServiceResolver,
logger log.SnTaggedLogger,
return service.NewPersistenceRateLimitingParams(
serviceConfig.PersistenceMaxQPS,
serviceConfig.PersistenceGlobalMaxQPS,
serviceConfig.PersistenceNamespaceMaxQPS,
serviceConfig.PersistenceGlobalNamespaceMaxQPS,
serviceConfig.PersistencePerShardNamespaceMaxQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.PersistenceQPSBurstRatio,
serviceConfig.PersistenceDynamicRateLimitingParams,
persistenceLazyLoadedServiceResolver,
logger,
)
}
func VisibilityManagerProvider(
chasmRegistry *chasm.Registry,
serializer serialization.Serializer,
return visibility.NewManager(
*persistenceConfig,
persistenceServiceResolver,
customVisibilityStoreFactory,
nil, // frontend visibility never write
saProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
serviceConfig.VisibilityPersistenceMaxReadQPS,
serviceConfig.VisibilityPersistenceMaxWriteQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.VisibilityPersistenceSlowQueryThreshold,
serviceConfig.EnableReadFromSecondaryVisibility,
serviceConfig.VisibilityEnableShadowReadMode,
dynamicconfig.GetStringPropertyFn(visibility.SecondaryVisibilityWritingModeOff), // frontend visibility never write
serviceConfig.VisibilityDisableOrderByClause,
serviceConfig.VisibilityEnableManualPagination,
serviceConfig.VisibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
}
func FEReplicatorNamespaceReplicationQueueProvider(
namespaceReplicationQueue persistence.NamespaceReplicationQueue,
clusterMetadata cluster.Metadata,
var replicatorNamespaceReplicationQueue persistence.NamespaceReplicationQueue
if clusterMetadata.IsGlobalNamespaceEnabled() {
replicatorNamespaceReplicationQueue = namespaceReplicationQueue
}
}
membershipMonitor membership.Monitor,
serviceName primitives.ServiceName,
return membershipMonitor.GetResolver(serviceName)
}
func AdminHandlerProvider(
schedulerClient schedulerpb.SchedulerServiceClient,
namespaceDLQHandler nsreplication.DLQMessageHandler,
args := NewAdminHandlerArgs{
persistenceConfig,
configuration,
namespaceReplicationQueue,
replicatorNamespaceReplicationQueue,
visibilityMgr,
logger,
taskManager,
fairTaskManager,
persistenceExecutionManager,
clusterMetadataManager,
persistenceMetadataManager,
clientFactory,
clientBean,
historyClient,
sdkClientFactory,
membershipMonitor,
hostInfoProvider,
metricsHandler,
namespaceRegistry,
saProvider,
saManager,
saMapperProvider,
clusterMetadata,
healthServer,
eventSerializer,
timeSource,
chasmRegistry,
namespaceDataMerger,
schedulerClient,
taskCategoryRegistry,
matchingClient,
}
return NewAdminHandler(args, namespaceDLQHandler)
}
// NamespaceDLQHandlerProvider provides the default namespace DLQ message handler.
logger log.SnTaggedLogger,
testHooks testhooks.TestHooks,
taskExecutor := nsreplication.NewTaskExecutor(
clusterMetadata.GetCurrentClusterName(),
persistenceMetadataManager,
namespaceDataMerger,
namespaceAdmitter,
logger,
testHooks,
)
return nsreplication.NewDLQMessageHandler(
taskExecutor,
namespaceReplicationQueue,
logger,
)
}
func OperatorHandlerProvider(
namespaceRegistry namespace.Registry,
nexusEndpointClient *NexusEndpointClient,
args := NewOperatorHandlerImplArgs{
configuration,
logger,
sdkClientFactory,
metricsHandler,
visibilityMgr,
saManager,
healthServer,
historyClient,
clusterMetadataManager,
clusterMetadata,
clientFactory,
namespaceRegistry,
nexusEndpointClient,
}
return NewOperatorHandlerImpl(args)
}
// callbackValidatorProvider creates a callback Validator using the production dynamic config keys
// so that existing operator configurations (callback.allowedAddresses) are honored.
return callback.NewValidator(
callback.MaxPerExecution.Get(dc),
dynamicconfig.FrontendCallbackURLMaxLength.Get(dc),
dynamicconfig.FrontendCallbackHeaderMaxSize.Get(dc),
callback.AllowedAddresses.Get(dc),
)
}
func HandlerProvider(
registry *chasm.Registry,
frontendServiceResolver membership.ServiceResolver,
workerDeploymentReadRateLimiter := configs.NewGlobalNamespaceRateLimiter(
frontendServiceResolver,
serviceConfig.GlobalWorkerDeploymentReadRPS,
serviceConfig.GlobalWorkerDeploymentReadBurstRatio,
log.With(logger, tag.ComponentRPCHandler, tag.ScopeNamespace),
)
wfHandler := NewWorkflowHandler(
callbackValidator,
serviceConfig,
namespaceReplicationQueue,
visibilityMgr,
logger,
throttledLogger,
persistenceExecutionManager.GetName(),
clusterMetadataManager,
persistenceMetadataManager,
historyClient,
matchingClient,
workerDeploymentStoreClient,
schedulerClient,
archiverProvider,
payloadSerializer,
namespaceRegistry,
saMapperProvider,
saProvider,
saValidator,
clusterMetadata,
archivalMetadata,
healthServer,
timeSource,
membershipMonitor,
healthInterceptor,
scheduleSpecBuilder,
httpEnabled(cfg, serviceName),
activityHandler,
nexusOperationHandler,
registry,
workerDeploymentReadRateLimiter,
chasmworkflow.NewValidator(
chasmworkflow.NewConfig(dc),
saMapperProvider,
saValidator,
),
)
return wfHandler
}
func RegisterNexusOperationHTTPHandler(
h *NexusOperationHTTPHandler,
router *mux.Router,
h.RegisterRoutes(router)
}
func RegisterNexusCompletionHTTPHandler(
h *nexusCompletionHTTPHandler,
router *mux.Router,
h.RegisterRoutes(router)
}
func RegisterOpenAPIHTTPHandler(
logger log.Logger,
router *mux.Router,
h := NewOpenAPIHTTPHandler(
rateLimitInterceptor,
logger,
)
h.RegisterRoutes(router)
return h
}
// Instantiate a router to support additional route prefixes.
return mux.NewRouter().UseEncodedPath()
}
// If the service is not the frontend service, HTTP API is disabled
if serviceName != primitives.FrontendService && serviceName != primitives.InternalFrontendService {
return false
}
// If HTTP API port is 0, it is disabled
}
nexusEndpointManager persistence.NexusEndpointManager,
logger log.Logger,
clientConfig := newNexusEndpointClientConfig(dc)
return newNexusEndpointClient(
clientConfig,
namespaceRegistry,
matchingClient,
nexusEndpointManager,
logger,
)
}
lc.Append(fx.StartStopHook(svc.Start, svc.Stop))
}
)
var s ServerFx
s.app = fx.New(
topLevelModule,
fx.Supply(opts),
fx.Populate(&s.startupSynchronizationMode),
fx.Populate(&s.logger),
)
if err := s.app.Err(); err != nil {
return nil, err
}
}
so := newServerOptions(opts)
err := so.loadAndValidate()
if err != nil {
return serverOptionsProvider{}, err
}
// Logger
if logger == nil {
logger = log.NewZapLogger(log.BuildZapLogger(so.config.Log))
}
err = verifyPersistenceCompatibleVersion(persistenceConfig, so.persistenceServiceResolver, logger)
if err != nil {
return serverOptionsProvider{}, err
}
// ClientFactoryProvider
clientFactoryProvider := so.clientFactoryProvider
if clientFactoryProvider == nil {
clientFactoryProvider = client.NewFactoryProvider()
}
// MetricsHandler
if metricHandler == nil {
metricHandler, err = metrics.MetricsHandlerFromConfig(logger, so.config.Global.Metrics)
if err != nil {
return serverOptionsProvider{}, fmt.Errorf("unable to create metrics handler: %w", err)
}
// if injected, else a no-op provider that discards events. A deployment opts in by injecting a
// provider via WithCustomEventLoggerProvider.
if eventLoggerProvider == nil {
eventLoggerProvider = lognoop.NewLoggerProvider()
}
// DynamicConfigClient
if dcClient == nil {
dcConfig := so.config.DynamicConfigClient
if dcConfig != nil {
// TLSConfigProvider
if tlsConfigProvider == nil {
tlsConfigProvider, err = encryption.NewTLSConfigProviderFromConfig(so.config.Global.TLS, metricHandler, logger, nil)
if err != nil {
return serverOptionsProvider{}, err
}
// EsConfig / EsClient
var esClient esclient.Client
if persistenceConfig.SecondaryVisibilityConfigExist() &&
persistenceConfig.DataStores[persistenceConfig.SecondaryVisibilityStore].Elasticsearch != nil {
esConfig = persistenceConfig.DataStores[persistenceConfig.SecondaryVisibilityStore].Elasticsearch
esConfig.SetHttpClient(so.elasticsearchHttpClient)
}
persistenceConfig.DataStores[persistenceConfig.VisibilityStore].Elasticsearch != nil {
esConfig = persistenceConfig.DataStores[persistenceConfig.VisibilityStore].Elasticsearch
esConfig.SetHttpClient(so.elasticsearchHttpClient)
}
esHttpClient := so.elasticsearchHttpClient
if esHttpClient == nil {
// check that when static hosts are defined, they are defined for all required hosts
for _, service := range DefaultServices {
hosts := so.hostsByService[primitives.ServiceName(service)]
}
if so.config.Global.Authorization.RemoteClusterAuth.Require && so.tokenProvider == nil {
fx.go
return serverOptionsProvider{}, errors.New("global.authorization.remoteClusterAuth.require is true but no TokenProvider is configured: use WithTokenProvider")
}
// Coarse check: any remote-cluster TLS entry passes; per-hostname config is still validated
// lazily on first dial.
if so.tokenProvider != nil && so.tlsConfigProvider == nil && len(so.config.Global.TLS.RemoteClusters) == 0 {
fx.go
return serverOptionsProvider{}, errors.New("WithTokenProvider is set but no remote-cluster TLS is configured: supply global.tls.remoteClusters in config, or pass a provider via WithTLSConfigProvider")
}
ServerOptions: so,
StopChan: stopChan,
StartupSynchronizationMode: so.startupSynchronizationMode,
Config: so.config,
PProfConfig: &so.config.Global.PProf,
LogConfig: so.config.Log,
ServiceNames: so.serviceNames,
ServiceHosts: so.hostsByService,
NamespaceLogger: so.namespaceLogger,
ServiceResolver: so.persistenceServiceResolver,
CustomDataStoreFactory: so.customDataStoreFactory,
CustomVisibilityStore: so.customVisibilityStoreFactory,
CustomHistoryArchiverFactory: so.customHistoryArchiverFactory,
CustomVisibilityArchiverFactory: so.customVisibilityArchiverFactory,
SearchAttributesMapper: so.searchAttributesMapper,
CustomFrontendInterceptors: so.customFrontendInterceptors,
Authorizer: so.authorizer,
ClaimMapper: so.claimMapper,
AudienceGetter: so.audienceGetter,
TokenProvider: so.tokenProvider,
Logger: logger,
ClientFactoryProvider: clientFactoryProvider,
DynamicConfigClient: dcClient,
TLSConfigProvider: tlsConfigProvider,
EsClient: esClient,
MetricsHandler: metricHandler,
EventLoggerProvider: eventLoggerProvider,
}, nil
}
// Start temporal server.
// This function should be called only once, Server doesn't support multiple restarts.
err := s.app.Start(context.Background())
if err != nil {
return err
}
// If s.so.interruptCh is nil this will wait forever.
interruptSignal := <-s.startupSynchronizationMode.interruptCh
}
}
// Stop stops the server.
return s.app.Stop(context.Background())
}
stopCtx, cancelFunc := context.WithTimeout(ctx, serviceStopTimeout)
defer cancelFunc()
err := svc.app.Stop(stopCtx)
if err != nil {
svc.logger.Error("Failed to stop service", tag.Service(svc.serviceName), tag.Error(err))
}
// into fx providers here. Essentially, we want an `fx.In` object in the server graph, and an `fx.Out` object in the
// service graphs. This is a workaround to achieve something similar.
func (params ServiceProviderParamsCommon) GetCommonServiceOptions(serviceName primitives.ServiceName) fx.Option {
fx.go
membershipModule := ringpop.MembershipModule
if len(params.StaticServiceHosts) > 0 {
membershipModule = static.MembershipModule(params.StaticServiceHosts)
}
fx.Supply(
serviceName,
params.PersistenceConfig,
params.ClusterMetadata,
params.Cfg,
params.SpanExporters,
),
fx.Provide(
resource.DefaultSnTaggedLoggerProvider,
params.PersistenceFactoryProvider,
func() persistenceClient.AbstractDataStoreFactory {
return params.DataStoreFactory
},
func() visibility.VisibilityStoreFactory {
return params.VisibilityStoreFactory
},
func() provider.CustomHistoryArchiverFactory {
return params.CustomHistoryArchiverFactory
},
func() provider.CustomVisibilityArchiverFactory {
return params.CustomVisibilityArchiverFactory
},
func() client.FactoryProvider {
return params.ClientFactoryProvider
},
func() authorization.JWTAudienceMapper {
return params.AudienceGetter
},
func() resolver.ServiceResolver {
return params.PersistenceServiceResolver
},
func() searchattribute.Mapper {
return params.SearchAttributesMapper
},
func() authorization.Authorizer {
return params.Authorizer
},
func() authorization.ClaimMapper {
return params.ClaimMapper
},
return params.TokenProvider
},
func() encryption.TLSConfigProvider {
return params.TlsConfigProvider
},
func() dynamicconfig.Client {
return params.DynamicConfigClient
},
func() log.Logger {
return params.Logger
},
func() metrics.Handler {
return params.MetricsHandler.WithTags(metrics.ServiceNameTag(serviceName))
},
func() otellog.Logger {
return wideevents.NewLogger(params.EventLoggerProvider, string(serviceName))
},
func() esclient.Client {
return params.EsClient
},
return params.NamespaceLogger
},
func() tasks.TaskCategoryRegistry {
return params.TaskCategoryRegistry
},
),
ServiceTracingModule,
// registry in the server graph, and then propagate it to the service graphs. Otherwise, it would be isolated to the
// history service's graph.
func TaskCategoryRegistryProvider(archivalMetadata archiver.ArchivalMetadata) tasks.TaskCategoryRegistry {
fx.go
registry := tasks.NewDefaultTaskCategoryRegistry()
if archivalMetadata.GetHistoryConfig().StaticClusterState() == archiver.ArchivalEnabled ||
archivalMetadata.GetVisibilityConfig().StaticClusterState() == archiver.ArchivalEnabled {
registry.AddCategory(tasks.CategoryArchival)
}
}
func NewService(app *fx.App, serviceName primitives.ServiceName, logger log.Logger) ServicesGroupOut {
fx.go
return ServicesGroupOut{
Services: &ServicesMetadata{
app: app,
serviceName: serviceName,
logger: logger,
},
}
}
func HistoryServiceProvider(
params ServiceProviderParamsCommon,
serviceName := primitives.HistoryService
if _, ok := params.ServiceNames[serviceName]; !ok {
params.Logger.Info("Service is not requested, skipping initialization.", tag.Service(serviceName))
return ServicesGroupOut{}, nil
}
params.GetCommonServiceOptions(serviceName),
history.QueueModule,
history.Module,
replication.Module,
)
return NewService(app, serviceName, params.Logger), app.Err()
}
func MatchingServiceProvider(
params ServiceProviderParamsCommon,
serviceName := primitives.MatchingService
if _, ok := params.ServiceNames[serviceName]; !ok {
params.Logger.Info("Service is not requested, skipping initialization.", tag.Service(serviceName))
return ServicesGroupOut{}, nil
}
params.GetCommonServiceOptions(serviceName),
matching.Module,
)
return NewService(app, serviceName, params.Logger), app.Err()
}
func FrontendServiceProvider(
params ServiceProviderParamsCommon,
return genericFrontendServiceProvider(params, primitives.FrontendService)
}
func InternalFrontendServiceProvider(
params ServiceProviderParamsCommon,
return genericFrontendServiceProvider(params, primitives.InternalFrontendService)
}
func genericFrontendServiceProvider(
params ServiceProviderParamsCommon,
serviceName primitives.ServiceName,
if _, ok := params.ServiceNames[serviceName]; !ok {
params.Logger.Info("Service is not requested, skipping initialization.", tag.Service(serviceName))
return ServicesGroupOut{}, nil
}
params.GetCommonServiceOptions(serviceName),
fx.Supply(params.CustomFrontendInterceptors),
fx.Supply([]grpc.StreamServerInterceptor{}),
fx.Decorate(func() authorization.ClaimMapper {
switch serviceName {
case primitives.FrontendService:
return params.ClaimMapper
case primitives.InternalFrontendService:
return authorization.NewInternalClaimMapper()
}
}),
// Use "frontend" for logs even if serviceName is "internal-frontend", but add an
// extra tag to differentiate.
tags := []tag.Tag{tag.Service(primitives.FrontendService)}
if serviceName == primitives.InternalFrontendService {
tags = append(tags, tag.Bool("internal-frontend", true))
}
}),
frontend.Module,
)
}
func WorkerServiceProvider(
params ServiceProviderParamsCommon,
serviceName := primitives.WorkerService
if _, ok := params.ServiceNames[serviceName]; !ok {
params.Logger.Info("Service is not requested, skipping initialization.", tag.Service(serviceName))
return ServicesGroupOut{}, nil
}
params.GetCommonServiceOptions(serviceName),
worker.Module,
)
return NewService(app, serviceName, params.Logger), app.Err()
}
metricsHandler metrics.Handler,
serializer serialization.Serializer,
ctx := context.TODO()
logger = log.With(logger, tag.ComponentMetadataInitializer)
metricsHandler = metricsHandler.WithTags(metrics.ServiceNameTag(primitives.ServerService))
clusterName := persistenceClient.ClusterName(svc.ClusterMetadata.CurrentClusterName)
dataStoreFactory := persistenceClient.DataStoreFactoryProvider(
clusterName,
persistenceServiceResolver,
&svc.Persistence,
customDataStoreFactory,
logger,
metricsHandler,
telemetry.NoopTracerProvider,
serializer,
)
factory := persistenceFactoryProvider(persistenceClient.NewFactoryParams{
DataStoreFactory: dataStoreFactory,
Cfg: &svc.Persistence,
PersistenceMaxQPS: nil,
PersistenceNamespaceMaxQPS: nil,
ClusterName: persistenceClient.ClusterName(svc.ClusterMetadata.CurrentClusterName),
MetricsHandler: metricsHandler,
Logger: logger,
Serializer: serializer,
})
defer factory.Close()
clusterMetadataManager, err := factory.NewClusterMetadataManager()
if err != nil {
return svc.ClusterMetadata, svc.Persistence, fmt.Errorf("error initializing cluster metadata manager: %w", err)
}
visCSAOverride := map[enumspb.IndexedValueType]int{}
for tpName, value := range svc.Visibility.PersistenceCustomSearchAttributes {
saType, ok := enumspb.IndexedValueType_shorthandValue[tpName]
if !ok {
}
svc.Persistence.GetVisibilityStoreConfig(),
svc.Persistence.GetSecondaryVisibilityStoreConfig(),
}
indexSearchAttributes := make(map[string]*persistencespb.IndexSearchAttributes)
for _, ds := range visDataStores {
indexSearchAttributes[ds.GetIndexName()] = sadefs.GetDBIndexSearchAttributes(visCSAOverride)
}
if len(clusterMetadata.ClusterInformation) > 1 {
logger.Warn(
"All remote cluster settings under ClusterMetadata.ClusterInformation config will be ignored. "+
tag.Key("clusterInformation"))
}
if _, ok := clusterMetadata.ClusterInformation[clusterMetadata.CurrentClusterName]; !ok {
fx.go
logger.Error("Current cluster setting is missing under clusterMetadata.ClusterInformation",
tag.ClusterName(clusterMetadata.CurrentClusterName))
return svc.ClusterMetadata, svc.Persistence, missingCurrentClusterMetadataErr
}
resp, err := clusterMetadataManager.GetClusterMetadata(
ctx,
&persistence.GetClusterMetadataRequest{ClusterName: clusterMetadata.CurrentClusterName},
)
switch err.(type) {
case nil:
// Update current record
logger,
)
// Initialize current cluster record
if initErr := initCurrentClusterMetadataRecord(
ctx,
clusterMetadataManager,
svc,
indexSearchAttributes,
logger,
); initErr != nil {
return svc.ClusterMetadata, svc.Persistence, initErr
}
}
err = clusterLoader.LoadAndMergeWithStaticConfig(ctx, svc)
if err != nil {
return svc.ClusterMetadata, svc.Persistence, fmt.Errorf("error while loading metadata from cluster: %w", err)
}
}
initialIndexSearchAttributes map[string]*persistencespb.IndexSearchAttributes,
logger log.Logger,
var clusterId string
currentClusterName := svc.ClusterMetadata.CurrentClusterName
currentClusterInfo := svc.ClusterMetadata.ClusterInformation[currentClusterName]
if uuid.Validate(currentClusterInfo.ClusterID) != nil {
if currentClusterInfo.ClusterID != "" {
logger.Warn("Cluster Id in Cluster Metadata config is not a valid uuid. Generating a new Cluster Id")
}
} else {
clusterId = currentClusterInfo.ClusterID
}
ctx,
&persistence.SaveClusterMetadataRequest{
ClusterMetadata: &persistencespb.ClusterMetadata{
HistoryShardCount: svc.Persistence.NumHistoryShards,
ClusterName: currentClusterName,
ClusterId: clusterId,
ClusterAddress: currentClusterInfo.RPCAddress,
HttpAddress: currentClusterInfo.HTTPAddress,
FailoverVersionIncrement: svc.ClusterMetadata.FailoverVersionIncrement,
InitialFailoverVersion: currentClusterInfo.InitialFailoverVersion,
IsGlobalNamespaceEnabled: svc.ClusterMetadata.EnableGlobalNamespace,
IsConnectionEnabled: currentClusterInfo.Enabled,
UseClusterIdMembership: true, // Enable this for new cluster after 1.19. This is to prevent two clusters join into one ring.
IndexSearchAttributes: initialIndexSearchAttributes,
Tags: svc.ClusterMetadata.Tags,
},
})
if err != nil {
logger.Warn("Failed to save cluster metadata.", tag.Error(err), tag.ClusterName(currentClusterName))
return err
}
logger.Error("Failed to apply cluster metadata.", tag.ClusterName(currentClusterName))
return clusterMetadataInitErr
}
}
}
return persistenceClient.FactoryProvider
}
func ServerLifetimeHooks(
lc fx.Lifecycle,
svr *ServerImpl,
lc.Append(fx.StartStopHook(svr.Start, svr.Stop))
}
func verifyPersistenceCompatibleVersion(
persistenceServiceResolver resolver.ServiceResolver,
logger log.Logger,
// cassandra schema version validation
if err := cassandra.VerifyCompatibleVersion(cfg, persistenceServiceResolver, logger); err != nil {
return fmt.Errorf("cassandra schema version compatibility check failed: %w", err)
}
// sql schema version validation
if err := sql.VerifyCompatibleVersion(cfg, persistenceServiceResolver, logger); err != nil {
fx.go
return fmt.Errorf("sql schema version compatibility check failed: %w", err)
}
}
// - []go.opentelemetry.io/otel/sdk/trace.SpanExporter
var TraceExportModule = fx.Options(
var tracingReady atomic.Bool
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
if tracingReady.Load() { // ignore errors during startup
inputs.Logger.Warn("OTEL error", tag.Error(err), tag.ServiceErrorType(err))
// (1) Exporters from config.
if inputs.Config != nil {
var err error
exportersByType, err = inputs.Config.ExporterConfig.SpanExporters()
if err != nil {
return nil, err
}
// (2) Exporters from env variables.
if err != nil {
return nil, err
}
// (3) Exporters from code (ie from testing).
// Merge exporters.
maps.Copy(exportersByType, exportersByTypeFromEnv) // env overrides config
maps.Copy(exportersByType, customExportersByType) // custom overrides all
exporters := expmaps.Values(exportersByType)
// Configure exporters' lifecycle hooks.
inputs.Lifecycyle.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
err = startAll(exporters)(ctx)
tracingReady.Store(true)
return err
},
OnStop: shutdownAll(exporters),
})
}),
)
fx.Provide(
fx.Annotate(
func(exps []otelsdktrace.SpanExporter, opts []otelsdktrace.BatchSpanProcessorOption) []otelsdktrace.SpanProcessor {
fx.go
sps := make([]otelsdktrace.SpanProcessor, 0, len(exps))
for _, exp := range exps {
sps = append(sps, otelsdktrace.NewBatchSpanProcessor(exp, opts...))
}
},
fx.ParamTags(`optional:"true"`, ``),
fx.Provide(
fx.Annotate(
func(rsn primitives.ServiceName, rsi resource.InstanceID) (*otelresource.Resource, error) {
fx.go
attrs := []attribute.KeyValue{
semconv.ServiceNameKey.String(telemetry.ResourceServiceName(rsn, os.LookupEnv)),
semconv.ServiceVersionKey.String(headers.ServerVersion),
}
if rsi != "" {
attrs = append(attrs, semconv.ServiceInstanceIDKey.String(string(rsi)))
}
otelresource.WithProcess(),
otelresource.WithOS(),
otelresource.WithHost(),
otelresource.WithContainer(),
otelresource.WithAttributes(attrs...),
)
},
fx.ParamTags(``, `optional:"true"`),
),
),
fx.Provide(func(lc fx.Lifecycle, r *otelresource.Resource, sps []otelsdktrace.SpanProcessor) trace.TracerProvider {
fx.go
if len(sps) == 0 {
}
opts := make([]otelsdktrace.TracerProviderOption, 0, len(sps)+1)
opts = append(opts, otelsdktrace.WithResource(r))
}),
// Haven't had use for baggage propagation yet
fx.Provide(func() propagation.TextMapPropagator { return propagation.TraceContext{} }),
fx.go
fx.Provide(telemetry.NewServerStatsHandler),
fx.Provide(telemetry.NewClientStatsHandler),
)
type starter interface{ Start(context.Context) error }
return func(ctx context.Context) error {
for _, e := range exporters {
if starter, ok := e.(starter); ok {
err := starter.Start(ctx)
}
}
}
}
func shutdownAll(exporters []otelsdktrace.SpanExporter) func(ctx context.Context) error {
fx.go
return func(ctx context.Context) error {
defer cancel()
for _, e := range exporters {
err := e.Shutdown(shutdownCtx)
if errors.Is(err, context.DeadlineExceeded) {
}
switch e := e.(type) {
case *fxevent.OnStartExecuting:
l.logger.Debug("OnStart hook executing",
tag.ComponentFX,
tag.String("callee", e.FunctionName),
tag.String("caller", e.CallerName),
)
case *fxevent.OnStartExecuted:
if e.Err != nil {
l.logger.Error("OnStart hook failed",
tag.ComponentFX,
tag.Error(e.Err),
)
l.logger.Debug("OnStart hook executed",
tag.ComponentFX,
tag.String("callee", e.FunctionName),
tag.String("caller", e.CallerName),
tag.Stringer("runtime", e.Runtime),
)
}
l.logger.Debug("OnStop hook executing",
tag.ComponentFX,
tag.String("callee", e.FunctionName),
tag.String("caller", e.CallerName),
)
case *fxevent.OnStopExecuted:
if e.Err != nil {
l.logger.Error("OnStop hook failed",
tag.ComponentFX,
tag.Error(e.Err),
)
l.logger.Debug("OnStop hook executed",
tag.ComponentFX,
tag.String("callee", e.FunctionName),
tag.String("caller", e.CallerName),
tag.Stringer("runtime", e.Runtime),
)
}
if e.Err != nil {
l.logger.Error("supplied",
tag.ComponentFX,
tag.Error(e.Err))
}
if e.Err != nil {
l.logger.Error("error encountered while applying options",
tag.ComponentFX,
tag.Error(e.Err))
}
if e.Err != nil {
l.logger.Error("error encountered while applying options",
tag.ComponentFX,
tag.Error(e.Err))
}
if e.Err != nil {
l.logger.Error("error returned",
tag.ComponentFX,
)
}
// Do not log stack as it will make logs hard to read.
l.logger.Debug("invoking",
tag.ComponentFX,
tag.String("function", e.FunctionName),
tag.String("module", e.ModuleName),
)
case *fxevent.Invoked:
if e.Err != nil {
l.logger.Error("invoke failed",
tag.ComponentFX,
tag.ComponentFX,
tag.Stringer("signal", e.Signal))
if e.Err != nil {
l.logger.Error("stop failed", tag.ComponentFX, tag.Error(e.Err))
}
l.logger.Error("rollback failed", tag.ComponentFX, tag.Error(e.Err))
}
if e.Err != nil {
l.logger.Error("start failed", tag.ComponentFX, tag.Error(e.Err))
l.logger.Debug("started", tag.ComponentFX)
}
case *fxevent.LoggerInitialized:
if e.Err != nil {
l.logger.Error("custom logger initialization failed", tag.ComponentFX, tag.Error(e.Err))
l.logger.Debug("initialized custom fxevent.Logger",
tag.ComponentFX,
tag.String("function", e.ConstructorName))
}
case *fxevent.BeforeRun:
l.logger.Debug("before run",
tag.ComponentFX,
tag.String("name", e.Name),
tag.String("kind", e.Kind),
tag.String("module", e.ModuleName),
)
default:
l.logger.Warn("unknown fx log type, update fxLogAdapter",
dc *dynamicconfig.Collection,
numberOfShards int32,
cfg := &Config{
NumberOfShards: numberOfShards,
EnableReplicationStream: dynamicconfig.EnableReplicationStream.Get(dc),
EmitReplicationLifecycleEvents: dynamicconfig.EmitReplicationLifecycleEvents.Get(dc),
EnableCloseInboundReplicationStreamOnShutdown: dynamicconfig.EnableCloseInboundReplicationStreamOnShutdown.Get(dc),
EnableSeparateReplicationEnableFlag: dynamicconfig.EnableSeparateReplicationEnableFlag.Get(dc),
HistoryReplicationDLQV2: dynamicconfig.EnableHistoryReplicationDLQV2.Get(dc),
RPS: dynamicconfig.HistoryRPS.Get(dc),
NamespaceRPS: dynamicconfig.HistoryNamespaceRPS.Get(dc),
OperatorRPSRatio: dynamicconfig.OperatorRPSRatio.Get(dc),
MaxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
PersistenceMaxQPS: dynamicconfig.HistoryPersistenceMaxQPS.Get(dc),
PersistenceGlobalMaxQPS: dynamicconfig.HistoryPersistenceGlobalMaxQPS.Get(dc),
PersistenceNamespaceMaxQPS: dynamicconfig.HistoryPersistenceNamespaceMaxQPS.Get(dc),
PersistenceGlobalNamespaceMaxQPS: dynamicconfig.HistoryPersistenceGlobalNamespaceMaxQPS.Get(dc),
PersistencePerShardNamespaceMaxQPS: dynamicconfig.HistoryPersistencePerShardNamespaceMaxQPS.Get(dc),
PersistenceDynamicRateLimitingParams: dynamicconfig.HistoryPersistenceDynamicRateLimitingParams.Get(dc),
PersistenceQPSBurstRatio: dynamicconfig.PersistenceQPSBurstRatio.Get(dc),
AlignMembershipChange: dynamicconfig.HistoryAlignMembershipChange.Get(dc),
ShutdownDrainDuration: dynamicconfig.HistoryShutdownDrainDuration.Get(dc),
StartupMembershipJoinDelay: dynamicconfig.HistoryStartupMembershipJoinDelay.Get(dc),
AllowResetWithPendingChildren: dynamicconfig.AllowResetWithPendingChildren.Get(dc),
MaxAutoResetPoints: dynamicconfig.HistoryMaxAutoResetPoints.Get(dc),
DefaultWorkflowTaskTimeout: dynamicconfig.DefaultWorkflowTaskTimeout.Get(dc),
MaxLocalParentWorkflowVerificationDuration: dynamicconfig.MaxLocalParentWorkflowVerificationDuration.Get(dc),
VisibilityPersistenceMaxReadQPS: dynamicconfig.VisibilityPersistenceMaxReadQPS.Get(dc),
VisibilityPersistenceMaxWriteQPS: dynamicconfig.VisibilityPersistenceMaxWriteQPS.Get(dc),
VisibilityPersistenceSlowQueryThreshold: dynamicconfig.VisibilityPersistenceSlowQueryThreshold.Get(dc),
EnableReadFromSecondaryVisibility: dynamicconfig.EnableReadFromSecondaryVisibility.Get(dc),
VisibilityEnableShadowReadMode: dynamicconfig.VisibilityEnableShadowReadMode.Get(dc),
SecondaryVisibilityWritingMode: dynamicconfig.SecondaryVisibilityWritingMode.Get(dc),
VisibilityDisableOrderByClause: dynamicconfig.VisibilityDisableOrderByClause.Get(dc),
VisibilityEnableManualPagination: dynamicconfig.VisibilityEnableManualPagination.Get(dc),
VisibilityEnableUnifiedQueryConverter: dynamicconfig.VisibilityEnableUnifiedQueryConverter.Get(dc),
VisibilityAllowList: dynamicconfig.VisibilityAllowList.Get(dc),
SuppressErrorSetSystemSearchAttribute: dynamicconfig.SuppressErrorSetSystemSearchAttribute.Get(dc),
EmitShardLagLog: dynamicconfig.EmitShardLagLog.Get(dc),
EnableDataLossMetrics: dynamicconfig.EnableDataLossMetrics.Get(dc),
// HistoryCacheLimitSizeBased should not change during runtime.
HistoryCacheLimitSizeBased: dynamicconfig.HistoryCacheSizeBasedLimit.Get(dc)(),
HistoryHostLevelCacheMaxSize: dynamicconfig.HistoryCacheHostLevelMaxSize.Get(dc),
HistoryHostLevelCacheMaxSizeBytes: dynamicconfig.HistoryCacheHostLevelMaxSizeBytes.Get(dc),
HistoryCacheTTL: dynamicconfig.HistoryCacheTTL.Get(dc),
HistoryCacheNonUserContextLockTimeout: dynamicconfig.HistoryCacheNonUserContextLockTimeout.Get(dc),
HistoryCacheBackgroundEvict: dynamicconfig.HistoryCacheBackgroundEvict.Get(dc),
EnableWorkflowExecutionTimeoutTimer: dynamicconfig.EnableWorkflowExecutionTimeoutTimer.Get(dc),
EnableUpdateWorkflowModeIgnoreCurrent: dynamicconfig.EnableUpdateWorkflowModeIgnoreCurrent.Get(dc),
EnableTransitionHistory: dynamicconfig.EnableTransitionHistory.Get(dc),
MaxCallbacksPerWorkflow: dynamicconfig.MaxCallbacksPerWorkflow.Get(dc),
MaxCallbacksPerExecution: callback.MaxPerExecution.Get(dc),
MaxCallbacksPerUpdateID: dynamicconfig.MaxCallbacksPerUpdateID.Get(dc),
EnableChasm: dynamicconfig.EnableChasm.Get(dc),
EnableChasmNexusWorkflowOperations: nexusoperation.EnableChasmWorkflowOperations.Get(dc),
ChasmMaxInMemoryPureTasks: dynamicconfig.ChasmMaxInMemoryPureTasks.Get(dc),
EnableCHASMSchedulerCreation: dynamicconfig.EnableCHASMSchedulerCreation.Get(dc),
EnableCHASMSchedulerMigration: dynamicconfig.EnableCHASMSchedulerMigration.Get(dc),
EnableCHASMCallbacks: dynamicconfig.EnableCHASMCallbacks.Get(dc),
EnableCHASMSignalBacklinks: dynamicconfig.EnableCHASMSignalBacklinks.Get(dc),
ExternalPayloadsEnabled: dynamicconfig.ExternalPayloadsEnabled.Get(dc),
EnableWorkflowUpdateCallbacks: dynamicconfig.EnableWorkflowUpdateCallbacks.Get(dc),
EventsShardLevelCacheMaxSizeBytes: dynamicconfig.EventsCacheMaxSizeBytes.Get(dc), // 512KB
EventsHostLevelCacheMaxSizeBytes: dynamicconfig.EventsHostLevelCacheMaxSizeBytes.Get(dc), // 256MB
EventsCacheTTL: dynamicconfig.EventsCacheTTL.Get(dc),
EnableHostLevelEventsCache: dynamicconfig.EnableHostLevelEventsCache.Get(dc),
RangeSizeBits: 20, // 20 bits for sequencer, 2^20 sequence number for any range
AcquireShardInterval: dynamicconfig.AcquireShardInterval.Get(dc),
AcquireShardConcurrency: dynamicconfig.AcquireShardConcurrency.Get(dc),
ShardIOConcurrency: dynamicconfig.ShardIOConcurrency.Get(dc),
ShardIOTimeout: dynamicconfig.ShardIOTimeout.Get(dc),
ShardLingerOwnershipCheckQPS: dynamicconfig.ShardLingerOwnershipCheckQPS.Get(dc),
ShardLingerTimeLimit: dynamicconfig.ShardLingerTimeLimit.Get(dc),
ShardFinalizerTimeout: dynamicconfig.ShardFinalizerTimeout.Get(dc),
HistoryClientOwnershipCachingEnabled: dynamicconfig.HistoryClientOwnershipCachingEnabled.Get(dc),
StandbyClusterDelay: dynamicconfig.StandbyClusterDelay.Get(dc),
StandbyTaskMissingEventsResendDelay: dynamicconfig.StandbyTaskMissingEventsResendDelay.Get(dc),
StandbyTaskMissingEventsDiscardDelay: dynamicconfig.StandbyTaskMissingEventsDiscardDelay.Get(dc),
ChasmStandbyTaskDiscardDelay: dynamicconfig.ChasmStandbyTaskDiscardDelay.Get(dc),
QueuePendingTaskCriticalCount: dynamicconfig.QueuePendingTaskCriticalCount.Get(dc),
QueueReaderStuckCriticalAttempts: dynamicconfig.QueueReaderStuckCriticalAttempts.Get(dc),
QueueCriticalSlicesCount: dynamicconfig.QueueCriticalSlicesCount.Get(dc),
QueuePendingTaskMaxCount: dynamicconfig.QueuePendingTaskMaxCount.Get(dc),
QueueMaxPredicateSize: dynamicconfig.QueueMaxPredicateSize.Get(dc),
QueueShrinkPredicateMaxPendingKeys: dynamicconfig.QueueShrinkPredicateMaxPendingKeys.Get(dc),
QueueMoveGroupTaskCountBase: dynamicconfig.QueueMoveGroupTaskCountBase.Get(dc),
QueueMoveGroupTaskCountMultiplier: dynamicconfig.QueueMoveGroupTaskCountMultiplier.Get(dc),
TaskDLQEnabled: dynamicconfig.HistoryTaskDLQEnabled.Get(dc),
TaskDLQUnexpectedErrorAttempts: dynamicconfig.HistoryTaskDLQUnexpectedErrorAttempts.Get(dc),
TaskDLQInternalErrors: dynamicconfig.HistoryTaskDLQInternalErrors.Get(dc),
TaskDLQErrorPattern: dynamicconfig.HistoryTaskDLQErrorPattern.Get(dc),
TaskSchedulerEnableRateLimiter: dynamicconfig.TaskSchedulerEnableRateLimiter.Get(dc),
TaskSchedulerEnableRateLimiterShadowMode: dynamicconfig.TaskSchedulerEnableRateLimiterShadowMode.Get(dc),
TaskSchedulerRateLimiterStartupDelay: dynamicconfig.TaskSchedulerRateLimiterStartupDelay.Get(dc),
TaskSchedulerGlobalMaxQPS: dynamicconfig.TaskSchedulerGlobalMaxQPS.Get(dc),
TaskSchedulerMaxQPS: dynamicconfig.TaskSchedulerMaxQPS.Get(dc),
TaskSchedulerNamespaceMaxQPS: dynamicconfig.TaskSchedulerNamespaceMaxQPS.Get(dc),
TaskSchedulerGlobalNamespaceMaxQPS: dynamicconfig.TaskSchedulerGlobalNamespaceMaxQPS.Get(dc),
TaskSchedulerInactiveChannelDeletionDelay: dynamicconfig.TaskSchedulerInactiveChannelDeletionDelay.Get(dc),
TaskSchedulerEnableExecutionQueueScheduler: dynamicconfig.TaskSchedulerEnableExecutionQueueScheduler.Get(dc),
TaskSchedulerExecutionQueueSchedulerMaxQueues: dynamicconfig.TaskSchedulerExecutionQueueSchedulerMaxQueues.Get(dc),
TaskSchedulerExecutionQueueSchedulerQueueTTL: dynamicconfig.TaskSchedulerExecutionQueueSchedulerQueueTTL.Get(dc),
TaskSchedulerExecutionQueueSchedulerQueueConcurrency: dynamicconfig.TaskSchedulerExecutionQueueSchedulerQueueConcurrency.Get(dc),
TimerTaskBatchSize: dynamicconfig.TimerTaskBatchSize.Get(dc),
TimerProcessorSchedulerWorkerCount: dynamicconfig.TimerProcessorSchedulerWorkerCount.Subscribe(dc),
TimerProcessorSchedulerActiveRoundRobinWeights: dynamicconfig.TimerProcessorSchedulerActiveRoundRobinWeights.WithDefault(ConvertWeightsToDynamicConfigValue(DefaultActiveTaskPriorityWeight)).Get(dc),
TimerProcessorSchedulerStandbyRoundRobinWeights: dynamicconfig.TimerProcessorSchedulerStandbyRoundRobinWeights.WithDefault(ConvertWeightsToDynamicConfigValue(DefaultStandbyTaskPriorityWeight)).Get(dc),
TimerProcessorUpdateAckInterval: dynamicconfig.TimerProcessorUpdateAckInterval.Get(dc),
TimerProcessorUpdateAckIntervalJitterCoefficient: dynamicconfig.TimerProcessorUpdateAckIntervalJitterCoefficient.Get(dc),
TimerProcessorMaxPollRPS: dynamicconfig.TimerProcessorMaxPollRPS.Get(dc),
TimerProcessorMaxPollHostRPS: dynamicconfig.TimerProcessorMaxPollHostRPS.Get(dc),
TimerProcessorMaxPollInterval: dynamicconfig.TimerProcessorMaxPollInterval.Get(dc),
TimerProcessorMaxPollIntervalJitterCoefficient: dynamicconfig.TimerProcessorMaxPollIntervalJitterCoefficient.Get(dc),
TimerProcessorPollBackoffInterval: dynamicconfig.TimerProcessorPollBackoffInterval.Get(dc),
TimerProcessorMaxTimeShift: dynamicconfig.TimerProcessorMaxTimeShift.Get(dc),
TransferQueueMaxReaderCount: dynamicconfig.TransferQueueMaxReaderCount.Get(dc),
RetentionTimerJitterDuration: dynamicconfig.RetentionTimerJitterDuration.Get(dc),
MemoryTimerProcessorSchedulerWorkerCount: dynamicconfig.MemoryTimerProcessorSchedulerWorkerCount.Subscribe(dc),
TransferTaskBatchSize: dynamicconfig.TransferTaskBatchSize.Get(dc),
TransferProcessorSchedulerWorkerCount: dynamicconfig.TransferProcessorSchedulerWorkerCount.Subscribe(dc),
TransferProcessorSchedulerActiveRoundRobinWeights: dynamicconfig.TransferProcessorSchedulerActiveRoundRobinWeights.WithDefault(ConvertWeightsToDynamicConfigValue(DefaultActiveTaskPriorityWeight)).Get(dc),
TransferProcessorSchedulerStandbyRoundRobinWeights: dynamicconfig.TransferProcessorSchedulerStandbyRoundRobinWeights.WithDefault(ConvertWeightsToDynamicConfigValue(DefaultStandbyTaskPriorityWeight)).Get(dc),
TransferProcessorMaxPollRPS: dynamicconfig.TransferProcessorMaxPollRPS.Get(dc),
TransferProcessorMaxPollHostRPS: dynamicconfig.TransferProcessorMaxPollHostRPS.Get(dc),
TransferProcessorMaxPollInterval: dynamicconfig.TransferProcessorMaxPollInterval.Get(dc),
TransferProcessorMaxPollIntervalJitterCoefficient: dynamicconfig.TransferProcessorMaxPollIntervalJitterCoefficient.Get(dc),
TransferProcessorUpdateAckInterval: dynamicconfig.TransferProcessorUpdateAckInterval.Get(dc),
TransferProcessorUpdateAckIntervalJitterCoefficient: dynamicconfig.TransferProcessorUpdateAckIntervalJitterCoefficient.Get(dc),
TransferProcessorPollBackoffInterval: dynamicconfig.TransferProcessorPollBackoffInterval.Get(dc),
TransferProcessorEnsureCloseBeforeDelete: dynamicconfig.TransferProcessorEnsureCloseBeforeDelete.Get(dc),
TimerQueueMaxReaderCount: dynamicconfig.TimerQueueMaxReaderCount.Get(dc),
OutboundTaskBatchSize: dynamicconfig.OutboundTaskBatchSize.Get(dc),
OutboundProcessorMaxPollRPS: dynamicconfig.OutboundProcessorMaxPollRPS.Get(dc),
OutboundProcessorMaxPollHostRPS: dynamicconfig.OutboundProcessorMaxPollHostRPS.Get(dc),
OutboundProcessorMaxPollInterval: dynamicconfig.OutboundProcessorMaxPollInterval.Get(dc),
OutboundProcessorMaxPollIntervalJitterCoefficient: dynamicconfig.OutboundProcessorMaxPollIntervalJitterCoefficient.Get(dc),
OutboundProcessorUpdateAckInterval: dynamicconfig.OutboundProcessorUpdateAckInterval.Get(dc),
OutboundProcessorUpdateAckIntervalJitterCoefficient: dynamicconfig.OutboundProcessorUpdateAckIntervalJitterCoefficient.Get(dc),
OutboundProcessorPollBackoffInterval: dynamicconfig.OutboundProcessorPollBackoffInterval.Get(dc),
OutboundQueuePendingTaskCriticalCount: dynamicconfig.OutboundQueuePendingTaskCriticalCount.Get(dc),
OutboundQueuePendingTaskMaxCount: dynamicconfig.OutboundQueuePendingTaskMaxCount.Get(dc),
OutboundQueueMaxPredicateSize: dynamicconfig.OutboundQueueMaxPredicateSize.Get(dc),
OutboundQueueMaxReaderCount: dynamicconfig.OutboundQueueMaxReaderCount.Get(dc),
OutboundQueueGroupLimiterBufferSize: dynamicconfig.OutboundQueueGroupLimiterBufferSize.Get(dc),
OutboundQueueGroupLimiterConcurrency: dynamicconfig.OutboundQueueGroupLimiterConcurrency.Get(dc),
OutboundQueueHostSchedulerMaxTaskRPS: dynamicconfig.OutboundQueueHostSchedulerMaxTaskRPS.Get(dc),
OutboundQueueCircuitBreakerSettings: dynamicconfig.OutboundQueueCircuitBreakerSettings.Subscribe(dc),
OutboundStandbyTaskMissingEventsDestinationDownErr: dynamicconfig.OutboundStandbyTaskMissingEventsDestinationDownErr.Get(dc),
OutboundStandbyTaskMissingEventsDiscardDelay: dynamicconfig.OutboundStandbyTaskMissingEventsDiscardDelay.Get(dc),
ReplicatorProcessorMaxPollInterval: dynamicconfig.ReplicatorProcessorMaxPollInterval.Get(dc),
ReplicatorProcessorMaxPollIntervalJitterCoefficient: dynamicconfig.ReplicatorProcessorMaxPollIntervalJitterCoefficient.Get(dc),
ReplicatorProcessorFetchTasksBatchSize: dynamicconfig.ReplicatorTaskBatchSize.Get(dc),
ReplicatorProcessorMaxSkipTaskCount: dynamicconfig.ReplicatorMaxSkipTaskCount.Get(dc),
ReplicationTaskProcessorHostQPS: dynamicconfig.ReplicationTaskProcessorHostQPS.Get(dc),
ReplicationTaskProcessorShardQPS: dynamicconfig.ReplicationTaskProcessorShardQPS.Get(dc),
ReplicationEnableDLQMetrics: dynamicconfig.ReplicationEnableDLQMetrics.Get(dc),
ReplicationEnableUpdateWithNewTaskMerge: dynamicconfig.ReplicationEnableUpdateWithNewTaskMerge.Get(dc),
ReplicationStreamSyncStatusDuration: dynamicconfig.ReplicationStreamSyncStatusDuration.Get(dc),
ReplicationProcessorSchedulerQueueSize: dynamicconfig.ReplicationProcessorSchedulerQueueSize.Get(dc),
ReplicationProcessorSchedulerWorkerCount: dynamicconfig.ReplicationProcessorSchedulerWorkerCount.Subscribe(dc),
ReplicationLowPriorityProcessorSchedulerWorkerCount: dynamicconfig.ReplicationLowPriorityProcessorSchedulerWorkerCount.Subscribe(dc),
ReplicationLowPriorityTaskParallelism: dynamicconfig.ReplicationLowPriorityTaskParallelism.Get(dc),
EnableReplicationTaskBatching: dynamicconfig.EnableReplicationTaskBatching.Get(dc),
EnableReplicationTaskTieredProcessing: dynamicconfig.EnableReplicationTaskTieredProcessing.Get(dc),
ReplicationStreamSenderHighPriorityQPS: dynamicconfig.ReplicationStreamSenderHighPriorityQPS.Get(dc),
ReplicationStreamSenderLowPriorityQPS: dynamicconfig.ReplicationStreamSenderLowPriorityQPS.Get(dc),
ReplicationStreamEventLoopRetryMaxAttempts: dynamicconfig.ReplicationStreamEventLoopRetryMaxAttempts.Get(dc),
ReplicationReceiverMaxOutstandingTaskCount: dynamicconfig.ReplicationReceiverMaxOutstandingTaskCount.Get(dc),
ReplicationReceiverSlowSubmissionLatencyThreshold: dynamicconfig.ReplicationReceiverSlowSubmissionLatencyThreshold.Get(dc),
ReplicationReceiverSlowSubmissionWindow: dynamicconfig.ReplicationReceiverSlowSubmissionWindow.Get(dc),
EnableReplicationReceiverSlowSubmissionFlowControl: dynamicconfig.EnableReplicationReceiverSlowSubmissionFlowControl.Get(dc),
ReplicationResendMaxBatchCount: dynamicconfig.ReplicationResendMaxBatchCount.Get(dc),
ReplicationProgressCacheMaxSize: dynamicconfig.ReplicationProgressCacheMaxSize.Get(dc),
ReplicationProgressCacheTTL: dynamicconfig.ReplicationProgressCacheTTL.Get(dc),
ReplicationEnableRateLimit: dynamicconfig.ReplicationEnableRateLimit.Get(dc),
ReplicationEnableRateLimitShadowMode: dynamicconfig.ReplicationEnableRateLimitShadowMode.Get(dc),
ReplicationStreamSendEmptyTaskDuration: dynamicconfig.ReplicationStreamSendEmptyTaskDuration.Get(dc),
ReplicationStreamReceiverLivenessMultiplier: dynamicconfig.ReplicationStreamReceiverLivenessMultiplier.Get(dc),
ReplicationStreamSenderLivenessMultiplier: dynamicconfig.ReplicationStreamSenderLivenessMultiplier.Get(dc),
EnableHistoryReplicationRateLimiter: dynamicconfig.EnableHistoryReplicationRateLimiter.Get(dc),
MaximumBufferedEventsBatch: dynamicconfig.MaximumBufferedEventsBatch.Get(dc),
MaximumBufferedEventsSizeInBytes: dynamicconfig.MaximumBufferedEventsSizeInBytes.Get(dc),
MaximumSignalsPerExecution: dynamicconfig.MaximumSignalsPerExecution.Get(dc),
MaximumEventBatchSizeInBytes: dynamicconfig.MaximumEventBatchSizeInBytes.Get(dc),
ShardUpdateMinInterval: dynamicconfig.ShardUpdateMinInterval.Get(dc),
ShardFirstUpdateInterval: dynamicconfig.ShardFirstUpdateInterval.Get(dc),
ShardUpdateMinTasksCompleted: dynamicconfig.ShardUpdateMinTasksCompleted.Get(dc),
ShardSyncMinInterval: dynamicconfig.ShardSyncMinInterval.Get(dc),
ShardSyncTimerJitterCoefficient: dynamicconfig.TransferProcessorMaxPollIntervalJitterCoefficient.Get(dc),
// history client: client/history/client.go set the client timeout 30s
// TODO: Return this value to the client: go.temporal.io/server/issues/294
LongPollExpirationInterval: dynamicconfig.HistoryLongPollExpirationInterval.Get(dc),
EnableParentClosePolicy: dynamicconfig.EnableParentClosePolicy.Get(dc),
NumParentClosePolicySystemWorkflows: dynamicconfig.NumParentClosePolicySystemWorkflows.Get(dc),
EnableParentClosePolicyWorker: dynamicconfig.EnableParentClosePolicyWorker.Get(dc),
ParentClosePolicyThreshold: dynamicconfig.ParentClosePolicyThreshold.Get(dc),
BlobSizeLimitError: dynamicconfig.BlobSizeLimitError.Get(dc),
BlobSizeLimitWarn: dynamicconfig.BlobSizeLimitWarn.Get(dc),
MemoSizeLimitError: dynamicconfig.MemoSizeLimitError.Get(dc),
MemoSizeLimitWarn: dynamicconfig.MemoSizeLimitWarn.Get(dc),
NumPendingChildExecutionsLimit: dynamicconfig.NumPendingChildExecutionsLimitError.Get(dc),
NumPendingActivitiesLimit: dynamicconfig.NumPendingActivitiesLimitError.Get(dc),
NumPendingSignalsLimit: dynamicconfig.NumPendingSignalsLimitError.Get(dc),
NumPendingCancelsRequestLimit: dynamicconfig.NumPendingCancelRequestsLimitError.Get(dc),
HistorySizeLimitError: dynamicconfig.HistorySizeLimitError.Get(dc),
HistorySizeLimitWarn: dynamicconfig.HistorySizeLimitWarn.Get(dc),
HistorySizeSuggestContinueAsNew: dynamicconfig.HistorySizeSuggestContinueAsNew.Get(dc),
HistoryCountLimitError: dynamicconfig.HistoryCountLimitError.Get(dc),
HistoryCountLimitWarn: dynamicconfig.HistoryCountLimitWarn.Get(dc),
HistoryCountSuggestContinueAsNew: dynamicconfig.HistoryCountSuggestContinueAsNew.Get(dc),
HistoryMaxPageSize: dynamicconfig.HistoryMaxPageSize.Get(dc),
MutableStateActivityFailureSizeLimitError: dynamicconfig.MutableStateActivityFailureSizeLimitError.Get(dc),
MutableStateActivityFailureSizeLimitWarn: dynamicconfig.MutableStateActivityFailureSizeLimitWarn.Get(dc),
MutableStateSizeLimitError: dynamicconfig.MutableStateSizeLimitError.Get(dc),
MutableStateSizeLimitWarn: dynamicconfig.MutableStateSizeLimitWarn.Get(dc),
MutableStateTombstoneCountLimit: dynamicconfig.MutableStateTombstoneCountLimit.Get(dc),
ThrottledLogRPS: dynamicconfig.HistoryThrottledLogRPS.Get(dc),
EnableStickyQuery: dynamicconfig.EnableStickyQuery.Get(dc),
DefaultActivityRetryPolicy: dynamicconfig.DefaultActivityRetryPolicy.Get(dc),
DefaultWorkflowRetryPolicy: dynamicconfig.DefaultWorkflowRetryPolicy.Get(dc),
WorkflowTaskHeartbeatTimeout: dynamicconfig.WorkflowTaskHeartbeatTimeout.Get(dc),
WorkflowTaskCriticalAttempts: dynamicconfig.WorkflowTaskCriticalAttempts.Get(dc),
WorkflowTaskRetryMaxInterval: dynamicconfig.WorkflowTaskRetryMaxInterval.Get(dc),
EnableWorkflowTaskStampIncrementOnFailure: dynamicconfig.EnableWorkflowTaskStampIncrementOnFailure.Get(dc),
DiscardSpeculativeWorkflowTaskMaximumEventsCount: dynamicconfig.DiscardSpeculativeWorkflowTaskMaximumEventsCount.Get(dc),
EnableDropRepeatedWorkflowTaskFailures: dynamicconfig.EnableDropRepeatedWorkflowTaskFailures.Get(dc),
SendTransientOrSpeculativeWorkflowTaskEvents: dynamicconfig.SendTransientOrSpeculativeWorkflowTaskEvents.Get(dc),
ReplicationTaskApplyTimeout: dynamicconfig.ReplicationTaskApplyTimeout.Get(dc),
ReplicationTaskFetcherParallelism: dynamicconfig.ReplicationTaskFetcherParallelism.Get(dc),
ReplicationTaskFetcherAggregationInterval: dynamicconfig.ReplicationTaskFetcherAggregationInterval.Get(dc),
ReplicationTaskFetcherTimerJitterCoefficient: dynamicconfig.ReplicationTaskFetcherTimerJitterCoefficient.Get(dc),
ReplicationTaskFetcherErrorRetryWait: dynamicconfig.ReplicationTaskFetcherErrorRetryWait.Get(dc),
ReplicationTaskProcessorErrorRetryWait: dynamicconfig.ReplicationTaskProcessorErrorRetryWait.Get(dc),
ReplicationTaskProcessorErrorRetryBackoffCoefficient: dynamicconfig.ReplicationTaskProcessorErrorRetryBackoffCoefficient.Get(dc),
ReplicationTaskProcessorErrorRetryMaxInterval: dynamicconfig.ReplicationTaskProcessorErrorRetryMaxInterval.Get(dc),
ReplicationTaskProcessorErrorRetryMaxAttempts: dynamicconfig.ReplicationTaskProcessorErrorRetryMaxAttempts.Get(dc),
ReplicationTaskProcessorErrorRetryExpiration: dynamicconfig.ReplicationTaskProcessorErrorRetryExpiration.Get(dc),
ReplicationTaskProcessorNoTaskRetryWait: dynamicconfig.ReplicationTaskProcessorNoTaskInitialWait.Get(dc),
ReplicationTaskProcessorCleanupInterval: dynamicconfig.ReplicationTaskProcessorCleanupInterval.Get(dc),
ReplicationTaskProcessorCleanupJitterCoefficient: dynamicconfig.ReplicationTaskProcessorCleanupJitterCoefficient.Get(dc),
ReplicationMultipleBatches: dynamicconfig.ReplicationMultipleBatches.Get(dc),
ReplicationStreamSenderErrorRetryWait: dynamicconfig.ReplicationStreamSenderErrorRetryWait.Get(dc),
ReplicationStreamSenderErrorRetryBackoffCoefficient: dynamicconfig.ReplicationStreamSenderErrorRetryBackoffCoefficient.Get(dc),
ReplicationStreamSenderErrorRetryMaxInterval: dynamicconfig.ReplicationStreamSenderErrorRetryMaxInterval.Get(dc),
ReplicationStreamSenderErrorRetryMaxAttempts: dynamicconfig.ReplicationStreamSenderErrorRetryMaxAttempts.Get(dc),
ReplicationStreamSenderErrorRetryExpiration: dynamicconfig.ReplicationStreamSenderErrorRetryExpiration.Get(dc),
ReplicationExecutableTaskErrorRetryWait: dynamicconfig.ReplicationExecutableTaskErrorRetryWait.Get(dc),
ReplicationExecutableTaskErrorRetryBackoffCoefficient: dynamicconfig.ReplicationExecutableTaskErrorRetryBackoffCoefficient.Get(dc),
ReplicationExecutableTaskErrorRetryMaxInterval: dynamicconfig.ReplicationExecutableTaskErrorRetryMaxInterval.Get(dc),
ReplicationExecutableTaskErrorRetryMaxAttempts: dynamicconfig.ReplicationExecutableTaskErrorRetryMaxAttempts.Get(dc),
ReplicationExecutableTaskErrorRetryExpiration: dynamicconfig.ReplicationExecutableTaskErrorRetryExpiration.Get(dc),
MaxBufferedQueryCount: dynamicconfig.MaxBufferedQueryCount.Get(dc),
MutableStateChecksumGenProbability: dynamicconfig.MutableStateChecksumGenProbability.Get(dc),
MutableStateChecksumVerifyProbability: dynamicconfig.MutableStateChecksumVerifyProbability.Get(dc),
MutableStateChecksumInvalidateBefore: dynamicconfig.MutableStateChecksumInvalidateBefore.Get(dc),
StandbyTaskReReplicationContextTimeout: dynamicconfig.StandbyTaskReReplicationContextTimeout.Get(dc),
SkipReapplicationByNamespaceID: dynamicconfig.SkipReapplicationByNamespaceID.Get(dc),
// ===== Visibility related =====
VisibilityTaskBatchSize: dynamicconfig.VisibilityTaskBatchSize.Get(dc),
VisibilityProcessorMaxPollRPS: dynamicconfig.VisibilityProcessorMaxPollRPS.Get(dc),
VisibilityProcessorMaxPollHostRPS: dynamicconfig.VisibilityProcessorMaxPollHostRPS.Get(dc),
VisibilityProcessorSchedulerWorkerCount: dynamicconfig.VisibilityProcessorSchedulerWorkerCount.Subscribe(dc),
VisibilityProcessorSchedulerActiveRoundRobinWeights: dynamicconfig.VisibilityProcessorSchedulerActiveRoundRobinWeights.WithDefault(ConvertWeightsToDynamicConfigValue(DefaultActiveTaskPriorityWeight)).Get(dc),
VisibilityProcessorSchedulerStandbyRoundRobinWeights: dynamicconfig.VisibilityProcessorSchedulerStandbyRoundRobinWeights.WithDefault(ConvertWeightsToDynamicConfigValue(DefaultStandbyTaskPriorityWeight)).Get(dc),
VisibilityProcessorMaxPollInterval: dynamicconfig.VisibilityProcessorMaxPollInterval.Get(dc),
VisibilityProcessorMaxPollIntervalJitterCoefficient: dynamicconfig.VisibilityProcessorMaxPollIntervalJitterCoefficient.Get(dc),
VisibilityProcessorUpdateAckInterval: dynamicconfig.VisibilityProcessorUpdateAckInterval.Get(dc),
VisibilityProcessorUpdateAckIntervalJitterCoefficient: dynamicconfig.VisibilityProcessorUpdateAckIntervalJitterCoefficient.Get(dc),
VisibilityProcessorPollBackoffInterval: dynamicconfig.VisibilityProcessorPollBackoffInterval.Get(dc),
VisibilityProcessorEnsureCloseBeforeDelete: dynamicconfig.VisibilityProcessorEnsureCloseBeforeDelete.Get(dc),
VisibilityProcessorEnableCloseWorkflowCleanup: dynamicconfig.VisibilityProcessorEnableCloseWorkflowCleanup.Get(dc),
VisibilityProcessorRelocateAttributesMinBlobSize: dynamicconfig.VisibilityProcessorRelocateAttributesMinBlobSize.Get(dc),
VisibilityQueueMaxReaderCount: dynamicconfig.VisibilityQueueMaxReaderCount.Get(dc),
DisableFetchRelocatableAttributesFromVisibility: dynamicconfig.DisableFetchRelocatableAttributesFromVisibility.Get(dc),
SearchAttributesNumberOfKeysLimit: dynamicconfig.SearchAttributesNumberOfKeysLimit.Get(dc),
SearchAttributesSizeOfValueLimit: dynamicconfig.SearchAttributesSizeOfValueLimit.Get(dc),
SearchAttributesTotalSizeLimit: dynamicconfig.SearchAttributesTotalSizeLimit.Get(dc),
IndexerConcurrency: dynamicconfig.WorkerIndexerConcurrency.Get(dc),
ESProcessorNumOfWorkers: dynamicconfig.WorkerESProcessorNumOfWorkers.Get(dc),
// Should not be greater than number of visibility task queue workers VisibilityProcessorSchedulerWorkerCount (default 512)
// Otherwise, visibility queue processors won't be able to fill up bulk with documents (even under heavy load) and bulk will flush due to interval, not number of actions.
ESProcessorBulkActions: dynamicconfig.WorkerESProcessorBulkActions.Get(dc),
// 16MB - just a sanity check. With ES document size ~1Kb it should never be reached.
ESProcessorBulkSize: dynamicconfig.WorkerESProcessorBulkSize.Get(dc),
// Bulk processor will flush every this interval regardless of last flush due to bulk actions.
ESProcessorFlushInterval: dynamicconfig.WorkerESProcessorFlushInterval.Get(dc),
ESProcessorAckTimeout: dynamicconfig.WorkerESProcessorAckTimeout.Get(dc),
EnableCrossNamespaceCommands: dynamicconfig.EnableCrossNamespaceCommands.Get(dc),
EnableActivityEagerExecution: dynamicconfig.EnableActivityEagerExecution.Get(dc),
EnableActivityRetryStampIncrement: dynamicconfig.EnableActivityRetryStampIncrement.Get(dc),
EnableCancelActivityWorkerCommand: dynamicconfig.EnableCancelActivityWorkerCommand.Get(dc),
EnableEagerWorkflowStart: dynamicconfig.EnableEagerWorkflowStart.Get(dc),
NamespaceCacheRefreshInterval: dynamicconfig.NamespaceCacheRefreshInterval.Get(dc),
// Archival related
ArchivalTaskBatchSize: dynamicconfig.ArchivalTaskBatchSize.Get(dc),
ArchivalProcessorMaxPollRPS: dynamicconfig.ArchivalProcessorMaxPollRPS.Get(dc),
ArchivalProcessorMaxPollHostRPS: dynamicconfig.ArchivalProcessorMaxPollHostRPS.Get(dc),
ArchivalProcessorSchedulerWorkerCount: dynamicconfig.ArchivalProcessorSchedulerWorkerCount.Subscribe(dc),
ArchivalProcessorMaxPollInterval: dynamicconfig.ArchivalProcessorMaxPollInterval.Get(dc),
ArchivalProcessorMaxPollIntervalJitterCoefficient: dynamicconfig.ArchivalProcessorMaxPollIntervalJitterCoefficient.Get(dc),
ArchivalProcessorUpdateAckInterval: dynamicconfig.ArchivalProcessorUpdateAckInterval.Get(dc),
ArchivalProcessorUpdateAckIntervalJitterCoefficient: dynamicconfig.ArchivalProcessorUpdateAckIntervalJitterCoefficient.Get(dc),
ArchivalProcessorPollBackoffInterval: dynamicconfig.ArchivalProcessorPollBackoffInterval.Get(dc),
ArchivalProcessorArchiveDelay: dynamicconfig.ArchivalProcessorArchiveDelay.Get(dc),
ArchivalBackendMaxRPS: dynamicconfig.ArchivalBackendMaxRPS.Get(dc),
ArchivalQueueMaxReaderCount: dynamicconfig.ArchivalQueueMaxReaderCount.Get(dc),
// workflow update related
WorkflowExecutionMaxInFlightUpdates: dynamicconfig.WorkflowExecutionMaxInFlightUpdates.Get(dc),
WorkflowExecutionMaxInFlightUpdatePayloads: dynamicconfig.WorkflowExecutionMaxInFlightUpdatePayloads.Get(dc),
WorkflowExecutionMaxTotalUpdates: dynamicconfig.WorkflowExecutionMaxTotalUpdates.Get(dc),
WorkflowExecutionMaxTotalUpdatesSuggestContinueAsNewThreshold: dynamicconfig.WorkflowExecutionMaxTotalUpdatesSuggestContinueAsNewThreshold.Get(dc),
EnableUpdateWithStartRetryOnClosedWorkflowAbort: dynamicconfig.EnableUpdateWithStartRetryOnClosedWorkflowAbort.Get(dc),
EnableUpdateWithStartRetryableErrorOnClosedWorkflowAbort: dynamicconfig.EnableUpdateWithStartRetryableErrorOnClosedWorkflowAbort.Get(dc),
SendRawHistoryBetweenInternalServices: dynamicconfig.SendRawHistoryBetweenInternalServices.Get(dc),
SendRawHistoryBytesToMatchingService: dynamicconfig.SendRawHistoryBytesToMatchingService.Get(dc),
SendRawWorkflowHistory: dynamicconfig.SendRawWorkflowHistory.Get(dc),
WorkflowIdReuseMinimalInterval: dynamicconfig.WorkflowIdReuseMinimalInterval.Get(dc),
EnableWorkflowIdReuseStartTimeValidation: dynamicconfig.EnableWorkflowIdReuseStartTimeValidation.Get(dc),
BusinessIDReuseRate: dynamicconfig.BusinessIDReuseRate.Get(dc),
BusinessIDReuseBurstRatio: dynamicconfig.BusinessIDReuseBurstRatio.Get(dc),
BusinessIDReuseLimiterCacheSize: dynamicconfig.BusinessIDReuseLimiterCacheSize.Get(dc),
BusinessIDReuseLimiterCacheTTL: dynamicconfig.BusinessIDReuseLimiterCacheTTL.Get(dc),
HealthPersistenceLatencyFailure: dynamicconfig.HealthPersistenceLatencyFailure.Get(dc),
HealthPersistenceLatencyPercentiles: dynamicconfig.PersistenceHealthSignalPercentileLatencySettings.Get(dc),
HealthPersistenceErrorRatio: dynamicconfig.HealthPersistenceErrorRatio.Get(dc),
HealthRPCLatencyFailure: dynamicconfig.HealthRPCLatencyFailure.Get(dc),
HealthRPCLatencyPercentiles: dynamicconfig.HistoryHealthSignalPercentileLatencySettings.Get(dc),
HealthRPCErrorRatio: dynamicconfig.HealthRPCErrorRatio.Get(dc),
HealthHistoryInitializationTime: dynamicconfig.HealthHistoryInitializationTime.Get(dc),
BreakdownMetricsByTaskQueue: dynamicconfig.MetricsBreakdownByTaskQueue.Get(dc),
LogAllReqErrors: dynamicconfig.LogAllReqErrors.Get(dc),
NumConsecutiveWorkflowTaskProblemsToTriggerSearchAttribute: dynamicconfig.NumConsecutiveWorkflowTaskProblemsToTriggerSearchAttribute.Get(dc),
// Worker-Versioning related
UseRevisionNumberForWorkerVersioning: dynamicconfig.UseRevisionNumberForWorkerVersioning.Get(dc),
EnableSuggestCaNOnNewTargetVersion: dynamicconfig.EnableSuggestCaNOnNewTargetVersion.Get(dc),
EnableSendTargetVersionChanged: dynamicconfig.EnableSendTargetVersionChanged.Get(dc),
VersionMembershipCacheTTL: dynamicconfig.VersionMembershipCacheTTL.Get(dc),
VersionMembershipCacheMaxSize: dynamicconfig.VersionMembershipCacheMaxSize.Get(dc),
EnableVersionReactivationSignals: dynamicconfig.EnableVersionReactivationSignals.Get(dc),
RoutingInfoCacheTTL: dynamicconfig.RoutingInfoCacheTTL.Get(dc),
RoutingInfoCacheMaxSize: dynamicconfig.RoutingInfoCacheMaxSize.Get(dc),
// Workflow task completion pagination
EnableWorkflowTaskCompletionPagination: dynamicconfig.EnableWorkflowTaskCompletionPagination.Get(dc),
WorkflowTaskCompletionBufferSizeLimit: dynamicconfig.WorkflowTaskCompletionBufferSizeLimit.Get(dc),
}
return cfg
}
// GetShardID return the corresponding shard ID for a given namespaceID and workflowID pair
type GlobalBoolConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[bool]
func NewGlobalBoolSetting(key string, def bool, description string) GlobalBoolSetting {
setting_gen.go
return NewGlobalTypedSettingWithConverter[bool](key, convertBool, def, description)
}
func NewGlobalBoolSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[bool], description string) GlobalBoolConstrainedDefaultSetting {
type NamespaceBoolConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[bool]
func NewNamespaceBoolSetting(key string, def bool, description string) NamespaceBoolSetting {
setting_gen.go
return NewNamespaceTypedSettingWithConverter[bool](key, convertBool, def, description)
}
func NewNamespaceBoolSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[bool], description string) NamespaceBoolConstrainedDefaultSetting {
type BoolPropertyFnWithNamespaceFilter = TypedPropertyFnWithNamespaceFilter[bool]
func GetBoolPropertyFnFilteredByNamespace(value bool) BoolPropertyFnWithNamespaceFilter {
setting_gen.go
return GetTypedPropertyFnFilteredByNamespace(value)
}
type NamespaceIDBoolSetting = NamespaceIDTypedSetting[bool]
type NamespaceIDBoolConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[bool]
func NewNamespaceIDBoolSetting(key string, def bool, description string) NamespaceIDBoolSetting {
setting_gen.go
return NewNamespaceIDTypedSettingWithConverter[bool](key, convertBool, def, description)
}
func NewNamespaceIDBoolSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[bool], description string) NamespaceIDBoolConstrainedDefaultSetting {
type TaskQueueBoolConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[bool]
func NewTaskQueueBoolSetting(key string, def bool, description string) TaskQueueBoolSetting {
setting_gen.go
return NewTaskQueueTypedSettingWithConverter[bool](key, convertBool, def, description)
}
func NewTaskQueueBoolSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[bool], description string) TaskQueueBoolConstrainedDefaultSetting {
type DestinationBoolConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[bool]
func NewDestinationBoolSetting(key string, def bool, description string) DestinationBoolSetting {
setting_gen.go
return NewDestinationTypedSettingWithConverter[bool](key, convertBool, def, description)
}
func NewDestinationBoolSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[bool], description string) DestinationBoolConstrainedDefaultSetting {
type GlobalIntConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[int]
func NewGlobalIntSetting(key string, def int, description string) GlobalIntSetting {
setting_gen.go
return NewGlobalTypedSettingWithConverter[int](key, convertInt, def, description)
}
func NewGlobalIntSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[int], description string) GlobalIntConstrainedDefaultSetting {
type NamespaceIntConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[int]
func NewNamespaceIntSetting(key string, def int, description string) NamespaceIntSetting {
setting_gen.go
return NewNamespaceTypedSettingWithConverter[int](key, convertInt, def, description)
}
func NewNamespaceIntSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[int], description string) NamespaceIntConstrainedDefaultSetting {
type IntPropertyFnWithNamespaceFilter = TypedPropertyFnWithNamespaceFilter[int]
func GetIntPropertyFnFilteredByNamespace(value int) IntPropertyFnWithNamespaceFilter {
setting_gen.go
return GetTypedPropertyFnFilteredByNamespace(value)
}
type NamespaceIDIntSetting = NamespaceIDTypedSetting[int]
type TaskQueueIntConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[int]
func NewTaskQueueIntSetting(key string, def int, description string) TaskQueueIntSetting {
setting_gen.go
return NewTaskQueueTypedSettingWithConverter[int](key, convertInt, def, description)
}
func NewTaskQueueIntSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[int], description string) TaskQueueIntConstrainedDefaultSetting {
setting_gen.go
return NewTaskQueueTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description)
}
type IntPropertyFnWithTaskQueueFilter = TypedPropertyFnWithTaskQueueFilter[int]
type ShardIDIntConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[int]
func NewShardIDIntSetting(key string, def int, description string) ShardIDIntSetting {
setting_gen.go
return NewShardIDTypedSettingWithConverter[int](key, convertInt, def, description)
}
func NewShardIDIntSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[int], description string) ShardIDIntConstrainedDefaultSetting {
type DestinationIntConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[int]
func NewDestinationIntSetting(key string, def int, description string) DestinationIntSetting {
setting_gen.go
return NewDestinationTypedSettingWithConverter[int](key, convertInt, def, description)
}
func NewDestinationIntSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[int], description string) DestinationIntConstrainedDefaultSetting {
type GlobalFloatConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[float64]
func NewGlobalFloatSetting(key string, def float64, description string) GlobalFloatSetting {
setting_gen.go
return NewGlobalTypedSettingWithConverter[float64](key, convertFloat, def, description)
}
func NewGlobalFloatSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[float64], description string) GlobalFloatConstrainedDefaultSetting {
type NamespaceFloatConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[float64]
func NewNamespaceFloatSetting(key string, def float64, description string) NamespaceFloatSetting {
setting_gen.go
return NewNamespaceTypedSettingWithConverter[float64](key, convertFloat, def, description)
}
func NewNamespaceFloatSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[float64], description string) NamespaceFloatConstrainedDefaultSetting {
type TaskQueueFloatConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[float64]
func NewTaskQueueFloatSetting(key string, def float64, description string) TaskQueueFloatSetting {
setting_gen.go
return NewTaskQueueTypedSettingWithConverter[float64](key, convertFloat, def, description)
}
func NewTaskQueueFloatSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[float64], description string) TaskQueueFloatConstrainedDefaultSetting {
type ShardIDFloatConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[float64]
func NewShardIDFloatSetting(key string, def float64, description string) ShardIDFloatSetting {
setting_gen.go
return NewShardIDTypedSettingWithConverter[float64](key, convertFloat, def, description)
}
func NewShardIDFloatSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[float64], description string) ShardIDFloatConstrainedDefaultSetting {
type DestinationFloatConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[float64]
func NewDestinationFloatSetting(key string, def float64, description string) DestinationFloatSetting {
setting_gen.go
return NewDestinationTypedSettingWithConverter[float64](key, convertFloat, def, description)
}
func NewDestinationFloatSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[float64], description string) DestinationFloatConstrainedDefaultSetting {
type GlobalStringConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[string]
func NewGlobalStringSetting(key string, def string, description string) GlobalStringSetting {
setting_gen.go
return NewGlobalTypedSettingWithConverter[string](key, convertString, def, description)
}
func NewGlobalStringSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[string], description string) GlobalStringConstrainedDefaultSetting {
type StringPropertyFn = TypedPropertyFn[string]
return GetTypedPropertyFn(value)
}
type NamespaceStringSetting = NamespaceTypedSetting[string]
type GlobalDurationConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[time.Duration]
func NewGlobalDurationSetting(key string, def time.Duration, description string) GlobalDurationSetting {
setting_gen.go
return NewGlobalTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewGlobalDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) GlobalDurationConstrainedDefaultSetting {
type NamespaceDurationConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[time.Duration]
func NewNamespaceDurationSetting(key string, def time.Duration, description string) NamespaceDurationSetting {
setting_gen.go
return NewNamespaceTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewNamespaceDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) NamespaceDurationConstrainedDefaultSetting {
type NamespaceIDDurationConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[time.Duration]
func NewNamespaceIDDurationSetting(key string, def time.Duration, description string) NamespaceIDDurationSetting {
setting_gen.go
return NewNamespaceIDTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewNamespaceIDDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) NamespaceIDDurationConstrainedDefaultSetting {
type TaskQueueDurationConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[time.Duration]
func NewTaskQueueDurationSetting(key string, def time.Duration, description string) TaskQueueDurationSetting {
setting_gen.go
return NewTaskQueueTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewTaskQueueDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) TaskQueueDurationConstrainedDefaultSetting {
setting_gen.go
return NewTaskQueueTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description)
}
type DurationPropertyFnWithTaskQueueFilter = TypedPropertyFnWithTaskQueueFilter[time.Duration]
type ShardIDDurationConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[time.Duration]
func NewShardIDDurationSetting(key string, def time.Duration, description string) ShardIDDurationSetting {
setting_gen.go
return NewShardIDTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewShardIDDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) ShardIDDurationConstrainedDefaultSetting {
type TaskTypeDurationConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[time.Duration]
func NewTaskTypeDurationSetting(key string, def time.Duration, description string) TaskTypeDurationSetting {
setting_gen.go
return NewTaskTypeTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewTaskTypeDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) TaskTypeDurationConstrainedDefaultSetting {
type DestinationDurationConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[time.Duration]
func NewDestinationDurationSetting(key string, def time.Duration, description string) DestinationDurationSetting {
setting_gen.go
return NewDestinationTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewDestinationDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) DestinationDurationConstrainedDefaultSetting {
type ChasmTaskTypeDurationConstrainedDefaultSetting = ChasmTaskTypeTypedConstrainedDefaultSetting[time.Duration]
func NewChasmTaskTypeDurationSetting(key string, def time.Duration, description string) ChasmTaskTypeDurationSetting {
setting_gen.go
return NewChasmTaskTypeTypedSettingWithConverter[time.Duration](key, convertDuration, def, description)
}
func NewChasmTaskTypeDurationSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[time.Duration], description string) ChasmTaskTypeDurationConstrainedDefaultSetting {
type NamespaceMapConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[map[string]any]
func NewNamespaceMapSetting(key string, def map[string]any, description string) NamespaceMapSetting {
setting_gen.go
return NewNamespaceTypedSettingWithConverter[map[string]any](key, convertMap, def, description)
}
func NewNamespaceMapSettingWithConstrainedDefault(key string, cdef []TypedConstrainedValue[map[string]any], description string) NamespaceMapConstrainedDefaultSetting {
// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful
// when using non-empty maps or slices as defaults, the result may not be what you want.
func NewGlobalTypedSetting[T any](key string, def T, description string) GlobalTypedSetting[T] {
setting_gen.go
// Warn on any shared structure used with ConvertStructure, even though we handle it by deep copying.
warnDefaultSharedStructure(key, def)
// If even deep copy won't even work, we should panic early. Do that by calling deep copy once here.
_ = deepCopyForMapstructure(def)
s := GlobalTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: ConvertStructure[T](def),
description: description,
}
register(s)
return s
}
// NewGlobalTypedSettingWithConverter creates a setting with a custom converter function.
func NewGlobalTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) GlobalTypedSetting[T] {
setting_gen.go
s := GlobalTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewGlobalTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s GlobalTypedSetting[T]) Precedence() Precedence { return PrecedenceGlobal }
func (s GlobalTypedSetting[T]) Validate(v any) error {
}
newS := s
newS.def = v
return newS
}
type TypedPropertyFn[T any] func() T
return func() T {
return matchAndConvert(
c,
s.key,
s.def,
s.convert,
prec,
)
}
}
type TypedSubscribable[T any] func(callback func(T)) (v T, cancel func())
return func(callback func(T)) (T, func()) {
return subscribe(c, s.key, s.def, s.convert, prec, callback)
}
}
}
return func() T {
return value
}
// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful
// when using non-empty maps or slices as defaults, the result may not be what you want.
func NewNamespaceTypedSetting[T any](key string, def T, description string) NamespaceTypedSetting[T] {
setting_gen.go
// Warn on any shared structure used with ConvertStructure, even though we handle it by deep copying.
warnDefaultSharedStructure(key, def)
// If even deep copy won't even work, we should panic early. Do that by calling deep copy once here.
_ = deepCopyForMapstructure(def)
s := NamespaceTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: ConvertStructure[T](def),
description: description,
}
register(s)
return s
}
// NewNamespaceTypedSettingWithConverter creates a setting with a custom converter function.
func NewNamespaceTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) NamespaceTypedSetting[T] {
setting_gen.go
s := NamespaceTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewNamespaceTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s NamespaceTypedSetting[T]) Precedence() Precedence { return PrecedenceNamespace }
func (s NamespaceTypedSetting[T]) Validate(v any) error {
}
newS := s
newS.def = v
return newS
}
type TypedPropertyFnWithNamespaceFilter[T any] func(namespace string) T
func (s NamespaceTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithNamespaceFilter[T] {
setting_gen.go
return func(namespace string) T {
return matchAndConvert(
c,
s.key,
s.def,
s.convert,
prec,
)
}
}
type TypedSubscribableWithNamespaceFilter[T any] func(namespace string, callback func(T)) (v T, cancel func())
func (s NamespaceTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithNamespaceFilter[T] {
setting_gen.go
return func(namespace string, callback func(T)) (T, func()) {
return subscribe(c, s.key, s.def, s.convert, prec, callback)
}
}
}
func GetTypedPropertyFnFilteredByNamespace[T any](value T) TypedPropertyFnWithNamespaceFilter[T] {
setting_gen.go
return func(namespace string) T {
return value
}
// NewNamespaceIDTypedSettingWithConverter creates a setting with a custom converter function.
func NewNamespaceIDTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) NamespaceIDTypedSetting[T] {
setting_gen.go
s := NamespaceIDTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewNamespaceIDTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s NamespaceIDTypedSetting[T]) Precedence() Precedence { return PrecedenceNamespaceID }
func (s NamespaceIDTypedSetting[T]) Validate(v any) error {
type TypedPropertyFnWithNamespaceIDFilter[T any] func(namespaceID namespace.ID) T
func (s NamespaceIDTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithNamespaceIDFilter[T] {
setting_gen.go
return func(namespaceID namespace.ID) T {
prec := []Constraints{{NamespaceID: namespaceID.String()}, {}}
return matchAndConvert(
// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful
// when using non-empty maps or slices as defaults, the result may not be what you want.
func NewTaskQueueTypedSetting[T any](key string, def T, description string) TaskQueueTypedSetting[T] {
setting_gen.go
// Warn on any shared structure used with ConvertStructure, even though we handle it by deep copying.
warnDefaultSharedStructure(key, def)
// If even deep copy won't even work, we should panic early. Do that by calling deep copy once here.
_ = deepCopyForMapstructure(def)
s := TaskQueueTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: ConvertStructure[T](def),
description: description,
}
register(s)
return s
}
// NewTaskQueueTypedSettingWithConverter creates a setting with a custom converter function.
func NewTaskQueueTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) TaskQueueTypedSetting[T] {
setting_gen.go
s := TaskQueueTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewTaskQueueTypedSettingWithConstrainedDefault creates a setting with a compound default value.
func NewTaskQueueTypedSettingWithConstrainedDefault[T any](key string, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) TaskQueueTypedConstrainedDefaultSetting[T] {
setting_gen.go
s := TaskQueueTypedConstrainedDefaultSetting[T]{
key: MakeKey(key),
cdef: cdef,
convert: convert,
description: description,
}
register(s)
return s
}
func (s TaskQueueTypedSetting[T]) Precedence() Precedence { return PrecedenceTaskQueue }
func (s TaskQueueTypedSetting[T]) Validate(v any) error {
}
func (s TaskQueueTypedConstrainedDefaultSetting[T]) Key() Key { return s.key }
setting_gen.go
func (s TaskQueueTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceTaskQueue }
func (s TaskQueueTypedConstrainedDefaultSetting[T]) Validate(v any) error {
type TypedPropertyFnWithTaskQueueFilter[T any] func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType) T
func (s TaskQueueTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskQueueFilter[T] {
setting_gen.go
return func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType) T {
prec := []Constraints{
{Namespace: namespace, TaskQueueName: taskQueue, TaskQueueType: taskQueueType},
}
func (s TaskQueueTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskQueueFilter[T] {
setting_gen.go
return func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType) T {
prec := []Constraints{
{Namespace: namespace, TaskQueueName: taskQueue, TaskQueueType: taskQueueType},
type TypedSubscribableWithTaskQueueFilter[T any] func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType, callback func(T)) (v T, cancel func())
func (s TaskQueueTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithTaskQueueFilter[T] {
setting_gen.go
return func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType, callback func(T)) (T, func()) {
prec := []Constraints{
{Namespace: namespace, TaskQueueName: taskQueue, TaskQueueType: taskQueueType},
}
func (s TaskQueueTypedConstrainedDefaultSetting[T]) Subscribe(c *Collection) TypedSubscribableWithTaskQueueFilter[T] {
setting_gen.go
return func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType, callback func(T)) (T, func()) {
prec := []Constraints{
{Namespace: namespace, TaskQueueName: taskQueue, TaskQueueType: taskQueueType},
// NewShardIDTypedSettingWithConverter creates a setting with a custom converter function.
func NewShardIDTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) ShardIDTypedSetting[T] {
setting_gen.go
s := ShardIDTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewShardIDTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s ShardIDTypedSetting[T]) Precedence() Precedence { return PrecedenceShardID }
func (s ShardIDTypedSetting[T]) Validate(v any) error {
type TypedPropertyFnWithShardIDFilter[T any] func(shardID int32) T
func (s ShardIDTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithShardIDFilter[T] {
setting_gen.go
return func(shardID int32) T {
return matchAndConvert(
c,
s.key,
s.def,
s.convert,
prec,
)
}
}
// NewTaskTypeTypedSettingWithConverter creates a setting with a custom converter function.
func NewTaskTypeTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) TaskTypeTypedSetting[T] {
setting_gen.go
s := TaskTypeTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewTaskTypeTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s TaskTypeTypedSetting[T]) Precedence() Precedence { return PrecedenceTaskType }
func (s TaskTypeTypedSetting[T]) Validate(v any) error {
type TypedPropertyFnWithTaskTypeFilter[T any] func(taskType enumsspb.TaskType) T
func (s TaskTypeTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskTypeFilter[T] {
setting_gen.go
return func(taskType enumsspb.TaskType) T {
prec := []Constraints{{TaskType: taskType}, {}}
return matchAndConvert(
// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful
// when using non-empty maps or slices as defaults, the result may not be what you want.
func NewDestinationTypedSetting[T any](key string, def T, description string) DestinationTypedSetting[T] {
setting_gen.go
// Warn on any shared structure used with ConvertStructure, even though we handle it by deep copying.
warnDefaultSharedStructure(key, def)
// If even deep copy won't even work, we should panic early. Do that by calling deep copy once here.
_ = deepCopyForMapstructure(def)
s := DestinationTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: ConvertStructure[T](def),
description: description,
}
register(s)
return s
}
// NewDestinationTypedSettingWithConverter creates a setting with a custom converter function.
func NewDestinationTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) DestinationTypedSetting[T] {
setting_gen.go
s := DestinationTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewDestinationTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s DestinationTypedSetting[T]) Precedence() Precedence { return PrecedenceDestination }
func (s DestinationTypedSetting[T]) Validate(v any) error {
type TypedPropertyFnWithDestinationFilter[T any] func(namespace string, destination string) T
func (s DestinationTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithDestinationFilter[T] {
setting_gen.go
return func(namespace string, destination string) T {
prec := []Constraints{
{Namespace: namespace, Destination: destination},
type TypedSubscribableWithDestinationFilter[T any] func(namespace string, destination string, callback func(T)) (v T, cancel func())
func (s DestinationTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithDestinationFilter[T] {
setting_gen.go
return func(namespace string, destination string, callback func(T)) (T, func()) {
prec := []Constraints{
{Namespace: namespace, Destination: destination},
// NewChasmTaskTypeTypedSettingWithConverter creates a setting with a custom converter function.
func NewChasmTaskTypeTypedSettingWithConverter[T any](key string, convert func(any) (T, error), def T, description string) ChasmTaskTypeTypedSetting[T] {
setting_gen.go
s := ChasmTaskTypeTypedSetting[T]{
key: MakeKey(key),
def: def,
convert: convert,
description: description,
}
register(s)
return s
}
// NewChasmTaskTypeTypedSettingWithConstrainedDefault creates a setting with a compound default value.
}
func (s ChasmTaskTypeTypedSetting[T]) Precedence() Precedence { return PrecedenceChasmTaskType }
func (s ChasmTaskTypeTypedSetting[T]) Validate(v any) error {
type TypedPropertyFnWithChasmTaskTypeFilter[T any] func(chasmTaskType string) T
func (s ChasmTaskTypeTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithChasmTaskTypeFilter[T] {
setting_gen.go
return func(chasmTaskType string) T {
prec := []Constraints{{ChasmTaskType: chasmTaskType}, {}}
return matchAndConvert(
// NewShardPersistenceMetricsClient creates a client to manage shards
func NewShardPersistenceMetricsClient(persistence ShardManager, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) ShardManager {
persistence_metric_clients.go
return &shardPersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
// NewExecutionPersistenceMetricsClient creates a client to manage executions
func NewExecutionPersistenceMetricsClient(persistence ExecutionManager, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) ExecutionManager {
persistence_metric_clients.go
return &executionPersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
// NewTaskPersistenceMetricsClient creates a client to manage tasks
func NewTaskPersistenceMetricsClient(persistence TaskManager, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) TaskManager {
persistence_metric_clients.go
return &taskPersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
// NewMetadataPersistenceMetricsClient creates a MetadataManager client to manage metadata
func NewMetadataPersistenceMetricsClient(persistence MetadataManager, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) MetadataManager {
persistence_metric_clients.go
return &metadataPersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
// NewClusterMetadataPersistenceMetricsClient creates a ClusterMetadataManager client to manage cluster metadata
func NewClusterMetadataPersistenceMetricsClient(persistence ClusterMetadataManager, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) ClusterMetadataManager {
persistence_metric_clients.go
return &clusterMetadataPersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
// NewQueuePersistenceMetricsClient creates a client to manage queue
func NewQueuePersistenceMetricsClient(persistence Queue, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) Queue {
persistence_metric_clients.go
return &queuePersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
// NewNexusEndpointPersistenceMetricsClient creates a NexusEndpointManager to manage nexus endpoints
func NewNexusEndpointPersistenceMetricsClient(persistence NexusEndpointManager, metricsHandler metrics.Handler, healthSignals HealthSignalAggregator, logger log.Logger, enableDataLossMetrics dynamicconfig.BoolPropertyFn) NexusEndpointManager {
persistence_metric_clients.go
return &nexusEndpointPersistenceClient{
metricEmitter: metricEmitter{
metricsHandler: metricsHandler,
logger: logger,
enableDataLossMetrics: enableDataLossMetrics,
},
healthSignals: healthSignals,
persistence: persistence,
}
}
func (p *shardPersistenceClient) GetName() string {
ctx context.Context,
request *GetOrCreateShardRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
latency := time.Since(startTime)
p.healthSignals.Record(request.ShardID, latency, retErr)
p.recordRequestMetrics(metrics.PersistenceGetOrCreateShardScope, caller, latency, retErr)
p.recordDataLossMetrics(metrics.PersistenceGetOrCreateShardScope, caller, retErr, "", "")
}()
return p.persistence.GetOrCreateShard(ctx, request)
}
ctx context.Context,
request *UpdateShardRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(request.ShardInfo.GetShardId(), time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceUpdateShardScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceUpdateShardScope, caller, retErr, "", "")
}()
return p.persistence.UpdateShard(ctx, request)
}
ctx context.Context,
request *AssertShardOwnershipRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(request.ShardID, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceAssertShardOwnershipScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceAssertShardOwnershipScope, caller, retErr, "", "")
}()
return p.persistence.AssertShardOwnership(ctx, request)
}
p.persistence.Close()
}
return p.persistence.GetName()
}
func (p *executionPersistenceClient) GetHistoryBranchUtil() HistoryBranchUtil {
ctx context.Context,
request *GetHistoryTasksRequest,
var operation string
switch request.TaskCategory.ID() {
case tasks.CategoryIDTransfer:
operation = metrics.PersistenceGetTransferTasksScope
case tasks.CategoryIDTimer:
operation = metrics.PersistenceGetTimerTasksScope
case tasks.CategoryIDVisibility:
operation = metrics.PersistenceGetVisibilityTasksScope
case tasks.CategoryIDReplication:
operation = metrics.PersistenceGetReplicationTasksScope
case tasks.CategoryIDArchival:
operation = metrics.PersistenceGetArchivalTasksScope
operation = metrics.PersistenceGetOutboundTasksScope
default:
return nil, serviceerror.NewInternalf("unknown task category type: %v", request.TaskCategory)
}
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(request.ShardID, time.Since(startTime), retErr)
p.recordRequestMetrics(operation, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(operation, caller, retErr, "", "")
}()
return p.persistence.GetHistoryTasks(ctx, request)
}
}
p.persistence.Close()
}
func (p *taskPersistenceClient) GetName() string {
}
p.persistence.Close()
}
func (p *metadataPersistenceClient) GetName() string {
ctx context.Context,
request *GetNamespaceRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceGetNamespaceScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceGetNamespaceScope, caller, retErr, "", "")
}()
return p.persistence.GetNamespace(ctx, request)
}
ctx context.Context,
request *ListNamespacesRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceListNamespacesScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceListNamespacesScope, caller, retErr, "", "")
}()
return p.persistence.ListNamespaces(ctx, request)
}
}
func (p *metadataPersistenceClient) WatchNamespaces(ctx context.Context) (_ <-chan *NamespaceWatchEvent, retErr error) {
persistence_metric_clients.go
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
metricErr := retErr
// WatchNotSupported isn't really a persistence error. It's just a signal that persistence doesn't support watching.
if errors.Is(metricErr, ErrWatchNotSupported) {
metricErr = nil
}
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), metricErr)
p.recordRequestMetrics(metrics.PersistenceWatchNamespacesScope, caller, time.Since(startTime), metricErr)
p.recordDataLossMetrics(metrics.PersistenceWatchNamespacesScope, caller, metricErr, "", "")
}()
}
p.persistence.Close()
}
// AppendHistoryNodes add a node to history node table
ctx context.Context,
blob *commonpb.DataBlob,
return p.persistence.Init(ctx, blob)
}
func (p *queuePersistenceClient) EnqueueMessage(
}
p.persistence.Close()
}
p.persistence.Close()
}
func (p *clusterMetadataPersistenceClient) ListClusterMetadata(
ctx context.Context,
request *ListClusterMetadataRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceListClusterMetadataScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceListClusterMetadataScope, caller, retErr, "", "")
}()
return p.persistence.ListClusterMetadata(ctx, request)
}
func (p *clusterMetadataPersistenceClient) GetCurrentClusterMetadata(
ctx context.Context,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceGetCurrentClusterMetadataScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceGetCurrentClusterMetadataScope, caller, retErr, "", "")
}()
return p.persistence.GetCurrentClusterMetadata(ctx)
}
ctx context.Context,
request *GetClusterMetadataRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceGetClusterMetadataScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceGetClusterMetadataScope, caller, retErr, "", "")
}()
return p.persistence.GetClusterMetadata(ctx, request)
}
ctx context.Context,
request *SaveClusterMetadataRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceSaveClusterMetadataScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceSaveClusterMetadataScope, caller, retErr, "", "")
}()
return p.persistence.SaveClusterMetadata(ctx, request)
}
ctx context.Context,
request *GetClusterMembersRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceGetClusterMembersScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceGetClusterMembersScope, caller, retErr, "", "")
}()
return p.persistence.GetClusterMembers(ctx, request)
}
ctx context.Context,
request *UpsertClusterMembershipRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceUpsertClusterMembershipScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceUpsertClusterMembershipScope, caller, retErr, "", "")
}()
return p.persistence.UpsertClusterMembership(ctx, request)
}
ctx context.Context,
request *PruneClusterMembershipRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistencePruneClusterMembershipScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistencePruneClusterMembershipScope, caller, retErr, "", "")
}()
return p.persistence.PruneClusterMembership(ctx, request)
}
ctx context.Context,
currentClusterName string,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceInitializeSystemNamespaceScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceInitializeSystemNamespaceScope, caller, retErr, "", "")
}()
return p.persistence.InitializeSystemNamespaces(ctx, currentClusterName)
}
}
p.persistence.Close()
}
func (p *nexusEndpointPersistenceClient) GetNexusEndpoint(
ctx context.Context,
request *ListNexusEndpointsRequest,
caller := headers.GetCallerInfo(ctx).CallerName
startTime := time.Now().UTC()
defer func() {
p.healthSignals.Record(CallerSegmentMissing, time.Since(startTime), retErr)
p.recordRequestMetrics(metrics.PersistenceListNexusEndpointsScope, caller, time.Since(startTime), retErr)
p.recordDataLossMetrics(metrics.PersistenceListNexusEndpointsScope, caller, retErr, "", "")
}()
return p.persistence.ListNexusEndpoints(ctx, request)
}
}
func (p *metricEmitter) recordRequestMetrics(operation string, caller string, latency time.Duration, err error) {
persistence_metric_clients.go
handler := p.metricsHandler.WithTags(metrics.OperationTag(operation), metrics.NamespaceTag(caller))
metrics.PersistenceRequests.With(handler).Record(1)
metrics.PersistenceLatency.With(handler).Record(latency)
updateErrorMetric(handler, p.logger, operation, err)
}
func (p *metricEmitter) recordDataLossMetrics(operation string, caller string, err error, workflowID, runID string) {
persistence_metric_clients.go
// Emit data loss metrics if enabled and error is DataLoss
var dataLoss *serviceerror.DataLoss
if errors.As(err, &dataLoss) {
if p.enableDataLossMetrics() {
EmitDataLossMetric(p.metricsHandler, caller, workflowID, runID, operation, err)
}
func updateErrorMetric(handler metrics.Handler, logger log.Logger, operation string, err error) {
persistence_metric_clients.go
if err != nil {
metrics.PersistenceErrorWithType.With(handler).Record(1, metrics.ServiceErrorTypeTag(err))
persistence_metric_clients.go
if common.IsContextCanceledErr(err) {
// no-op
return
}
case *ShardAlreadyExistError,
*ShardOwnershipLostError,
*serviceerror.NamespaceAlreadyExists,
*serviceerror.NotFound,
// no-op
components []workercommon.PerNSWorkerComponent,
taskQueueName string,
return &PerNamespaceWorkerManager{
logger: log.With(logger, tag.ComponentPerNSWorkerManager),
sdkClientFactory: sdkClientFactory,
namespaceRegistry: namespaceRegistry,
hostName: hostName,
taskQueueName: taskQueueName,
config: config,
components: components,
initialRetry: 1 * time.Second,
thisClusterName: clusterMetadata.GetCurrentClusterName(),
startLimiter: quotas.NewDefaultOutgoingRateLimiter(quotas.RateFn(config.PerNamespaceWorkerStartRate)),
membershipChangedCh: make(chan *membership.ChangedEvent),
workers: make(map[namespace.ID]*perNamespaceWorker),
}
}
return atomic.LoadInt32(&wm.status) == common.DaemonStatusStarted
}
func (wm *PerNamespaceWorkerManager) Start(
self membership.HostInfo,
serviceResolver membership.ServiceResolver,
if !atomic.CompareAndSwapInt32(
&wm.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
wm.serviceResolver = serviceResolver
wm.logger.Info("", tag.LifeCycleStarting)
// this will call namespaceCallback with current namespaces
wm.namespaceRegistry.RegisterStateChangeCallback(wm, wm.namespaceCallback)
err := wm.serviceResolver.AddListener(fmt.Sprintf("%p", wm), wm.membershipChangedCh)
if err != nil {
wm.logger.Fatal("Unable to register membership listener", tag.Error(err))
}
wm.backgroundLoops.Go(wm.periodicRefreshLoop)
wm.logger.Info("", tag.LifeCycleStarted)
}
if !atomic.CompareAndSwapInt32(
&wm.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
wm.namespaceRegistry.UnregisterStateChangeCallback(wm)
err := wm.serviceResolver.RemoveListener(fmt.Sprintf("%p", wm))
if err != nil {
wm.logger.Error("Unable to unregister membership listener", tag.Error(err))
}
wm.backgroundLoops.Wait()
wm.lock.Lock()
workers := expmaps.Values(wm.workers)
maps.DeleteFunc(wm.workers, func(_ namespace.ID, _ *perNamespaceWorker) bool { return true })
wm.lock.Unlock()
for _, worker := range workers {
worker.cancel()
}
}
func (wm *PerNamespaceWorkerManager) namespaceCallback(ns *namespace.Namespace, nsDeleted bool) {
pernamespaceworker.go
go wm.getWorkerByNamespace(ns).update(ns, nsDeleted, nil, nil)
}
func (wm *PerNamespaceWorkerManager) refreshAll() {
}
func (wm *PerNamespaceWorkerManager) membershipChangedListener(ctx context.Context) error {
pernamespaceworker.go
for {
select {
return nil
case <-wm.membershipChangedCh:
wm.refreshAll()
}
func (wm *PerNamespaceWorkerManager) periodicRefreshLoop(ctx context.Context) error {
pernamespaceworker.go
ticker := time.NewTicker(refreshInterval)
defer ticker.Stop()
for {
select {
return nil
case <-ticker.C:
wm.refreshAll()
}
func (wm *PerNamespaceWorkerManager) getWorkerByNamespace(ns *namespace.Namespace) *perNamespaceWorker {
pernamespaceworker.go
wm.lock.Lock()
defer wm.lock.Unlock()
if worker, ok := wm.workers[ns.ID()]; ok {
return worker
}
wm: wm,
logger: log.With(wm.logger, tag.WorkflowNamespace(ns.Name().String())),
retrier: backoff.NewRetrier(backoff.NewExponentialRetryPolicy(wm.initialRetry), clock.NewRealTimeSource()),
}
count, c1 := wm.config.PerNamespaceWorkerCount(ns.Name().String(), worker.setWorkerCount)
opts, c2 := wm.config.PerNamespaceWorkerOptions(ns.Name().String(), worker.setWorkerOptions)
worker.ns = ns
worker.count = count
worker.opts = opts
worker.cancel = func() { c1(); c2() }
return worker
}
}
func (w *perNamespaceWorker) getWorkerAllocation(args refreshArgs) (workerAllocation, error) {
pernamespaceworker.go
if args.count < 0 {
return workerAllocation{}, errInvalidConfiguration
return workerAllocation{0, 0}, nil
}
if err != nil {
return workerAllocation{}, err
}
}
func (w *perNamespaceWorker) getLocallyDesiredWorkers(args refreshArgs) (int, error) {
pernamespaceworker.go
key := args.ns.ID().String()
availableHosts := w.wm.serviceResolver.LookupN(key, args.count)
hostsCount := len(availableHosts)
if hostsCount == 0 {
return 0, membership.ErrInsufficientHosts
}
desiredDistribution := util.RepeatSlice(availableHosts, maxWorkersPerHost)[:args.count]
isLocal := func(info membership.HostInfo) bool { return info.Identity() == w.wm.self.Identity() }
result := len(util.FilterSlice(desiredDistribution, isLocal))
return result, nil
}
// called on namespace state change callback, membership change, and dynamic config change
func (w *perNamespaceWorker) update(ns *namespace.Namespace, nsDeleted bool, newCount *int, newOpts *sdkworker.Options) {
pernamespaceworker.go
w.lock.Lock()
if ns != nil {
w.ns = ns
// The name inside of *ns, which was used to initialize the logger, can change, but
// don't update w.logger here, otherwise we'd have to hold w.lock just to log.
}
if newCount != nil {
w.count = *newCount
}
w.opts = *newOpts
}
isRetrying := w.retryTimer != nil
w.lock.Unlock()
if nsDeleted {
w.stopWorkerAndResetTimer()
// if namespace is fully deleted from db, we can remove from our map also
}
w.refresh(refreshArgs)
}
}
// handleError should be called on errors from worker creation or run. it will attempt to
// refresh the worker again at a later time.
if err == nil {
return
}
w.stopWorkerAndResetTimer()
return
}
defer w.lock.Unlock()
if w.retryTimer != nil {
// this shouldn't ever happen
w.logger.Error("bug: handleError found existing timer")
}
if retryAfter, ok := err.(errRetryAfter); ok {
// asked for an explicit delay due to rate limit, use that
sleep = time.Duration(retryAfter)
if sleep < 0 {
w.logger.Error("Failed to start sdk worker, out of retries", tag.Error(err))
return
}
w.logger.Warn("Failed to start sdk worker", tag.Error(err), tag.Duration("sleep", sleep))
pernamespaceworker.go
}
w.lock.Lock()
w.retryTimer = nil
// Returning an error from here means that we should retry creating/starting the worker.
// Returning noWorkerNeeded means any existing worker should be stopped.
defer func() {
w.handleError(retErr)
}()
// note w.lock is not locked until we're about to start/stop a worker
args.ns.State() == enumspb.NAMESPACE_STATE_DELETED ||
//nolint:forbidigo // per-namespace worker lifecycle, no workflow context
!args.ns.ActiveInCluster(w.wm.thisClusterName) {
return errNoWorkerNeeded
// figure out which components are enabled at all for this namespace
var componentSet strings.Builder
for _, cmp := range w.wm.components {
options := cmp.DedicatedWorkerOptions(args.ns)
if options.Enabled {
fmt.Fprintf(&componentSet, "%p,", cmp)
}
}
// no components enabled, we don't need a worker
return errNoWorkerNeeded
// check if we are responsible for this namespace at all
if err != nil {
w.logger.Error("Failed to look up hosts", tag.Error(err))
// TODO: add metric also
return err
}
// not ours, don't need a worker
return errNoWorkerNeeded
}
// ensure this changes if multiplicity changes
// get sdk worker options
fmt.Fprintf(&componentSet, "%+v,", w.opts)
// we do need a worker, but maybe we have one already
w.lock.Lock()
defer w.lock.Unlock()
if args.ns != w.ns {
// stale refresh goroutine, do nothing
return nil
}
// no change in set of components enabled, leave existing running
return nil
// ask rate limiter if we can start now
w.reserved = true
if delay := w.wm.startLimiter.Reserve().Delay(); delay > 0 {
return errRetryAfter(delay)
}
// set of components changed, need to recreate worker. first stop old one
// create new one. note that even before startWorker returns, the worker may have started
// and already called the fatal error handler. we need to set w.client+worker+componentSet
// before releasing the lock to keep our state consistent.
client, worker, err := w.startWorker(enabledComponents, workerAllocation)
if err != nil {
w.stopWorkerLocked() // for calling cleanup
return err
}
w.client = client
components []workercommon.PerNSWorkerComponent,
allocation workerAllocation,
nsName := w.ns.Name().String()
// this should not block because it uses an existing grpc connection
client := w.wm.sdkClientFactory.NewClient(sdkclient.Options{
Namespace: nsName,
DataConverter: sdk.PreferProtoDataConverter,
})
var sdkoptions sdkworker.Options
// copy from dynamic config. apply explicit defaults for some instead of using the sdk
// defaults so that we can multiply below.
sdkoptions.MaxConcurrentActivityExecutionSize = cmp.Or(w.opts.MaxConcurrentActivityExecutionSize, 1000)
sdkoptions.WorkerActivitiesPerSecond = w.opts.WorkerActivitiesPerSecond
sdkoptions.MaxConcurrentLocalActivityExecutionSize = cmp.Or(w.opts.MaxConcurrentLocalActivityExecutionSize, 1000)
sdkoptions.WorkerLocalActivitiesPerSecond = w.opts.WorkerLocalActivitiesPerSecond
sdkoptions.MaxConcurrentActivityTaskPollers = max(cmp.Or(w.opts.MaxConcurrentActivityTaskPollers, 2), 2)
sdkoptions.MaxConcurrentWorkflowTaskExecutionSize = cmp.Or(w.opts.MaxConcurrentWorkflowTaskExecutionSize, 1000)
sdkoptions.MaxConcurrentWorkflowTaskPollers = max(cmp.Or(w.opts.MaxConcurrentWorkflowTaskPollers, 2), 2)
sdkoptions.StickyScheduleToStartTimeout = w.opts.StickyScheduleToStartTimeout
sdkoptions.BackgroundActivityContext = headers.SetCallerInfo(context.Background(), headers.NewBackgroundHighCallerInfo(nsName))
sdkoptions.Identity = fmt.Sprintf("temporal-system@%s@%s", w.wm.hostName, nsName)
// increase these if we're supposed to run with more allocation
sdkoptions.MaxConcurrentWorkflowTaskPollers *= allocation.local
sdkoptions.MaxConcurrentActivityTaskPollers *= allocation.local
sdkoptions.MaxConcurrentLocalActivityExecutionSize *= allocation.local
sdkoptions.MaxConcurrentWorkflowTaskExecutionSize *= allocation.local
sdkoptions.MaxConcurrentActivityExecutionSize *= allocation.local
sdkoptions.OnFatalError = w.onFatalError
// this should not block because the client already has server capabilities
worker := w.wm.sdkClientFactory.NewWorker(client, w.wm.taskQueueName, sdkoptions)
details := workercommon.RegistrationDetails{
TotalWorkers: allocation.total,
Multiplicity: allocation.local,
}
for _, cmp := range components {
cleanup := cmp.Register(worker, w.ns, details)
if cleanup != nil {
}
}
// this blocks by calling DescribeNamespace a few times (with a 10s timeout)
if err != nil {
return nil, nil, err
}
return client, worker, nil
}
w.lock.Lock()
defer w.lock.Unlock()
w.stopWorkerLocked()
w.retrier.Reset()
// Note that we only reset reserved here, not in stopWorkerLocked: if we did it there, we
// would take a rate limiter token on each retry after failure. Failure to start the worker
// means we probably didn't do any polls yet, which is the main reason for the rate limit,
// so this it's okay to use only the backoff timer in that case.
w.reserved = false
if w.retryTimer != nil {
w.retryTimer = nil
}
}
for _, cleanup := range w.cleanup {
}
if w.worker != nil {
w.worker.Stop()
w.worker = nil
}
w.client.Close()
w.client = nil
}
}
chasmRegistry *chasm.Registry,
testHooks testhooks.TestHooks,
if hook, ok := testhooks.Get(
testHooks,
testhooks.HistoryChasmRuntimeProvider,
testhooks.GlobalScope,
); ok {
hook(chasmEngine, chasmVisibilityManager, chasmRegistry)
}
)
return grpc.NewServer(grpcServerOptions...)
}
func HistoryServiceServerProvider(handler *Handler) historyservice.HistoryServiceServer {
fx.go
return handler
}
func ServiceResolverProvider(
membershipMonitor membership.Monitor,
return membershipMonitor.GetResolver(primitives.HistoryService)
}
handler := &Handler{
status: common.DaemonStatusInitialized,
config: args.Config,
nexusCompletionHandler: args.NexusCompletionHandler,
tokenSerializer: tasktoken.NewSerializer(),
deepHealthCheckHandler: deepHealthCheckHandler{
healthServer: args.HealthServer,
metricsHandler: args.MetricsHandler,
config: args.Config,
historyHealthSignal: args.HistoryHealthSignal,
persistenceHealthSignal: args.PersistenceHealthSignal,
startupTime: time.Now(),
},
logger: args.Logger,
throttledLogger: args.ThrottledLogger,
persistenceExecutionManager: args.PersistenceExecutionManager,
persistenceShardManager: args.PersistenceShardManager,
persistenceVisibilityManager: args.PersistenceVisibilityManager,
historyServiceResolver: args.HistoryServiceResolver,
metricsHandler: args.MetricsHandler,
payloadSerializer: args.PayloadSerializer,
timeSource: args.TimeSource,
namespaceRegistry: args.NamespaceRegistry,
saProvider: args.SaProvider,
clusterMetadata: args.ClusterMetadata,
archivalMetadata: args.ArchivalMetadata,
hostInfoProvider: args.HostInfoProvider,
controller: args.ShardController,
eventNotifier: args.EventNotifier,
tracer: args.TracerProvider.Tracer(consts.LibraryName),
taskQueueManager: args.TaskQueueManager,
taskCategoryRegistry: args.TaskCategoryRegistry,
dlqMetricsEmitter: args.DLQMetricsEmitter,
chasmEngine: args.ChasmEngine,
chasmRegistry: args.ChasmRegistry,
testHooks: args.TestHooks,
replicationTaskFetcherFactory: args.ReplicationTaskFetcherFactory,
replicationTaskConverterProvider: args.ReplicationTaskConverterFactory,
streamReceiverMonitor: args.StreamReceiverMonitor,
replicationServerRateLimiter: args.ReplicationServerRateLimiter,
}
// Build the Nexus handler in OnStart rather than here so that it runs after all
// fx.Invoke functions have completed. If we built it eagerly, the dependency chain
//
// activity.HistoryModule (fx.Invoke)
// → *library → *handler → historyservice.HistoryServiceServer
// → HistoryServiceServerProvider → HandlerProvider (this function)
//
// would force HandlerProvider to run before modules like chasmtests.Module have had
// a chance to register their nexus services via their own fx.Invoke calls. As a
// result, buildNexusHandler would snapshot an empty registry and h.nexusHandler
// would remain nil, causing all StartNexusOperation calls to the system endpoint to
// return "no nexus services registered". OnStart hooks run after ALL invokes are
// done, so the registry is fully populated by the time we call buildNexusHandler.
lc.Append(fx.Hook{
OnStart: func(_ context.Context) error {
if err != nil {
return err
}
return nil
},
})
}
nexusServices := chasmRegistry.NexusServices()
if len(nexusServices) == 0 {
return nil, nil
}
for _, svc := range nexusServices {
// No chance of collision here since the registry would have errored out earlier.
serviceRegistry.MustRegister(svc)
}
}
func HistoryEngineFactoryProvider(
params HistoryEngineFactoryParams,
return &historyEngineFactory{
HistoryEngineFactoryParams: params,
}
}
func ConfigProvider(
dc *dynamicconfig.Collection,
persistenceConfig config.Persistence,
return configs.NewConfig(
dc,
persistenceConfig.NumHistoryShards,
)
}
func ServiceErrorInterceptorProvider(
dc *dynamicconfig.Collection,
return interceptor.NewServiceErrorInterceptor(
dynamicconfig.MaxServiceErrorMessageLength.Get(dc),
)
}
func ThrottledLoggerRpsFnProvider(serviceConfig *configs.Config) resource.ThrottledLoggerRpsFn {
fx.go
return func() float64 { return float64(serviceConfig.ThrottledLogRPS()) }
}
return interceptor.NewRetryableInterceptor(
common.CreateHistoryHandlerRetryPolicy(),
api.IsRetryableError,
)
}
func ErrorHandlerProvider(
logger log.Logger,
serviceConfig *configs.Config,
return interceptor.NewRequestErrorHandler(
logger,
serviceConfig.LogAllReqErrors,
)
}
func TelemetryInterceptorProvider(
serviceConfig *configs.Config,
requestErrorHandler *interceptor.RequestErrorHandler,
return interceptor.NewTelemetryInterceptor(
namespaceRegistry,
metricsHandler,
logger,
serviceConfig.LogAllReqErrors,
requestErrorHandler,
)
}
func HealthSignalAggregatorProvider(
dynamicCollection *dynamicconfig.Collection,
logger log.ThrottledLogger,
return interceptor.NewHealthSignalAggregator(
logger,
dynamicconfig.HistoryHealthSignalMetricsEnabled.Get(dynamicCollection),
dynamicconfig.HistoryHealthSignalUsePercentiles.Get(dynamicCollection),
dynamicconfig.PersistenceHealthSignalWindowSize.Get(dynamicCollection)(),
dynamicconfig.PersistenceHealthSignalBufferSize.Get(dynamicCollection)(),
dynamicconfig.HistoryHealthSignalLatencyWindowSize.Get(dynamicCollection)(),
dynamicconfig.HistoryHealthSignalLatencyWindowCount.Get(dynamicCollection)(),
)
}
func HealthCheckInterceptorProvider(
healthSignalAggregator interceptor.HealthSignalAggregator,
return interceptor.NewHealthCheckInterceptor(
healthSignalAggregator,
)
}
func ContextMetadataInterceptorProvider(logger log.Logger) *interceptor.ContextMetadataInterceptor {
fx.go
return interceptor.NewContextMetadataInterceptor(true, logger)
}
func HistoryAdditionalInterceptorsProvider(
chasmRequestEngineInterceptor *chasm.ChasmEngineInterceptor,
chasmRequestVisibilityInterceptor *chasm.ChasmVisibilityInterceptor,
return []grpc.UnaryServerInterceptor{
healthCheckInterceptor.UnaryIntercept,
chasmRequestEngineInterceptor.Intercept,
chasmRequestVisibilityInterceptor.Intercept,
}
}
func NamespaceRateLimitInterceptorProvider(
namespaceRegistry namespace.Registry,
metricsHandler metrics.Handler,
namespaceRateFn := func(namespaceName string) float64 {
if namespaceRPS := serviceConfig.NamespaceRPS(namespaceName); namespaceRPS > 0 {
return float64(namespaceRPS)
}
namespaceRegistry,
configs.NewNamespaceRateLimiter(
namespaceRateFn,
serviceConfig.OperatorRPSRatio,
),
map[string]int{}, // no token overrides
map[string]struct{}{}, // no long polls on history service
dynamicconfig.GetBoolPropertyFnFilteredByNamespace(false), // no long poll methods
metricsHandler,
)
}
func RateLimitInterceptorProvider(
serviceConfig *configs.Config,
return interceptor.NewRateLimitInterceptor(
configs.NewPriorityRateLimiter(func() float64 { return float64(serviceConfig.RPS()) }, serviceConfig.OperatorRPSRatio),
map[string]int{
healthpb.Health_Check_FullMethodName: 0, // exclude health check requests from rate limiting.
func ESProcessorConfigProvider(
serviceConfig *configs.Config,
return &elasticsearch.ProcessorConfig{
IndexerConcurrency: serviceConfig.IndexerConcurrency,
ESProcessorNumOfWorkers: serviceConfig.ESProcessorNumOfWorkers,
ESProcessorBulkActions: serviceConfig.ESProcessorBulkActions,
ESProcessorBulkSize: serviceConfig.ESProcessorBulkSize,
ESProcessorFlushInterval: serviceConfig.ESProcessorFlushInterval,
ESProcessorAckTimeout: serviceConfig.ESProcessorAckTimeout,
}
}
func PersistenceRateLimitingParamsProvider(
ownershipBasedQuotaScaler shard.LazyLoadedOwnershipBasedQuotaScaler,
logger log.SnTaggedLogger,
hostCalculator := calculator.NewLoggedCalculator(
shard.NewOwnershipAwareQuotaCalculator(
ownershipBasedQuotaScaler,
persistenceLazyLoadedServiceResolver,
serviceConfig.PersistenceMaxQPS,
serviceConfig.PersistenceGlobalMaxQPS,
),
log.With(logger, tag.ComponentPersistence, tag.ScopeHost),
)
namespaceCalculator := calculator.NewLoggedNamespaceCalculator(
shard.NewOwnershipAwareNamespaceQuotaCalculator(
ownershipBasedQuotaScaler,
persistenceLazyLoadedServiceResolver,
serviceConfig.PersistenceNamespaceMaxQPS,
serviceConfig.PersistenceGlobalNamespaceMaxQPS,
),
log.With(logger, tag.ComponentPersistence, tag.ScopeNamespace),
)
return service.PersistenceRateLimitingParams{
PersistenceMaxQps: func() int {
return int(hostCalculator.GetQuota())
},
PersistenceNamespaceMaxQps: func(namespace string) int {
return int(namespaceCalculator.GetQuota(namespace))
chasmRegistry *chasm.Registry,
serializer serialization.Serializer,
return visibility.NewManager(
*persistenceConfig,
persistenceServiceResolver,
customVisibilityStoreFactory,
esProcessorConfig,
saProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
serviceConfig.VisibilityPersistenceMaxReadQPS,
serviceConfig.VisibilityPersistenceMaxWriteQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.VisibilityPersistenceSlowQueryThreshold,
serviceConfig.EnableReadFromSecondaryVisibility,
serviceConfig.VisibilityEnableShadowReadMode,
serviceConfig.SecondaryVisibilityWritingMode,
serviceConfig.VisibilityDisableOrderByClause,
serviceConfig.VisibilityEnableManualPagination,
serviceConfig.VisibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
}
func ChasmVisibilityManagerProvider(
metricsHandler metrics.Handler,
config *configs.Config,
return events.NewNotifier(
timeSource,
metricsHandler,
config.GetShardID,
)
}
lc.Append(fx.StartStopHook(svc.Start, svc.Stop))
}
func ReplicationProgressCacheProvider(
logger log.Logger,
handler metrics.Handler,
return replication.NewProgressCache(serviceConfig, logger, handler)
}
func VersionMembershipCacheProvider(
serviceConfig *configs.Config,
metricsHandler metrics.Handler,
c := commoncache.New(serviceConfig.VersionMembershipCacheMaxSize(), &commoncache.Options{
TTL: max(1*time.Second, serviceConfig.VersionMembershipCacheTTL()),
})
lc.Append(fx.Hook{
OnStop: func(context.Context) error {
return nil
},
})
return worker_versioning.NewVersionMembershipAndReactivationStatusCache(c, metricsHandler)
fx.go
}
serviceConfig *configs.Config,
metricsHandler metrics.Handler,
c := commoncache.New(serviceConfig.RoutingInfoCacheMaxSize(), &commoncache.Options{
TTL: max(1*time.Second, serviceConfig.RoutingInfoCacheTTL()),
})
lc.Append(fx.Hook{
OnStop: func(context.Context) error {
return nil
},
})
}
fx.Provide(SearchAttributeManagerProvider),
fx.Provide(NamespaceRegistryProvider),
fx.Provide(func() namespace.NamespaceStateChangedFn { return nsregistry.DefaultNamespaceStateChanged }),
fx.go
nsregistry.RegistryLifetimeHooksModule,
fx.Provide(fx.Annotate(
fx.ResultTags(`group:"deadlockDetectorRoots"`),
)),
)
func DefaultSnTaggedLoggerProvider(logger log.Logger, sn primitives.ServiceName) log.SnTaggedLogger {
fx.go
return log.With(logger, tag.Service(sn))
}
func ThrottledLoggerProvider(
logger log.SnTaggedLogger,
fn ThrottledLoggerRpsFn,
return log.NewThrottledLogger(
logger,
quotas.RateFn(fn),
)
}
return factory.GetGRPCListener()
}
hn, err := os.Hostname()
return HostName(hn), err
}
return clock.NewRealTimeSource()
}
func SearchAttributeMapperProviderProvider(
searchAttributeProvider searchattribute.Provider,
persistenceConfig *config.Persistence,
primaryVisibilityStoreConfig := persistenceConfig.GetVisibilityStoreConfig()
return searchattribute.NewMapperProvider(
saMapper,
namespaceRegistry,
searchAttributeProvider,
primaryVisibilityStoreConfig.GetIndexName(),
)
}
func SearchAttributeProviderProvider(
cmMgr persistence.ClusterMetadataManager,
dynamicCollection *dynamicconfig.Collection,
return searchattribute.NewManager(
timeSource,
cmMgr,
logger,
dynamicconfig.ForceSearchAttributesCacheRefreshOnRead.Get(dynamicCollection))
}
func SearchAttributeManagerProvider(
cmMgr persistence.ClusterMetadataManager,
dynamicCollection *dynamicconfig.Collection,
return searchattribute.NewManager(
timeSource,
cmMgr,
logger,
dynamicconfig.ForceSearchAttributesCacheRefreshOnRead.Get(dynamicCollection))
}
// SearchAttributeValidatorProvider creates a new search attribute validator with the given dependencies. It configures
metricsHandler metrics.Handler,
logger log.Logger,
return searchattribute.NewValidator(
saProvider,
saMapperProvider,
dynamicconfig.SearchAttributesNumberOfKeysLimit.Get(dynamicCollection),
dynamicconfig.SearchAttributesSizeOfValueLimit.Get(dynamicCollection),
dynamicconfig.SearchAttributesTotalSizeLimit.Get(dynamicCollection),
visibilityMgr,
visibility.AllowListForValidation(
visibilityMgr.GetStoreNames(),
dynamicconfig.VisibilityAllowList.Get(dynamicCollection),
),
dynamicconfig.SuppressErrorSetSystemSearchAttribute.Get(dynamicCollection),
metricsHandler,
logger,
)
}
type NamespaceRegistryParams struct {
}
return nsregistry.NewRegistry(
params.MetadataManager,
params.ClusterMetadata.IsGlobalNamespaceEnabled(),
params.ClusterMetadata.GetCurrentClusterName(),
dynamicconfig.NamespaceCacheRefreshInterval.Get(params.DynamicCollection),
dynamicconfig.ForceSearchAttributesCacheRefreshOnRead.Get(params.DynamicCollection),
params.MetricsHandler,
params.Logger,
params.ReplicationResolverFactory,
params.NamespaceStateChangedFn,
)
}
func ClientFactoryProvider(
logger log.SnTaggedLogger,
throttledLogger log.ThrottledLogger,
return factoryProvider.NewFactory(
rpcFactory,
membershipMonitor,
metricsHandler,
dynamicCollection,
testHooks,
persistenceConfig.NumHistoryShards,
logger,
throttledLogger,
)
}
func ClientBeanProvider(
clientFactory client.Factory,
clusterMetadata cluster.Metadata,
bean, err := client.NewClientBean(
clientFactory,
clusterMetadata,
)
if err != nil {
return nil, err
}
// Deterministically release the bean's clients (daemon goroutines and
// cached gRPC connections) on shutdown.
return bean, nil
}
func FrontendClientProvider(clientBean client.Bean) workflowservice.WorkflowServiceClient {
fx.go
frontendRawClient := clientBean.GetFrontendClient()
return frontend.NewRetryableClient(
frontendRawClient,
common.CreateFrontendClientRetryPolicy(),
common.IsServiceClientTransientError,
)
}
func AdminClientProvider(clientBean client.Bean, clusterMetadata cluster.Metadata) (adminservice.AdminServiceClient, error) {
fx.go
adminRawClient, err := clientBean.GetRemoteAdminClient(clusterMetadata.GetCurrentClusterName())
if err != nil {
return nil, err
}
adminRawClient,
common.CreateFrontendClientRetryPolicy(),
common.IsServiceClientTransientError,
), nil
}
func RuntimeMetricsReporterProvider(
params RuntimeMetricsReporterParams,
return metrics.NewRuntimeMetricsReporter(
params.MetricHandler,
time.Minute,
params.Logger,
string(params.InstanceID),
)
}
return clientBean.GetHistoryClient()
}
func HistoryClientProvider(historyRawClient HistoryRawClient, dc *dynamicconfig.Collection) HistoryClient {
fx.go
return history.NewRetryableClient(
historyRawClient,
common.CreateHistoryClientRetryPolicy(dynamicconfig.RetryUnboundedOnSystemResourceExhausted.Get(dc)),
common.IsServiceClientTransientError,
)
}
func MatchingRawClientProvider(
clientBean client.Bean,
namespaceRegistry namespace.Registry,
return clientBean.GetMatchingClient(namespaceRegistry.GetNamespaceName)
}
func MatchingClientProvider(matchingRawClient MatchingRawClient, dc *dynamicconfig.Collection) MatchingClient {
fx.go
return matching.NewRetryableClient(
matchingRawClient,
common.CreateMatchingClientRetryPolicy(dynamicconfig.RetryUnboundedOnSystemResourceExhausted.Get(dc)),
common.CreateMatchingClientLongPollRetryPolicy(),
common.IsServiceClientTransientError,
)
}
func PersistenceConfigProvider(persistenceConfig config.Persistence, dc *dynamicconfig.Collection) *config.Persistence {
fx.go
persistenceConfig.TransactionSizeLimit = dynamicconfig.TransactionSizeLimit.Get(dc)
return &persistenceConfig
}
func ArchivalMetadataProvider(dc *dynamicconfig.Collection, cfg *config.Config) archiver.ArchivalMetadata {
fx.go
return archiver.NewArchivalMetadata(
dc,
cfg.Archival.History.State,
cfg.Archival.History.EnableRead,
cfg.Archival.Visibility.State,
cfg.Archival.Visibility.EnableRead,
&cfg.NamespaceDefaults.Archival,
)
}
func ArchiverProviderProvider(
logger log.SnTaggedLogger,
metricsHandler metrics.Handler,
return provider.NewArchiverProvider(
cfg.Archival.History.Provider,
cfg.Archival.Visibility.Provider,
customHistoryArchiverFactory,
customVisibilityArchiverFactory,
persistenceExecutionManager,
logger,
metricsHandler,
)
}
func SdkClientFactoryProvider(
resolver *membership.GRPCResolver,
dc *dynamicconfig.Collection,
frontendURL, _, _, frontendTLSConfig, err := getFrontendConnectionDetails(cfg, tlsConfigProvider, resolver)
if err != nil {
return nil, err
}
frontendURL,
frontendTLSConfig,
metricsHandler,
logger,
dynamicconfig.WorkerStickyCacheSize.Get(dc),
), nil
}
return cfg.DCRedirectionPolicy
}
func PerServiceDialOptionsProvider(
logger log.SnTaggedLogger,
trailerInterceptor := interceptor.TrailerToContextMetadataInterceptor(logger)
dialOpt := grpc.WithChainUnaryInterceptor(trailerInterceptor)
return map[primitives.ServiceName][]grpc.DialOption{
primitives.HistoryService: {dialOpt},
primitives.MatchingService: {dialOpt},
}
}
func RPCFactoryProvider(
dc *dynamicconfig.Collection,
tokenProvider auth.TokenProvider,
frontendURL, frontendHTTPURL, frontendHTTPPort, frontendTLSConfig, err := getFrontendConnectionDetails(cfg, tlsConfigProvider, resolver)
if err != nil {
return nil, err
}
if tracingStatsHandler != nil {
options = append(options, grpc.WithStatsHandler(tracingStatsHandler))
}
enableClientKeepalive := dynamicconfig.EnableInternodeClientKeepAlive.Get(dc)()
factory := rpc.NewFactory(
cfg,
svcName,
logger,
metricsHandler,
tlsConfigProvider,
frontendURL,
frontendHTTPURL,
frontendHTTPPort,
frontendTLSConfig,
options,
perServiceDialOptions,
monitor,
tokenProvider,
)
factory.EnableInternodeServerKeepalive = enableServerKeepalive
factory.EnableInternodeClientKeepalive = enableClientKeepalive
logger.Debug(fmt.Sprintf("RPC factory created. enableServerKeepalive: %v, enableClientKeepalive: %v", enableServerKeepalive, enableClientKeepalive))
return factory, nil
}
metadata cluster.Metadata,
tlsConfigProvider encryption.TLSConfigProvider,
return cluster.NewFrontendHTTPClientCache(metadata, tlsConfigProvider)
}
func getFrontendConnectionDetails(
tlsConfigProvider encryption.TLSConfigProvider,
resolver *membership.GRPCResolver,
// To simplify the static config, we switch default values based on whether the config
// defines an "internal-frontend" service. The default for TLS config can be overridden
// with publicClient.forceTLSConfig.
_, hasIFE := cfg.Services[string(primitives.InternalFrontendService)]
forceTLS := cfg.PublicClient.ForceTLSConfig
if forceTLS == config.ForceTLSConfigAuto {
if hasIFE {
forceTLS = config.ForceTLSConfigInternode
forceTLS = config.ForceTLSConfigFrontend
}
}
var err error
switch forceTLS {
case config.ForceTLSConfigInternode:
frontendTLSConfig, err = tlsConfigProvider.GetInternodeClientConfig()
frontendTLSConfig, err = tlsConfigProvider.GetFrontendClientConfig()
default:
err = fmt.Errorf("invalid forceTLSConfig")
}
return "", "", 0, nil, fmt.Errorf("unable to load TLS configuration: %w", err)
}
if frontendURL == "" {
if hasIFE {
frontendURL = resolver.MakeURL(primitives.InternalFrontendService)
}
}
if frontendHTTPURL == "" {
if hasIFE {
frontendHTTPURL = resolver.MakeURL(primitives.InternalFrontendService)
frontendHTTPURL = resolver.MakeURL(primitives.FrontendService)
}
}
if hasIFE {
frontendHTTPPort = cfg.Services[string(primitives.InternalFrontendService)].RPC.HTTPPort
frontendHTTPPort = cfg.Services[string(primitives.FrontendService)].RPC.HTTPPort
}
}
replicationResolverFactory namespace.ReplicationResolverFactory,
namespaceStateChangedFn namespace.NamespaceStateChangedFn,
return ®istry{
persistence: aPersistence,
globalNamespacesEnabled: enableGlobalNamespaces,
currentClusterName: currentClusterName,
clock: clock.NewRealTimeSource(),
metricsHandler: metricsHandler.WithTags(metrics.OperationTag(metrics.NamespaceCacheScope)),
logger: logger,
nameToID: make(map[namespace.Name]namespace.ID),
idToNamespace: make(map[namespace.ID]*namespace.Namespace),
refreshInterval: refreshInterval,
readthroughNotFoundCache: cache.New(readthroughCacheSize, &readthroughNotFoundCacheOpts),
forceSearchAttributesCacheRefreshOnRead: forceSearchAttributesCacheRefreshOnRead,
replicationResolverFactory: replicationResolverFactory,
namespaceStateChangedFn: namespaceStateChangedFn,
}
}
// DefaultNamespaceStateChanged is the default implementation that checks whether a namespace
// state change is significant enough to trigger callbacks.
func DefaultNamespaceStateChanged(currentClusterName string, oldNS *namespace.Namespace, newNS *namespace.Namespace) bool {
registry.go
return oldNS == nil ||
oldNS.State() != newNS.State() ||
oldNS.Name() != newNS.Name() ||
oldNS.IsGlobalNamespace() != newNS.IsGlobalNamespace() ||
//nolint:forbidigo // ns-wide state diff for cache invalidation.
oldNS.ActiveInCluster(currentClusterName) != newNS.ActiveInCluster(currentClusterName) ||
oldNS.ReplicationState("") != newNS.ReplicationState("")
}
// GetRegistrySize observes the size of the by-name and by-ID maps.
// arrive. If not supported, falls back to periodic polling. Start blocks until the initial namespace refresh completes.
// The initial refresh must succeed or the function will fatal.
ctx := headers.SetCallerInfo(
context.Background(),
headers.SystemBackgroundHighCallerInfo,
)
watchStarted := false
r.refresher, watchStarted = r.runWatchLoop(ctx)
if watchStarted {
// Watch started successfully
return
}
// Watch failed to start for a reason other than ErrWatchNotSupported
metrics.NamespaceRegistryWatchStartFailures.With(r.metricsHandler).Record(1)
r.logger.Warn("Unable to start namespace watch - falling back to polling", tag.Error(err))
r.logger.Info("Watch not supported by persistence, namespace registry will use polling")
}
// Fall back to polling
r.logger.Fatal("Unable to initialize namespace registry", tag.Error(err))
}
}
// Stop ends background refresh. Should only be invoked by fx lifecycle hook.
// Should not be called multiple times or concurrently with Start().
// refresher may be nil if watch failed to start and we're shutting down.
if r.refresher != nil {
r.refresher.Cancel()
<-r.refresher.Done()
}
}
return []pingable.Check{
{
Name: "namespace registry lock",
// we don't do any persistence ops, this shouldn't be blocked
Timeout: 10 * time.Second,
Ping: func() []pingable.Pingable {
// just checking if we can acquire the lock
r.nsMapsLock.Lock()
// nolint:staticcheck
r.nsMapsLock.Unlock()
return nil
},
MetricsName: metrics.DDNamespaceRegistryLockLatency.Name(),
},
}
r.nsMapsLock.RLock()
defer r.nsMapsLock.RUnlock()
return expmaps.Values(r.idToNamespace)
}
func (r *registry) RegisterStateChangeCallback(key any, cb namespace.StateChangeCallbackFn) {
registry.go
// Store callback first to avoid race where watch events arrive between reading the namespace snapshot and storing the
// callback. This ensures no events are missed, but introduces a different trade-off: The callback may receive duplicate
// calls for the same namespace if a watch event arrives while we're iterating through the catch-up loop below. For
// example:
// 1. Callback is stored in stateChangeCallbacks
// 2. Watch event arrives for namespace X, callback is invoked
// 3. Catch-up loop reaches namespace X, callback is invoked again
//
// This is acceptable because callbacks are rarely added (so unlikely to trigger this) and callbacks should be idempotent anyway.
callbackWithTiming := func(ns *namespace.Namespace, deletedFromDb bool) {
// Track callback duration so we can identify slow callbacks
start := time.Now()
defer func() {
duration := time.Since(start)
if duration > slowCallbackDuration {
metrics.NamespaceRegistrySlowCallbacks.With(r.metricsHandler).Record(1)
r.logger.Warn(
}()
}
r.stateChangeCallbacks.Store(key, namespace.StateChangeCallbackFn(callbackWithTiming))
registry.go
r.nsMapsLock.RLock()
allNamespaces := expmaps.Values(r.idToNamespace)
r.nsMapsLock.RUnlock()
// call once for each namespace already in the registry
for _, ns := range allNamespaces {
}
}
r.stateChangeCallbacks.Delete(key)
}
// GetNamespace retrieves the information from the internal maps if it exists, otherwise retrieves the information from metadata
// store and update internal entries with an expiry before returning back
func (r *registry) GetNamespace(name namespace.Name) (*namespace.Namespace, error) {
registry.go
if name == "" {
return nil, serviceerror.NewInvalidArgument("Namespace is empty.")
}
}
// GetNamespaceWithOptions retrieves a namespace entry by name, with behavior controlled by options.
func (r *registry) GetNamespaceWithOptions(name namespace.Name, opts namespace.GetNamespaceOptions) (*namespace.Namespace, error) {
registry.go
if name == "" {
return nil, serviceerror.NewInvalidArgument("Namespace is empty.")
}
return r.getNamespace(name)
}
return r.getOrReadthroughNamespace(name)
}
// On initial startup (initialWatch=true), retries are limited to avoid blocking server startup indefinitely.
// On reconnection after a previous success (initialWatch=false), retries continue indefinitely.
policy := backoff.NewExponentialRetryPolicy(CacheRefreshFailureRetryInterval)
if initialWatch {
return policy.WithMaximumAttempts(startWatchMaxAttempts)
}
return policy.WithExpirationInterval(backoff.NoInterval)
}
// Uses ShutdownOnce to track whether the watch has ever started successfully, which affects retry behavior: limited
// retries on initial startup, unlimited on reconnection.
// watchStartedOnce tracks whether the watch has ever started successfully.
// Used to determine retry policy and signal to the caller when watch is ready.
watchStartedOnce := channel.NewShutdownOnce()
handle := goro.NewHandle(ctx).Go(
func(ctx context.Context) error {
// Outer loop handles watch restarts after connection failures.
for {
select {
case <-ctx.Done():
return nil
}
if err != nil {
}
watchStartedOnce.Shutdown()
// Wait for either the watch to start successfully, or the goroutine to exit (due to error
// or because watch is not supported). Return true only if watch started successfully.
case <-watchStartedOnce.Channel():
return handle, true
return handle, false
}
}
// startWatch attempts to establish a namespace watch with retries.
// Returns the watch channel and context on success.
func (r *registry) startWatch(ctx context.Context, initialWatch bool) (watchStartResult, error) {
registry.go
return backoff.ThrottleRetryContextWithReturn(
ctx,
func(ctx context.Context) (startResult watchStartResult, err error) {
// Create fresh watch context for this attempt
watchCtx, watchCancel := context.WithCancel(ctx)
defer func() {
if err != nil {
watchCancel()
}
}()
startResult.watchCancel = watchCancel
if startResult.eventCh, err = r.persistence.WatchNamespaces(watchCtx); err != nil {
r.logger.Error("Error starting namespace watch", tag.Error(err))
}
}
},
watchStartRetryPolicy(initialWatch),
return !errors.Is(err, persistence.ErrWatchNotSupported)
},
)
}
// runPollingLoop periodically refreshes the namespace cache.
// Used as fallback when namespace watches are not supported.
timer := time.NewTimer(r.refreshInterval())
for {
select {
return nil
case <-timer.C:
}
start := time.Now()
defer func() {
if err != nil {
metrics.NamespaceRegistryRefreshFailures.With(r.metricsHandler).Record(1)
}
metrics.NamespaceRegistryRefreshLatency.With(r.metricsHandler).Record(time.Since(start))
registry.go
}()
PageSize: CacheRefreshPageSize,
IncludeDeleted: true,
}
var namespacesDb namespace.Namespaces
namespaceIDsDb := make(map[namespace.ID]struct{})
for {
// TODO: consider adding a timeout and/or retries here - long ListNamespaces
// calls could delay watch reconnection or block shutdown
response, err := r.persistence.ListNamespaces(ctx, request)
if err != nil {
return err
}
namespaceDb.Namespace,
r.replicationResolverFactory(namespaceDb.Namespace),
namespace.WithGlobalFlag(namespaceDb.IsGlobalNamespace),
namespace.WithNotificationVersion(namespaceDb.NotificationVersion),
)
if err != nil {
return err
}
namespaceIDsDb[namespace.ID(namespaceDb.Namespace.Info.Id)] = struct{}{}
}
break
}
request.NextPageToken = response.NextPageToken
// Make a copy of the existing namespace maps (excluding deleted), so we can calculate diff and do atomic swap.
newIDToNamespace := make(map[namespace.ID]*namespace.Namespace)
var deletedEntries []*namespace.Namespace
for _, ns := range r.GetAllNamespaces() {
if _, namespaceExistsDb := namespaceIDsDb[ns.ID()]; !namespaceExistsDb {
deletedEntries = append(deletedEntries, ns)
}
for _, aNamespace := range namespacesDb {
// If namespace was renamed, remove entry for the old name
if oldNS != nil && oldNS.Name() != aNamespace.Name() {
delete(newNameToID, oldNS.Name())
}
if r.namespaceStateChanged(oldNS, aNamespace) {
stateChanged = append(stateChanged, aNamespace)
}
}
totalNamespaceCount := len(newIDToNamespace) // record metric value within lock boundary
r.idToNamespace = newIDToNamespace
r.nameToID = newNameToID
stateChanged = append(stateChanged, r.stateChangedDuringReadthrough...)
r.stateChangedDuringReadthrough = nil
r.nsMapsLock.Unlock()
metrics.TotalNamespaces.With(r.metricsHandler).Record(float64(totalNamespaceCount))
r.stateChangeCallbacks.Range(
func(_, value any) bool {
cb := value.(namespace.StateChangeCallbackFn)
for _, ns := range deletedEntries {
cb(ns, true)
}
}
})
}
id namespace.ID,
newNS *namespace.Namespace,
oldNS := iDToNamespace[id]
iDToNamespace[id] = newNS
return oldNS
}
// getNamespace retrieves the information from the cache if it exists
func (r *registry) getNamespace(name namespace.Name) (*namespace.Namespace, error) {
registry.go
r.nsMapsLock.RLock()
defer r.nsMapsLock.RUnlock()
if id, ok := r.nameToID[name]; ok {
}
return nil, serviceerror.NewNamespaceNotFound(name.String())
}
}
func (r *registry) getNamespaceByIDLocked(id namespace.ID) (*namespace.Namespace, error) {
registry.go
if ns, ok := r.idToNamespace[id]; ok {
}
return nil, serviceerror.NewNamespaceNotFound(id.String())
}
// getOrReadthroughNamespace returns namespace information if it exists or reads through
// to the persistence layer and updates internal entry if it doesn't
func (r *registry) getOrReadthroughNamespace(name namespace.Name) (*namespace.Namespace, error) {
registry.go
// check main caches
ns, err := r.getNamespace(name)
if err == nil {
}
r.readthroughLock.Lock()
}
func (r *registry) namespaceStateChanged(oldNS *namespace.Namespace, newNS *namespace.Namespace) bool {
registry.go
return r.namespaceStateChangedFn(r.currentClusterName, oldNS, newNS)
}
dc *dynamicconfig.Collection,
numHistoryShards int32,
return &Config{
NumHistoryShards: numHistoryShards,
PersistenceMaxQPS: dynamicconfig.FrontendPersistenceMaxQPS.Get(dc),
PersistenceGlobalMaxQPS: dynamicconfig.FrontendPersistenceGlobalMaxQPS.Get(dc),
PersistenceNamespaceMaxQPS: dynamicconfig.FrontendPersistenceNamespaceMaxQPS.Get(dc),
PersistenceGlobalNamespaceMaxQPS: dynamicconfig.FrontendPersistenceGlobalNamespaceMaxQPS.Get(dc),
PersistencePerShardNamespaceMaxQPS: dynamicconfig.DefaultPerShardNamespaceRPSMax,
PersistenceDynamicRateLimitingParams: dynamicconfig.FrontendPersistenceDynamicRateLimitingParams.Get(dc),
PersistenceQPSBurstRatio: dynamicconfig.PersistenceQPSBurstRatio.Get(dc),
VisibilityPersistenceMaxReadQPS: dynamicconfig.VisibilityPersistenceMaxReadQPS.Get(dc),
VisibilityPersistenceMaxWriteQPS: dynamicconfig.VisibilityPersistenceMaxWriteQPS.Get(dc),
VisibilityPersistenceSlowQueryThreshold: dynamicconfig.VisibilityPersistenceSlowQueryThreshold.Get(dc),
VisibilityMaxPageSize: dynamicconfig.FrontendVisibilityMaxPageSize.Get(dc),
EnableReadFromSecondaryVisibility: dynamicconfig.EnableReadFromSecondaryVisibility.Get(dc),
VisibilityEnableShadowReadMode: dynamicconfig.VisibilityEnableShadowReadMode.Get(dc),
VisibilityDisableOrderByClause: dynamicconfig.VisibilityDisableOrderByClause.Get(dc),
VisibilityEnableManualPagination: dynamicconfig.VisibilityEnableManualPagination.Get(dc),
VisibilityEnableUnifiedQueryConverter: dynamicconfig.VisibilityEnableUnifiedQueryConverter.Get(dc),
VisibilityAllowList: dynamicconfig.VisibilityAllowList.Get(dc),
SuppressErrorSetSystemSearchAttribute: dynamicconfig.SuppressErrorSetSystemSearchAttribute.Get(dc),
HistoryMaxPageSize: dynamicconfig.FrontendHistoryMaxPageSize.Get(dc),
RPS: dynamicconfig.FrontendRPS.Get(dc),
GlobalRPS: dynamicconfig.FrontendGlobalRPS.Get(dc),
OperatorRPSRatio: dynamicconfig.OperatorRPSRatio.Get(dc),
NamespaceReplicationInducingAPIsRPS: dynamicconfig.FrontendNamespaceReplicationInducingAPIsRPS.Get(dc),
MaxNamespaceRPSPerInstance: dynamicconfig.FrontendMaxNamespaceRPSPerInstance.Get(dc),
MaxNamespaceBurstRatioPerInstance: dynamicconfig.FrontendMaxNamespaceBurstRatioPerInstance.Get(dc),
MaxConcurrentLongRunningRequestsPerInstance: dynamicconfig.FrontendMaxConcurrentLongRunningRequestsPerInstance.Get(dc),
MaxGlobalConcurrentLongRunningRequests: dynamicconfig.FrontendGlobalMaxConcurrentLongRunningRequests.Get(dc),
PollWaitForNamespaceRateLimitToken: dynamicconfig.PollWaitForNamespaceRateLimitToken.Get(dc),
MaxNamespaceVisibilityRPSPerInstance: dynamicconfig.FrontendMaxNamespaceVisibilityRPSPerInstance.Get(dc),
MaxNamespaceVisibilityBurstRatioPerInstance: dynamicconfig.FrontendMaxNamespaceVisibilityBurstRatioPerInstance.Get(dc),
MaxNamespaceNamespaceReplicationInducingAPIsRPSPerInstance: dynamicconfig.FrontendMaxNamespaceNamespaceReplicationInducingAPIsRPSPerInstance.Get(dc),
MaxNamespaceNamespaceReplicationInducingAPIsBurstRatioPerInstance: dynamicconfig.FrontendMaxNamespaceNamespaceReplicationInducingAPIsBurstRatioPerInstance.Get(dc),
GlobalWorkerDeploymentReadRPS: dynamicconfig.FrontendGlobalWorkerDeploymentReadRPS.Get(dc),
GlobalWorkerDeploymentReadBurstRatio: dynamicconfig.FrontendGlobalWorkerDeploymentReadBurstRatio.Get(dc),
GlobalNamespaceRPS: dynamicconfig.FrontendGlobalNamespaceRPS.Get(dc),
InternalFEGlobalNamespaceRPS: dynamicconfig.InternalFrontendGlobalNamespaceRPS.Get(dc),
GlobalNamespaceVisibilityRPS: dynamicconfig.FrontendGlobalNamespaceVisibilityRPS.Get(dc),
InternalFEGlobalNamespaceVisibilityRPS: dynamicconfig.InternalFrontendGlobalNamespaceVisibilityRPS.Get(dc),
// Overshoot since these low rate limits don't work well in an uncoordinated global limiter.
GlobalNamespaceNamespaceReplicationInducingAPIsRPS: dynamicconfig.FrontendGlobalNamespaceNamespaceReplicationInducingAPIsRPS.Get(dc),
MaxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
WorkerBuildIdSizeLimit: dynamicconfig.WorkerBuildIdSizeLimit.Get(dc),
ReachabilityTaskQueueScanLimit: dynamicconfig.ReachabilityTaskQueueScanLimit.Get(dc),
ReachabilityQueryBuildIdLimit: dynamicconfig.ReachabilityQueryBuildIdLimit.Get(dc),
ReachabilityCacheOpenWFsTTL: dynamicconfig.ReachabilityCacheOpenWFsTTL.Get(dc),
ReachabilityCacheClosedWFsTTL: dynamicconfig.ReachabilityCacheClosedWFsTTL.Get(dc),
ReachabilityQuerySetDurationSinceDefault: dynamicconfig.ReachabilityQuerySetDurationSinceDefault.Get(dc),
MaxBadBinaries: dynamicconfig.FrontendMaxBadBinaries.Get(dc),
DisableListVisibilityByFilter: dynamicconfig.DisableListVisibilityByFilter.Get(dc),
BlobSizeLimitError: dynamicconfig.BlobSizeLimitError.Get(dc),
BlobSizeLimitWarn: dynamicconfig.BlobSizeLimitWarn.Get(dc),
MemoSizeLimitError: dynamicconfig.MemoSizeLimitError.Get(dc),
ThrottledLogRPS: dynamicconfig.FrontendThrottledLogRPS.Get(dc),
ShutdownDrainDuration: dynamicconfig.FrontendShutdownDrainDuration.Get(dc),
ShutdownFailHealthCheckDuration: dynamicconfig.FrontendShutdownFailHealthCheckDuration.Get(dc),
EnableNamespaceNotActiveAutoForwarding: dynamicconfig.EnableNamespaceNotActiveAutoForwarding.Get(dc),
ForceNamespaceSelectedAPIAutoForwarding: dynamicconfig.ForceNamespaceSelectedAPIAutoForwarding.Get(dc),
NamespaceMinRetentionLocal: dynamicconfig.NamespaceMinRetentionLocal.Get(dc),
NamespaceMinRetentionGlobal: dynamicconfig.NamespaceMinRetentionGlobal.Get(dc),
SearchAttributesNumberOfKeysLimit: dynamicconfig.SearchAttributesNumberOfKeysLimit.Get(dc),
SearchAttributesSizeOfValueLimit: dynamicconfig.SearchAttributesSizeOfValueLimit.Get(dc),
SearchAttributesTotalSizeLimit: dynamicconfig.SearchAttributesTotalSizeLimit.Get(dc),
VisibilityArchivalQueryMaxPageSize: dynamicconfig.VisibilityArchivalQueryMaxPageSize.Get(dc),
DisallowQuery: dynamicconfig.DisallowQuery.Get(dc),
SendRawWorkflowHistory: dynamicconfig.SendRawWorkflowHistory.Get(dc),
DefaultWorkflowRetryPolicy: dynamicconfig.DefaultWorkflowRetryPolicy.Get(dc),
DefaultWorkflowTaskTimeout: dynamicconfig.DefaultWorkflowTaskTimeout.Get(dc),
EnableServerVersionCheck: dynamicconfig.EnableServerVersionCheck.Get(dc),
EnableTokenNamespaceEnforcement: dynamicconfig.EnableTokenNamespaceEnforcement.Get(dc),
ExposeAuthorizerErrors: dynamicconfig.ExposeAuthorizerErrors.Get(dc),
KeepAliveMinTime: dynamicconfig.KeepAliveMinTime.Get(dc),
KeepAlivePermitWithoutStream: dynamicconfig.KeepAlivePermitWithoutStream.Get(dc),
KeepAliveMaxConnectionIdle: dynamicconfig.KeepAliveMaxConnectionIdle.Get(dc),
KeepAliveMaxConnectionAge: dynamicconfig.KeepAliveMaxConnectionAge.Get(dc),
KeepAliveMaxConnectionAgeGrace: dynamicconfig.KeepAliveMaxConnectionAgeGrace.Get(dc),
KeepAliveTime: dynamicconfig.KeepAliveTime.Get(dc),
KeepAliveTimeout: dynamicconfig.KeepAliveTimeout.Get(dc),
DeleteNamespaceDeleteActivityRPS: dynamicconfig.DeleteNamespaceDeleteActivityRPS.Get(dc),
DeleteNamespacePageSize: dynamicconfig.DeleteNamespacePageSize.Get(dc),
DeleteNamespacePagesPerExecution: dynamicconfig.DeleteNamespacePagesPerExecution.Get(dc),
DeleteNamespaceConcurrentDeleteExecutionsActivities: dynamicconfig.DeleteNamespaceConcurrentDeleteExecutionsActivities.Get(dc),
DeleteNamespaceNamespaceDeleteDelay: dynamicconfig.DeleteNamespaceNamespaceDeleteDelay.Get(dc),
MaxFairnessWeightOverrideConfigLimit: dynamicconfig.MatchingMaxFairnessKeyWeightOverrides.Get(dc),
EnableSchedules: dynamicconfig.FrontendEnableSchedules.Get(dc),
EnableChasm: dynamicconfig.EnableChasm.Get(dc),
EnableCHASMSchedulerCreation: dynamicconfig.EnableCHASMSchedulerCreation.Get(dc),
CHASMSchedulerCreationRolloutPercent: dynamicconfig.CHASMSchedulerCreationRolloutPercent.Get(dc),
EnableCHASMSchedulerRouting: dynamicconfig.EnableCHASMSchedulerRouting.Get(dc),
EnableCHASMSchedulerSentinels: dynamicconfig.EnableCHASMSchedulerSentinels.Get(dc),
// [cleanup-wv-pre-release]
EnableDeployments: dynamicconfig.EnableDeployments.Get(dc),
EnableDeploymentVersions: dynamicconfig.EnableDeploymentVersions.Get(dc),
EnableBatcher: dynamicconfig.FrontendEnableBatcher.Get(dc),
MaxConcurrentBatchOperation: dynamicconfig.FrontendMaxConcurrentBatchOperationPerNamespace.Get(dc),
MaxExecutionCountBatchOperation: dynamicconfig.FrontendMaxExecutionCountBatchOperationPerNamespace.Get(dc),
MaxConcurrentAdminBatchOperation: dynamicconfig.FrontendMaxConcurrentAdminBatchOperationPerNamespace.Get(dc),
EnableBatchOperationsForStandaloneActivities: dynamicconfig.FrontendEnableBatchOperationsForStandaloneActivities.Get(dc),
EnableUpdateWorkflowExecution: dynamicconfig.FrontendEnableUpdateWorkflowExecution.Get(dc),
EnableUpdateWorkflowExecutionAsyncAccepted: dynamicconfig.FrontendEnableUpdateWorkflowExecutionAsyncAccepted.Get(dc),
EnableWorkflowUpdateCallbacks: dynamicconfig.EnableWorkflowUpdateCallbacks.Get(dc),
NumConsecutiveWorkflowTaskProblemsToTriggerSearchAttribute: dynamicconfig.NumConsecutiveWorkflowTaskProblemsToTriggerSearchAttribute.Get(dc),
EnableWorkerVersioningData: dynamicconfig.FrontendEnableWorkerVersioningDataAPIs.Get(dc),
EnableWorkerVersioningWorkflow: dynamicconfig.FrontendEnableWorkerVersioningWorkflowAPIs.Get(dc),
EnableWorkerVersioningRules: dynamicconfig.FrontendEnableWorkerVersioningRuleAPIs.Get(dc),
CallbackURLMaxLength: dynamicconfig.FrontendCallbackURLMaxLength.Get(dc),
CallbackHeaderMaxSize: dynamicconfig.FrontendCallbackHeaderMaxSize.Get(dc),
MaxCallbacksPerWorkflow: dynamicconfig.MaxCallbacksPerWorkflow.Get(dc),
MaxNexusOperationTokenLength: nexusoperations.MaxOperationTokenLength.Get(dc),
NexusRequestHeadersBlacklist: dynamicconfig.FrontendNexusRequestHeadersBlacklist.Get(dc),
NexusForwardRequestUseEndpoint: dynamicconfig.FrontendNexusForwardRequestUseEndpointDispatch.Get(dc),
NexusOperationsMetricTagConfig: nexusoperations.MetricTagConfiguration.Get(dc),
LinkMaxSize: dynamicconfig.FrontendLinkMaxSize.Get(dc),
MaxLinksPerRequest: dynamicconfig.FrontendMaxLinksPerRequest.Get(dc),
CallbackEndpointConfigs: callback.AllowedAddresses.Get(dc),
AdminEnableListHistoryTasks: dynamicconfig.AdminEnableListHistoryTasks.Get(dc),
MaskInternalErrorDetails: dynamicconfig.FrontendMaskInternalErrorDetails.Get(dc),
HistoryHostErrorPercentage: dynamicconfig.HistoryHostErrorPercentage.Get(dc),
HistoryHostSelfErrorProportion: dynamicconfig.HistoryHostSelfErrorProportion.Get(dc),
LogAllReqErrors: dynamicconfig.LogAllReqErrors.Get(dc),
EnableEagerWorkflowStart: dynamicconfig.EnableEagerWorkflowStart.Get(dc),
WorkflowRulesAPIsEnabled: dynamicconfig.WorkflowRulesAPIsEnabled.Get(dc),
MaxWorkflowRulesPerNamespace: dynamicconfig.MaxWorkflowRulesPerNamespace.Get(dc),
WorkerHeartbeatsEnabled: dynamicconfig.WorkerHeartbeatsEnabled.Get(dc),
EnableCancelWorkerPollsOnShutdown: dynamicconfig.EnableCancelWorkerPollsOnShutdown.Get(dc),
EnableMatchingFanOutForPollCancellation: dynamicconfig.EnableMatchingFanOutForPollCancellation.Get(dc),
NumTaskQueueReadPartitions: dynamicconfig.MatchingNumTaskqueueReadPartitions.Get(dc),
WorkerCommandsEnabled: dynamicconfig.WorkerCommandsEnabled.Get(dc),
PollerAutoscalingAutoEnroll: dynamicconfig.PollerAutoscalingAutoEnroll.Get(dc),
WorkflowPauseEnabled: dynamicconfig.WorkflowPauseEnabled.Get(dc),
TimeSkippingEnabled: dynamicconfig.TimeSkippingEnabled.Get(dc),
StandaloneNexusOperationsEnabled: chasmnexus.Enabled.Get(dc),
EnableWorkflowTaskCompletionPagination: dynamicconfig.EnableWorkflowTaskCompletionPagination.Get(dc),
HTTPAllowedHosts: dynamicconfig.FrontendHTTPAllowedHosts.Get(dc),
AllowedExperiments: dynamicconfig.FrontendAllowedExperiments.Get(dc),
Activity: activity.ConfigProvider(dc),
}
}
// Service represents the frontend service
metricsHandler metrics.Handler,
membershipMonitor membership.Monitor,
return &Service{
config: serviceConfig,
server: server,
healthServer: healthServer,
httpAPIServer: httpAPIServer,
handler: handler,
adminHandler: adminHandler,
operatorHandler: operatorHandler,
versionChecker: versionChecker,
visibilityManager: visibilityMgr,
logger: logger,
grpcListener: grpcListener,
metricsHandler: metricsHandler,
membershipMonitor: membershipMonitor,
}
}
// Start starts the service
s.logger.Info("frontend starting")
healthpb.RegisterHealthServer(s.server, s.healthServer)
workflowservice.RegisterWorkflowServiceServer(s.server, s.handler)
adminservice.RegisterAdminServiceServer(s.server, s.adminHandler)
operatorservice.RegisterOperatorServiceServer(s.server, s.operatorHandler)
reflection.Register(s.server)
// must start resource first
metrics.RestartCount.With(s.metricsHandler).Record(1)
s.versionChecker.Start()
s.adminHandler.Start()
s.operatorHandler.Start()
s.handler.Start()
go func() {
s.logger.Info("Starting to serve on frontend listener")
if err := s.server.Serve(s.grpcListener); err != nil {
s.logger.Fatal("Failed to serve on frontend listener", tag.Error(err))
}
}()
go func() {
if err := s.httpAPIServer.Serve(); err != nil {
}
}()
s.logger.Warn("HTTP API port has not been set. Nexus HTTP endpoints will not be available. " +
"To enable Nexus, follow these instructions: https://github.com/temporalio/temporal/blob/main/docs/architecture/nexus.md#enabling-nexus.")
}
}
// Stop stops the service
// initiate graceful shutdown:
// 1. Fail rpc health check, this will cause client side load balancer to stop forwarding requests to this node
// 2. wait for failure detection time
// 3. stop taking new requests by returning InternalServiceError
// 4. Wait for X second
// 5. Stop everything forcefully and return
requestDrainTime := max(time.Second, s.config.ShutdownDrainDuration())
failureDetectionTime := max(0, s.config.ShutdownFailHealthCheckDuration())
s.logger.Info("ShutdownHandler: Updating gRPC health status to ShuttingDown")
s.healthServer.Shutdown()
s.membershipMonitor.SetDraining(true)
s.logger.Info("ShutdownHandler: Waiting for others to discover I am unhealthy")
time.Sleep(failureDetectionTime)
s.handler.Stop()
s.operatorHandler.Stop()
s.adminHandler.Stop()
s.versionChecker.Stop()
s.visibilityManager.Close()
s.logger.Info("ShutdownHandler: Draining traffic")
// Gracefully stop gRPC server and HTTP API server concurrently
var wg sync.WaitGroup
wg.Go(func() {
t := time.AfterFunc(requestDrainTime, func() {
s.logger.Info("ShutdownHandler: Drain time expired, stopping all traffic")
s.server.Stop()
})
t.Stop()
})
wg.Go(func() {
s.httpAPIServer.GracefulStop(requestDrainTime)
})
}
if s.metricsHandler != nil {
s.metricsHandler.Stop(s.logger)
}
}
hostInfoProvider membership.HostInfoProvider,
contextFactory ContextFactory,
hostIdentity := hostInfoProvider.HostInfo().Identity()
contextTaggedLogger := log.With(logger, tag.ComponentShardController, tag.Address(hostIdentity))
taggedMetricsHandler := metricsHandler.WithTags(metrics.OperationTag(metrics.HistoryShardControllerScope))
ownership := newOwnership(
config,
historyServiceResolver,
hostInfoProvider,
contextTaggedLogger,
taggedMetricsHandler,
)
c := &ControllerImpl{
config: config,
contextFactory: contextFactory,
contextTaggedLogger: contextTaggedLogger,
historyShards: make(map[int32]historyi.ControllableContext),
hostInfoProvider: hostInfoProvider,
ownership: ownership,
taggedMetricsHandler: taggedMetricsHandler,
shardCountSubscriptions: map[*shardCountSubscription]struct{}{},
initialShardsAcquired: future.NewFuture[struct{}](),
}
c.lingerState.shards = make(map[historyi.ControllableContext]struct{})
return c
}
if !atomic.CompareAndSwapInt32(
&c.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
c.contextTaggedLogger.Info("", tag.LifeCycleStarted)
}
if !atomic.CompareAndSwapInt32(
&c.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
c.ownership.stop()
c.doShutdown()
c.contextTaggedLogger.Info("", tag.LifeCycleStopped)
}
return []pingable.Check{{
Name: "shard controller",
Timeout: 10 * time.Second,
Ping: func() []pingable.Pingable {
// we only need to read but get write lock to make sure we can
c.Lock()
defer c.Unlock()
out := make([]pingable.Pingable, 0, len(c.historyShards))
for _, shard := range c.historyShards {
out = append(out, shard)
}
return out
},
MetricsName: metrics.DDShardControllerLockLatency.Name(),
}
func (c *ControllerImpl) InitialShardsAcquired(ctx context.Context) error {
controller_impl.go
_, err := c.initialShardsAcquired.Get(ctx)
return err
}
// GetShardByNamespaceWorkflow returns a shard context for the given namespace and workflow.
func (c *ControllerImpl) GetShardByID(
shardID int32,
startTime := time.Now().UTC()
defer func() {
metrics.GetEngineForShardLatency.With(c.taggedMetricsHandler).Record(time.Since(startTime))
}()
}
}
c.RLock()
defer c.RUnlock()
ids := make([]int32, 0, len(c.historyShards))
for id := range c.historyShards {
ids = append(ids, id)
}
}
// if necessary. If a shard context is created, it will initialize in the background.
// This function won't block on rangeid lease acquisition.
func (c *ControllerImpl) getOrCreateShardContext(shardID int32) (historyi.ControllableContext, error) {
controller_impl.go
if err := c.validateShardId(shardID); err != nil {
return nil, err
}
if shard, ok := c.historyShards[shardID]; ok {
c.RUnlock()
return shard, nil
}
// if shard not valid then proceed to create a new one
}
c.Lock()
defer c.Unlock()
// Check again with exclusive lock
if shard, ok := c.historyShards[shardID]; ok {
if shard.IsValid() {
return shard, nil
}
return nil, err
}
hostInfo := c.hostInfoProvider.HostInfo()
return nil, fmt.Errorf("ControllerImpl for host '%v' shutting down", hostInfo.Identity())
}
shard, err := c.contextFactory.CreateContext(shardID, c.shardRemoveAndStop)
controller_impl.go
if err != nil {
return nil, err
}
metrics.ShardContextCreatedCounter.With(c.taggedMetricsHandler).Record(1)
c.contextTaggedLogger.Info("", numShardsTag(len(c.historyShards)))
return shard, nil
}
}
metrics.AcquireShardsCounter.With(c.taggedMetricsHandler).Record(1)
startTime := time.Now().UTC()
defer func() {
metrics.AcquireShardsLatency.With(c.taggedMetricsHandler).Record(time.Since(startTime))
}()
// Readiness check: if we haven't marked readiness yet, then we need to set up a context to
// run the readiness check on owned shards.
var readinessCtx context.Context
var readinessCancel context.CancelFunc
if !c.initialShardsAcquired.Ready() {
readinessCtx, readinessCancel = context.WithCancel(ctx)
} else {
}
// Cancel previous readiness check to ensure that the readiness check is always running on
// the most recent set of owned shards (e.g. after a membership change).
if prevCancel := c.shardReadinessCancel.Swap(readinessCancel); prevCancel != nil {
controller_impl.go
}
var ownedShards []int32 // only populated if we are doing a readiness check
tryAcquire := func(shardID int32) {
if err := c.ownership.verifyOwnership(shardID); err != nil {
// current host is not owner of shard, unload it if it is already loaded.
if c.config.ShardLingerTimeLimit() > 0 {
}
}
}
ownedShardsLock.Lock()
ownedShards = append(ownedShards, shardID)
ownedShardsLock.Unlock()
}
if err != nil {
metrics.GetEngineForShardErrorCounter.With(c.taggedMetricsHandler).Record(1)
c.contextTaggedLogger.Error("Unable to create history shard context", tag.Error(err), tag.OperationFailed, tag.ShardID(shardID))
// Wait up to 1s for the shard to acquire the rangeid lock.
// After 1s we will move on but the shard will continue trying in the background.
defer engineCancel()
_, _ = shard.GetEngine(engineCtx)
}
sem := semaphore.NewWeighted(concurrency)
numShards := c.config.NumberOfShards
randomStartOffset := rand.Int31n(numShards)
for index := range numShards {
shardID := (index+randomStartOffset)%numShards + 1
if err := sem.Acquire(ctx, 1); err != nil {
break
}
defer sem.Release(1)
tryAcquire(shardID)
}()
}
c.RLock()
// note that this count includes lingering shards
numOfOwnedShards := len(c.historyShards)
c.RUnlock()
metrics.NumShardsGauge.With(c.taggedMetricsHandler).Record(float64(numOfOwnedShards))
c.publishShardCountUpdate(numOfOwnedShards)
// Readiness check: We should set initialShardsAcquired when:
// 1. It's not already set.
// 2. We should own at least one shard (i.e. not before we join membership).
// 3. We have ownership of all the shards we're supposed to own.
if readinessCtx != nil {
if len(ownedShards) > 0 {
defer readinessCancel()
if c.checkShardReadiness(readinessCtx, ownedShards) {
}
}()
} else {
ctx context.Context,
shards []int32,
concurrency := int64(max(c.config.AcquireShardConcurrency(), 1))
sem := semaphore.NewWeighted(concurrency)
var ready atomic.Int32
for _, shardID := range shards {
if sem.Acquire(ctx, 1) != nil {
return false
}
defer sem.Release(1)
// Note that AssertOwnership uses a detached context for the actual persistence
// op so we can't cancel it. If context is canceled, the final Acquire will
// fail and we won't do anything.
if shard, err := c.GetShardByID(shardID); err != nil {
return
return
return
}
}()
}
return false
}
c.contextTaggedLogger.Info("initial shards not ready",
tag.Int32("ready", ready.Load()), tag.Int("total", len(shards)))
return false
}
c.contextTaggedLogger.Info("initial shards ready", tag.Int("total", len(shards)))
controller_impl.go
return true
}
// publishShardCountUpdate publishes the current number of shards that this controller owns to all shard count
// subscribers in a non-blocking manner.
c.RLock()
defer c.RUnlock()
for sub := range c.shardCountSubscriptions {
case sub.ch <- shardCount:
default:
}
}
c.contextTaggedLogger.Info("", tag.LifeCycleStopping)
c.Lock()
defer c.Unlock()
for _, shard := range c.historyShards {
}
}
if shardID <= 0 {
return invalidShardIdLowerBound
}
return invalidShardIdUpperBound
}
}
// SubscribeShardCount returns a subscription to shard count updates with a 1-buffered channel. This method is thread-safe.
c.Lock()
defer c.Unlock()
sub := &shardCountSubscription{
controller: c,
ch: make(chan int, 1), // buffered because we do a non-blocking send
}
c.shardCountSubscriptions[sub] = struct{}{}
return sub
}
// ShardCount returns a channel that receives the current shard count. This channel will be closed when the subscription
// is canceled.
return s.ch
}
// Unsubscribe removes the subscription from the controller's list of subscriptions.
s.controller.Lock()
defer s.controller.Unlock()
if _, ok := s.controller.shardCountSubscriptions[s]; !ok {
return
}
close(s.ch)
}
switch err.(type) {
case *persistence.ShardOwnershipLostError:
return true
}
}
return tag.Int("numShards", n)
}
replicaPoints int,
logger log.Logger,
resolver := &serviceResolver{
service: service,
port: port,
rp: rp,
replicaPoints: replicaPoints,
refreshChan: make(chan struct{}),
shutdownCh: make(chan struct{}),
logger: log.With(logger, tag.ComponentServiceResolver, tag.Service(service)),
scheduledRefreshMap: make(map[int64]*time.Timer),
listeners: make(map[string]chan<- *membership.ChangedEvent),
}
resolver.ringAndHosts.Store(ringAndHosts{
ring: newHashRing(replicaPoints),
hosts: make(map[string]*hostInfo),
})
return resolver
}
return hashring.New(farm.Fingerprint32, replicaPoints)
}
// Start starts the oracle
r.rp.AddListener(r)
if err := r.refresh(refreshModeAlways); err != nil {
r.logger.Fatal("unable to start ring pop service resolver", tag.Error(err))
}
go r.refreshRingWorker()
}
// Stop stops the resolver
r.listenerLock.Lock()
defer r.listenerLock.Unlock()
r.rp.RemoveListener(r)
r.ringAndHosts.Store(ringAndHosts{
ring: newHashRing(r.replicaPoints),
hosts: nil,
})
r.listeners = make(map[string]chan<- *membership.ChangedEvent)
close(r.shutdownCh)
if success := common.AwaitWaitGroup(&r.shutdownWG, time.Minute); !success {
r.logger.Warn("service resolver timed out on shutdown.")
}
}
select {
}
}
// Lookup finds the host in the ring responsible for serving the given key
func (r *serviceResolver) Lookup(key string) (membership.HostInfo, error) {
service_resolver.go
ring, hosts := r.ring()
addr, found := ring.Lookup(key)
if !found {
return nil, membership.ErrInsufficientHosts
}
}
func (r *serviceResolver) LookupN(key string, n int) []membership.HostInfo {
service_resolver.go
if n <= 0 {
return nil
}
addrs := ring.LookupN(key, n)
if len(addrs) == 0 {
return nil
}
return util.MapSlice(addrs, func(addr string) membership.HostInfo { return hosts[addr] })
service_resolver.go
}
name string,
notifyChannel chan<- *membership.ChangedEvent,
r.listenerLock.Lock()
defer r.listenerLock.Unlock()
_, ok := r.listeners[name]
if ok {
return membership.ErrListenerAlreadyExist
}
return nil
}
func (r *serviceResolver) RemoveListener(
name string,
r.listenerLock.Lock()
defer r.listenerLock.Unlock()
_, ok := r.listeners[name]
if !ok {
return nil
}
return nil
}
func (r *serviceResolver) HandleEvent(
event events.Event,
// We only about membership.ChangeEvent. Normally ringpop converts membership.ChangeEvent
// into events.RingChangedEvent when its internal hash ring changes, but since we construct
// our own hash rings with filtering, we have to handle the lower-level event ourselves.
if _, ok := event.(rpmembership.ChangeEvent); ok {
r.logger.Debug("Received a ring changed event")
// Note that we receive events asynchronously, possibly out of order.
// We cannot rely on the content of the event, rather we load everything
// from ringpop when we get a notification that something changed.
if err := r.refresh(refreshModeAlways); err != nil {
r.logger.Error("error refreshing ring when receiving a ring changed event", tag.Error(err))
}
}
var event *membership.ChangedEvent
var err error
defer func() {
if event != nil {
r.emitEvent(event)
}
}()
defer r.refreshLock.Unlock()
if mode == refreshModeLazy && r.lastRefreshTime.After(time.Now().UTC().Add(-minRefreshInternal)) {
}
return err
}
func (r *serviceResolver) refreshLocked() (*membership.ChangedEvent, error) {
service_resolver.go
hosts, nextEvent, err := r.getReachableMembers()
if err != nil {
return nil, err
}
// if we found an add/remove event, schedule another refresh right at that time
newMembersMap, changedEvent := r.compareMembers(hosts)
if changedEvent == nil {
return nil, nil
}
ring.AddMembers(util.MapSlice(hosts, func(h *hostInfo) rpmembership.Member { return h })...)
r.ringAndHosts.Store(ringAndHosts{
ring: ring,
hosts: newMembersMap,
})
addrs := util.MapSlice(hosts, func(h *hostInfo) string { return h.summary() })
slices.Sort(addrs)
r.logger.Info("Current reachable members", tag.Addresses(addrs))
return changedEvent, nil
}
if nextEvent == 0 {
return
}
if _, ok := r.scheduledRefreshMap[nextEvent]; ok {
return // already have a timer scheduled for this time
}
func (r *serviceResolver) getReachableMembers() ([]*hostInfo, int64, error) {
service_resolver.go
members, err := r.rp.GetReachableMemberObjects(swim.MemberWithLabelAndValue(roleKey, string(r.service)))
if err != nil {
return nil, 0, err
}
// need to keep track of one event since we'll refresh at that time and find the next one.
// Note that nextEvent is mutated by the filter functions below.
nextEvent := int64(math.MaxInt64)
// Filter by startAt
members = slices.DeleteFunc(members, func(member swim.Member) bool {
startAt, err := parseIntLabel(member, startAtKey)
if err != nil {
return false // ignore label if missing or can't parse
} else if startAt <= nowUnix {
return false // start time is in the past
}
// Filter by stopAt
stopAt, err := parseIntLabel(member, stopAtKey)
if err != nil {
return false // ignore label if missing or can't parse
} else if stopAt > nowUnix {
// stop time is in the future: schedule refresh at that time
nextEvent = min(nextEvent, stopAt)
// Turn swim.Members into hostInfo
for i, member := range members {
servicePort := r.port
// Each temporal service in the ring should advertise which port it has its gRPC listener
// on via a service label. If we cannot find the label, we will assume that the
// temporal service is listening on the same port that this node is listening on.
servicePortLabel, ok := member.Label(portKey)
if ok {
servicePort, err = strconv.Atoi(servicePortLabel)
if err != nil {
return nil, 0, err
}
}
if err != nil {
return nil, 0, err
}
// We can share member.Labels without copying since we never modify it.
}
nextEvent = 0
}
return hosts, nextEvent, nil
}
// Notify listeners
r.listenerLock.RLock()
defer r.listenerLock.RUnlock()
for name, ch := range r.listeners {
case ch <- event:
default:
r.logger.Error("Failed to send listener notification, channel full", tag.ListenerName(name))
}
defer r.shutdownWG.Done()
refreshTicker := time.NewTicker(defaultRefreshInterval)
defer refreshTicker.Stop()
for {
select {
return
if err := r.refresh(refreshModeLazy); err != nil {
r.logger.Error("error refreshing ring by request", tag.Error(err))
}
}
func (r *serviceResolver) ring() (*hashring.HashRing, map[string]*hostInfo) {
service_resolver.go
ring := r.ringAndHosts.Load().(ringAndHosts)
return ring.ring, ring.hosts
}
func (r *serviceResolver) compareMembers(hosts []*hostInfo) (map[string]*hostInfo, *membership.ChangedEvent) {
service_resolver.go
event := &membership.ChangedEvent{}
changed := false
_, prevHosts := r.ring() // note that this is always called with refreshLock so we can't miss an update here
newMembersMap := make(map[string]*hostInfo, len(hosts))
for _, host := range hosts {
newMembersMap[host.GetAddress()] = host
if prev, ok := prevHosts[host.GetAddress()]; !ok {
changed = true
changed = true
}
}
if _, ok := newMembersMap[addr]; !ok {
changed = true
}
}
return newMembersMap, event
}
}
// buildBroadcastHostPort return the listener hostport from an existing tchannel
// and overrides the address with broadcastAddress if specified
func buildBroadcastHostPort(listenerPeerInfo tchannel.LocalPeerInfo, broadcastAddress string) (string, error) {
service_resolver.go
// Ephemeral port check copied from ringpop-go/ringpop.go/channelAddressResolver
// Check that TChannel is listening on a real hostport. By default,
// TChannel listens on an ephemeral host/port. The real port is then
// assigned by the OS when ListenAndServe is called. If the hostport is
// ephemeral, it means TChannel is not yet listening and the hostport
// cannot be resolved.
if listenerPeerInfo.IsEphemeralHostPort() {
return "", ringpop.ErrEphemeralAddress
}
// Parse listener hostport
listenerIPString, port, err := net.SplitHostPort(listenerPeerInfo.HostPort)
service_resolver.go
if err != nil {
return "", err
}
// Broadcast IP override
// Parse supplied broadcastAddress override
ip := net.ParseIP(broadcastAddress)
if ip == nil {
return "", errors.New("broadcastAddress set but unknown failure encountered while parsing")
}
// If no errors, use the parsed IP with the port from our listener
}
// parseIntLabel returns the value of the given label as an integer.
str, ok := member.Label(label)
if !ok {
return 0, errMissingLabel
}
return strconv.ParseInt(str, 10, 64)
}
joinTime time.Time,
replicaPoints int,
lifecycleCtx, lifecycleCancel := context.WithCancel(context.Background())
lifecycleCtx = headers.SetCallerInfo(
lifecycleCtx,
headers.SystemBackgroundHighCallerInfo,
)
hostID, _ := uuid.New().MarshalBinary()
// MarshalBinary should never error.
rpo := &monitor{
status: common.DaemonStatusInitialized,
lifecycleCtx: lifecycleCtx,
lifecycleCancel: lifecycleCancel,
serviceName: serviceName,
services: services,
rp: rp,
rings: make(map[primitives.ServiceName]*serviceResolver),
replicaPoints: replicaPoints,
logger: logger,
metadataManager: metadataManager,
broadcastHostPortResolver: broadcastHostPortResolver,
hostID: hostID,
initialized: future.NewFuture[struct{}](),
maxJoinDuration: maxJoinDuration,
propagationTime: propagationTime,
joinTime: joinTime,
}
for service, port := range services {
rpo.rings[service] = newServiceResolver(service, port, rp, replicaPoints, logger)
}
return rpo
}
// it's safe for Stop() to run, which is at any point when we are neither updating the status field nor
// starting rings
rpo.stateLock.Lock()
if rpo.status != common.DaemonStatusInitialized {
rpo.stateLock.Unlock()
return
}
rpo.stateLock.Unlock()
broadcastAddress, err := rpo.broadcastHostPortResolver()
if err != nil {
rpo.logger.Fatal("unable to resolve broadcast address", tag.Error(err))
}
// we must know our seed nodes before bootstrapping
rpo.logger.Fatal("unable to initialize membership heartbeats", tag.Error(err))
}
// Stop() called during Start()'s execution. This is ok
if strings.Contains(err.Error(), "destroyed while attempting to join") {
}
if err != nil {
rpo.logger.Fatal("unable to get ringpop labels", tag.Error(err))
}
if until := time.Until(rpo.joinTime); until > 0 && until.Seconds() < maxScheduledEventTimeSeconds {
monitor.go
if err = labels.Set(startAtKey, strconv.FormatInt(rpo.joinTime.Unix(), 10)); err != nil {
rpo.logger.Fatal("unable to set ringpop label", tag.Error(err), tag.Key(startAtKey))
}
if err = labels.Set(portKey, strconv.Itoa(rpo.services[rpo.serviceName])); err != nil {
monitor.go
rpo.logger.Fatal("unable to set ringpop label", tag.Error(err), tag.Key(portKey))
}
// This label should be set last, it's used as the prediciate for finding members for rings.
rpo.logger.Fatal("unable to set ringpop label", tag.Error(err), tag.Key(roleKey))
}
// Our individual rings may not support concurrent start/stop calls so we reacquire the state lock while acting upon them
for _, ring := range rpo.rings {
ring.Start()
}
rpo.stateLock.Unlock()
rpo.initialized.Set(struct{}{}, nil)
}
// bootstrap ring pop service by discovering the bootstrap hosts and joining the ring pop cluster
policy := backoff.NewExponentialRetryPolicy(healthyHostLastHeartbeatCutoff / 2).
WithBackoffCoefficient(1).
WithMaximumAttempts(maxBootstrapRetries)
op := func() error {
hostPorts, err := rpo.fetchCurrentBootstrapHostports()
if err != nil {
return err
}
ParallelismFactor: 10,
JoinSize: 1,
MaxJoinDuration: rpo.maxJoinDuration,
DiscoverProvider: statichosts.New(hostPorts...),
}
_, err = rpo.rp.Bootstrap(bootParams)
if err != nil {
rpo.logger.Warn("unable to bootstrap ringpop. retrying", tag.Error(err))
}
}
return fmt.Errorf("exhausted all retries: %w", err)
}
}
}
_, err := rpo.initialized.Get(ctx)
return err
}
func (rpo *monitor) upsertMyMembership(
ctx context.Context,
request *persistence.UpsertClusterMembershipRequest,
err := rpo.metadataManager.UpsertClusterMembership(ctx, request)
if err == nil {
hostID, err := uuid.FromBytes(request.HostID)
if err != nil {
return err
}
tag.Address(request.RPCAddress.String()),
tag.Port(int(request.RPCPort)),
tag.HostID(hostID.String()))
}
}
// splitHostPortTyped expands upon net.SplitHostPort by providing type parsing.
ipstr, portstr, err := net.SplitHostPort(hostPort)
if err != nil {
return nil, 0, err
}
broadcastPort, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
return nil, 0, err
}
}
// Start by cleaning up expired records to avoid growth
err := rpo.metadataManager.PruneClusterMembership(rpo.lifecycleCtx, &persistence.PruneClusterMembershipRequest{MaxRecordsPruned: 10})
if err != nil {
return err
}
// Parse and validate broadcast hostport
broadcastAddress, broadcastPort, err := splitHostPortTyped(broadcastHostport)
if err != nil {
return err
}
// Parse and validate existing service name
if err != nil {
return err
}
Role: role,
RPCAddress: broadcastAddress,
RPCPort: broadcastPort,
SessionStart: sessionStarted,
RecordExpiry: upsertMembershipRecordExpiryDefault,
HostID: rpo.hostID,
}
// Upsert before fetching bootstrap hosts.
// This makes us discoverable by other Temporal cluster members
// Expire in 48 hours to allow for inspection of table by humans for debug scenarios.
// For bootstrapping, we filter to a much shorter duration on the
// read side by filtering on the last time a heartbeat was seen.
err = rpo.upsertMyMembership(rpo.lifecycleCtx, req)
if err == nil {
hostID, err := uuid.FromBytes(rpo.hostID)
if err != nil {
return err
}
tag.Address(broadcastAddress.String()),
tag.Port(int(broadcastPort)),
tag.HostID(hostID.String()))
rpo.startHeartbeatUpsertLoop(req)
}
}
pageSize := 1000
set := make(map[string]struct{})
var nextPageToken []byte
for {
resp, err := rpo.metadataManager.GetClusterMembers(
rpo.lifecycleCtx,
&persistence.GetClusterMembersRequest{
LastHeartbeatWithin: healthyHostLastHeartbeatCutoff,
PageSize: pageSize,
NextPageToken: nextPageToken,
})
if err != nil {
return nil, err
}
// Dedupe on hostport
set[net.JoinHostPort(host.RPCAddress.String(), convert.Uint16ToString(host.RPCPort))] = struct{}{}
}
nextPageToken = resp.NextPageToken
// Stop iterating once we have either 500 unique ip:port combos or there is no more results.
if len(nextPageToken) == 0 || len(set) >= 500 {
bootstrapHostPorts := make([]string, 0, len(set))
for k := range set {
bootstrapHostPorts = append(bootstrapHostPorts, k)
}
rpo.logger.Info("bootstrap hosts fetched", tag.BootstrapHostPorts(strings.Join(bootstrapHostPorts, ",")))
monitor.go
return bootstrapHostPorts, nil
}
}
}
func (rpo *monitor) startHeartbeatUpsertLoop(request *persistence.UpsertClusterMembershipRequest) {
monitor.go
loopUpsertMembership := func() {
for {
select {
case <-rpo.lifecycleCtx.Done():
return
}
if err != nil {
rpo.logger.Error("Membership upsert failed.", tag.Error(err))
}
time.Sleep(time.Second * time.Duration(10+jitter))
}
}
}
// for the entire call as the individual ring Start/Stop functions may not be safe to
// call concurrently
rpo.stateLock.Lock()
defer rpo.stateLock.Unlock()
if rpo.status != common.DaemonStatusStarted {
return
}
rpo.lifecycleCancel()
for _, ring := range rpo.rings {
ring.Stop()
}
}
return rpo.rp.SelfEvict()
}
func (rpo *monitor) EvictSelfAt(asOf time.Time) (time.Duration, error) {
}
func (rpo *monitor) GetResolver(service primitives.ServiceName) (membership.ServiceResolver, error) {
monitor.go
ring, found := rpo.rings[service]
if !found {
return nil, membership.ErrUnknownService
}
}
}
labels, err := rpo.rp.Labels()
if err != nil {
// This only happens if ringpop is not bootstrapped yet.
return err
}
}
}
host, _, err := net.SplitHostPort(address)
if err != nil {
return "", membership.ErrIncorrectAddressFormat
}
}
// RegisterServiceNameToServiceTypeEnum must be called from a static init().
func RegisterServiceNameToServiceTypeEnum(serviceName primitives.ServiceName, serviceType persistence.ServiceType) {
monitor.go
serviceNameToServiceTypeEnumMap[serviceName] = serviceType
}
RegisterServiceNameToServiceTypeEnum(primitives.AllServices, persistence.All)
RegisterServiceNameToServiceTypeEnum(primitives.FrontendService, persistence.Frontend)
RegisterServiceNameToServiceTypeEnum(primitives.InternalFrontendService, persistence.InternalFrontend)
RegisterServiceNameToServiceTypeEnum(primitives.HistoryService, persistence.History)
RegisterServiceNameToServiceTypeEnum(primitives.MatchingService, persistence.Matching)
RegisterServiceNameToServiceTypeEnum(primitives.WorkerService, persistence.Worker)
}
func serviceNameToServiceTypeEnum(name primitives.ServiceName) (persistence.ServiceType, error) {
monitor.go
if serviceType, ok := serviceNameToServiceTypeEnumMap[name]; ok {
return serviceType, nil
}
return persistence.All, fmt.Errorf("unable to parse servicename '%s'", name)
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &shardRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
// NewExecutionPersistenceRetryableClient creates a client to manage executions
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &executionRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
// NewTaskPersistenceRetryableClient creates a client to manage tasks
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &taskRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
// NewMetadataPersistenceRetryableClient creates a MetadataManager client to manage metadata
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &metadataRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
// NewClusterMetadataPersistenceRetryableClient creates a ClusterMetadataManager client to manage cluster metadata
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &clusterMetadataRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
// NewQueuePersistenceRetryableClient creates a client to manage queue
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &queueRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
// NewNexusEndpointPersistenceRetryableClient creates a NexusEndpointManager client to manage nexus endpoints
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &nexusEndpointRetryablePersistenceClient{
persistence: persistence,
policy: policy,
isRetryable: isRetryable,
}
}
func (p *shardRetryablePersistenceClient) GetName() string {
ctx context.Context,
request *GetOrCreateShardRequest,
var response *GetOrCreateShardResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.GetOrCreateShard(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
ctx context.Context,
request *UpdateShardRequest,
op := func(ctx context.Context) error {
return p.persistence.UpdateShard(ctx, request)
}
return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
}
ctx context.Context,
request *AssertShardOwnershipRequest,
op := func(ctx context.Context) error {
return p.persistence.AssertShardOwnership(ctx, request)
}
return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
}
p.persistence.Close()
}
func (p *executionRetryablePersistenceClient) GetName() string {
persistence_retryable_clients.go
return p.persistence.GetName()
}
func (p *executionRetryablePersistenceClient) GetHistoryBranchUtil() HistoryBranchUtil {
ctx context.Context,
request *GetHistoryTasksRequest,
var response *GetHistoryTasksResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.GetHistoryTasks(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
}
p.persistence.Close()
}
func (p *taskRetryablePersistenceClient) GetName() string {
}
p.persistence.Close()
}
func (p *metadataRetryablePersistenceClient) GetName() string {
ctx context.Context,
request *GetNamespaceRequest,
var response *GetNamespaceResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.GetNamespace(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
ctx context.Context,
request *ListNamespacesRequest,
var response *ListNamespacesResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.ListNamespaces(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
ctx context.Context,
currentClusterName string,
op := func(ctx context.Context) error {
return p.persistence.InitializeSystemNamespaces(ctx, currentClusterName)
}
return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
}
func (p *metadataRetryablePersistenceClient) WatchNamespaces(
ctx context.Context,
var watchCh <-chan *NamespaceWatchEvent
op := func(ctx context.Context) error {
var err error
watchCh, err = p.persistence.WatchNamespaces(ctx)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return watchCh, err
}
p.persistence.Close()
}
func (p *clusterMetadataRetryablePersistenceClient) GetName() string {
ctx context.Context,
request *GetClusterMembersRequest,
var response *GetClusterMembersResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.GetClusterMembers(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
ctx context.Context,
request *UpsertClusterMembershipRequest,
op := func(ctx context.Context) error {
return p.persistence.UpsertClusterMembership(ctx, request)
}
return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
}
ctx context.Context,
request *PruneClusterMembershipRequest,
op := func(ctx context.Context) error {
return p.persistence.PruneClusterMembership(ctx, request)
}
return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
}
ctx context.Context,
request *ListClusterMetadataRequest,
var response *ListClusterMetadataResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.ListClusterMetadata(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
func (p *clusterMetadataRetryablePersistenceClient) GetCurrentClusterMetadata(
ctx context.Context,
var response *GetClusterMetadataResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.GetCurrentClusterMetadata(ctx)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
ctx context.Context,
request *GetClusterMetadataRequest,
var response *GetClusterMetadataResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.GetClusterMetadata(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
ctx context.Context,
request *SaveClusterMetadataRequest,
var response bool
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.SaveClusterMetadata(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
return response, err
}
}
func (p *clusterMetadataRetryablePersistenceClient) Close() {
persistence_retryable_clients.go
p.persistence.Close()
}
func (p *queueRetryablePersistenceClient) Init(
ctx context.Context,
blob *commonpb.DataBlob,
op := func(ctx context.Context) error {
return p.persistence.Init(ctx, blob)
}
return backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
persistence_retryable_clients.go
}
}
p.persistence.Close()
}
func (p *nexusEndpointRetryablePersistenceClient) GetName() string {
}
p.persistence.Close()
}
func (p *nexusEndpointRetryablePersistenceClient) GetNexusEndpoint(
ctx context.Context,
request *ListNexusEndpointsRequest,
var response *ListNexusEndpointsResponse
op := func(ctx context.Context) error {
var err error
response, err = p.persistence.ListNexusEndpoints(ctx, request)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, p.policy, p.isRetryable)
return response, err
}
grpcListener net.Listener,
healthServer *health.Server,
workerServiceResolver, err := membershipMonitor.GetResolver(primitives.WorkerService)
if err != nil {
return nil, err
}
config: serviceConfig,
sdkClientFactory: sdkClientFactory,
logger: logger,
clusterMetadata: clusterMetadata,
clientBean: clientBean,
clusterMetadataManager: clusterMetadataManager,
namespaceRegistry: namespaceRegistry,
executionManager: executionManager,
workerServiceResolver: workerServiceResolver,
membershipMonitor: membershipMonitor,
hostInfo: hostInfoProvider.HostInfo(),
namespaceReplicationQueue: namespaceReplicationQueue,
metricsHandler: metricsHandler,
metadataManager: metadataManager,
taskManager: taskManager,
historyClient: historyClient,
visibilityManager: visibilityManager,
workerManager: workerManager,
perNamespaceWorkerManager: perNamespaceWorkerManager,
matchingClient: matchingClient,
namespaceReplicationTaskExecutor: namespaceReplicationTaskExecutor,
server: server,
grpcListener: grpcListener,
healthServer: healthServer,
}
if err := s.initScanner(serializer); err != nil {
return nil, err
}
}
dc *dynamicconfig.Collection,
persistenceConfig *config.Persistence,
config := &Config{
ParentCloseCfg: &parentclosepolicy.Config{
MaxConcurrentActivityExecutionSize: dynamicconfig.WorkerParentCloseMaxConcurrentActivityExecutionSize.Get(dc),
MaxConcurrentWorkflowTaskExecutionSize: dynamicconfig.WorkerParentCloseMaxConcurrentWorkflowTaskExecutionSize.Get(dc),
MaxConcurrentActivityTaskPollers: dynamicconfig.WorkerParentCloseMaxConcurrentActivityTaskPollers.Get(dc),
MaxConcurrentWorkflowTaskPollers: dynamicconfig.WorkerParentCloseMaxConcurrentWorkflowTaskPollers.Get(dc),
NumParentClosePolicySystemWorkflows: dynamicconfig.NumParentClosePolicySystemWorkflows.Get(dc),
},
ScannerCfg: &scanner.Config{
MaxConcurrentActivityExecutionSize: dynamicconfig.WorkerScannerMaxConcurrentActivityExecutionSize.Get(dc),
MaxConcurrentWorkflowTaskExecutionSize: dynamicconfig.WorkerScannerMaxConcurrentWorkflowTaskExecutionSize.Get(dc),
MaxConcurrentActivityTaskPollers: dynamicconfig.WorkerScannerMaxConcurrentActivityTaskPollers.Get(dc),
MaxConcurrentWorkflowTaskPollers: dynamicconfig.WorkerScannerMaxConcurrentWorkflowTaskPollers.Get(dc),
PersistenceMaxQPS: dynamicconfig.ScannerPersistenceMaxQPS.Get(dc),
Persistence: persistenceConfig,
TaskQueueScannerEnabled: dynamicconfig.TaskQueueScannerEnabled.Get(dc),
BuildIdScavengerEnabled: dynamicconfig.BuildIdScavengerEnabled.Get(dc),
HistoryScannerEnabled: dynamicconfig.HistoryScannerEnabled.Get(dc),
ExecutionsScannerEnabled: dynamicconfig.ExecutionsScannerEnabled.Get(dc),
HistoryScannerDataMinAge: dynamicconfig.HistoryScannerDataMinAge.Get(dc),
HistoryScannerVerifyRetention: dynamicconfig.HistoryScannerVerifyRetention.Get(dc),
ExecutionScannerPerHostQPS: dynamicconfig.ExecutionScannerPerHostQPS.Get(dc),
ExecutionScannerPerShardQPS: dynamicconfig.ExecutionScannerPerShardQPS.Get(dc),
ExecutionDataDurationBuffer: dynamicconfig.ExecutionDataDurationBuffer.Get(dc),
ExecutionScannerWorkerCount: dynamicconfig.ExecutionScannerWorkerCount.Get(dc),
ExecutionScannerHistoryEventIdValidator: dynamicconfig.ExecutionScannerHistoryEventIdValidator.Get(dc),
RemovableBuildIdDurationSinceDefault: dynamicconfig.RemovableBuildIdDurationSinceDefault.Get(dc),
BuildIdScavengerVisibilityRPS: dynamicconfig.BuildIdScavengerVisibilityRPS.Get(dc),
ScheduleInvariantsScannerOptions: dynamicconfig.ScheduleInvariantsScannerOptions.Get(dc),
},
BatcherRPS: dynamicconfig.BatcherRPS.Get(dc),
BatcherConcurrency: dynamicconfig.BatcherConcurrency.Get(dc),
EnableParentClosePolicyWorker: dynamicconfig.EnableParentClosePolicyWorker.Get(dc),
PerNamespaceWorkerCount: dynamicconfig.WorkerPerNamespaceWorkerCount.Subscribe(dc),
PerNamespaceWorkerOptions: dynamicconfig.WorkerPerNamespaceWorkerOptions.Subscribe(dc),
PerNamespaceWorkerStartRate: dynamicconfig.WorkerPerNamespaceWorkerStartRate.Get(dc),
ThrottledLogRPS: dynamicconfig.WorkerThrottledLogRPS.Get(dc),
PersistenceMaxQPS: dynamicconfig.WorkerPersistenceMaxQPS.Get(dc),
PersistenceGlobalMaxQPS: dynamicconfig.WorkerPersistenceGlobalMaxQPS.Get(dc),
PersistenceNamespaceMaxQPS: dynamicconfig.WorkerPersistenceNamespaceMaxQPS.Get(dc),
PersistenceGlobalNamespaceMaxQPS: dynamicconfig.WorkerPersistenceGlobalNamespaceMaxQPS.Get(dc),
PersistencePerShardNamespaceMaxQPS: dynamicconfig.DefaultPerShardNamespaceRPSMax,
PersistenceDynamicRateLimitingParams: dynamicconfig.WorkerPersistenceDynamicRateLimitingParams.Get(dc),
PersistenceQPSBurstRatio: dynamicconfig.PersistenceQPSBurstRatio.Get(dc),
OperatorRPSRatio: dynamicconfig.OperatorRPSRatio.Get(dc),
VisibilityPersistenceMaxReadQPS: dynamicconfig.VisibilityPersistenceMaxReadQPS.Get(dc),
VisibilityPersistenceMaxWriteQPS: dynamicconfig.VisibilityPersistenceMaxWriteQPS.Get(dc),
VisibilityPersistenceSlowQueryThreshold: dynamicconfig.VisibilityPersistenceSlowQueryThreshold.Get(dc),
EnableReadFromSecondaryVisibility: dynamicconfig.EnableReadFromSecondaryVisibility.Get(dc),
VisibilityEnableShadowReadMode: dynamicconfig.VisibilityEnableShadowReadMode.Get(dc),
VisibilityDisableOrderByClause: dynamicconfig.VisibilityDisableOrderByClause.Get(dc),
VisibilityEnableManualPagination: dynamicconfig.VisibilityEnableManualPagination.Get(dc),
VisibilityEnableUnifiedQueryConverter: dynamicconfig.VisibilityEnableUnifiedQueryConverter.Get(dc),
}
return config
}
// Start is called to start the service
s.logger.Info(
"worker starting",
tag.ComponentWorker,
)
metrics.RestartCount.With(s.metricsHandler).Record(1)
s.membershipMonitor.Start()
s.ensureSystemNamespaceExists(context.TODO())
s.startScanner()
if s.clusterMetadata.IsGlobalNamespaceEnabled() {
s.startReplicator()
}
s.startParentClosePolicyProcessor()
}
s.perNamespaceWorkerManager.Start(
// TODO: get these from fx instead of passing through Start
s.hostInfo,
s.workerServiceResolver,
)
healthpb.RegisterHealthServer(s.server, s.healthServer)
s.healthServer.SetServingStatus(ServiceName, healthpb.HealthCheckResponse_SERVING)
reflection.Register(s.server)
go func() {
s.logger.Info("Starting to serve on worker listener")
if err := s.server.Serve(s.grpcListener); err != nil {
s.logger.Fatal("Failed to serve on worker listener", tag.Error(err))
}
}()
"worker service started",
tag.ComponentWorker,
tag.Address(s.hostInfo.GetAddress()),
)
}
// Stop is called to stop the service
s.healthServer.SetServingStatus(ServiceName, healthpb.HealthCheckResponse_NOT_SERVING)
s.scanner.Stop()
s.perNamespaceWorkerManager.Stop()
s.workerManager.Stop()
s.visibilityManager.Close()
s.server.GracefulStop()
s.logger.Info(
"worker service stopped",
tag.ComponentWorker,
tag.Address(s.hostInfo.GetAddress()),
)
}
params := &parentclosepolicy.BootstrapParams{
Config: *s.config.ParentCloseCfg,
SdkClientFactory: s.sdkClientFactory,
MetricsHandler: s.metricsHandler,
Logger: s.logger,
ClientBean: s.clientBean,
CurrentCluster: s.clusterMetadata.GetCurrentClusterName(),
HostInfo: s.hostInfo,
}
processor := parentclosepolicy.New(params)
if err := processor.Start(); err != nil {
"error starting parentclosepolicy processor",
tag.Error(err),
)
}
}
currentCluster := s.clusterMetadata.GetCurrentClusterName()
adminClient, err := s.clientBean.GetRemoteAdminClient(currentCluster)
if err != nil {
return err
}
s.logger,
s.config.ScannerCfg,
s.sdkClientFactory,
s.metricsHandler,
s.executionManager,
s.metadataManager,
s.visibilityManager,
s.taskManager,
s.historyClient,
adminClient,
s.matchingClient,
s.namespaceRegistry,
currentCluster,
s.hostInfo,
serializer,
)
return nil
}
if err := s.scanner.Start(); err != nil {
"error starting scanner",
tag.Error(err),
)
}
}
func (s *Service) ensureSystemNamespaceExists(
ctx context.Context,
_, err := s.metadataManager.GetNamespace(ctx, &persistence.GetNamespaceRequest{Name: primitives.SystemLocalNamespace})
switch err.(type) {
case nil:
// noop
case *serviceerror.NamespaceNotFound:
refreshDuration dynamicconfig.DurationPropertyFn,
logger log.Logger,
if len(clusterInfo) == 0 {
panic("Empty cluster information")
panic("Master cluster name is empty")
panic("Current cluster name is empty")
} else if failoverVersionIncrement == 0 || failoverVersionIncrement > math.MaxInt32 {
metadata.go
panic("Version increment <= 0 or > 2147483647")
}
versionToClusterName, err := updateVersionToClusterName(clusterInfo, failoverVersionIncrement)
metadata.go
if err != nil {
// nolint:forbidigo // matches the other startup-config panics in this constructor
panic(err.Error())
}
panic("Current cluster is not specified in cluster info")
}
panic("Master cluster is not specified in cluster info")
}
maps.Copy(copyClusterInfo, clusterInfo)
if refreshDuration == nil {
refreshDuration = dynamicconfig.GetDurationPropertyFn(refreshInterval)
}
status: common.DaemonStatusInitialized,
enableGlobalNamespace: enableGlobalNamespace,
failoverVersionIncrement: failoverVersionIncrement,
masterClusterName: masterClusterName,
currentClusterName: currentClusterName,
clusterInfo: copyClusterInfo,
versionToClusterName: versionToClusterName,
clusterChangeCallback: make(map[any]CallbackFn),
clusterMetadataStore: clusterMetadataStore,
logger: logger,
refreshDuration: refreshDuration,
}
}
dynamicCollection *dynamicconfig.Collection,
logger log.Logger,
return NewMetadata(
config.EnableGlobalNamespace,
config.FailoverVersionIncrement,
config.MasterClusterName,
config.CurrentClusterName,
config.ClusterInformation,
clusterMetadataStore,
dynamicconfig.ClusterMetadataRefreshInterval.Get(dynamicCollection),
logger,
)
}
if !atomic.CompareAndSwapInt32(&m.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
// TODO: specify a timeout for the context
context.TODO(),
headers.SystemBackgroundHighCallerInfo,
)
err := m.refreshClusterMetadata(ctx)
if err != nil {
// Crash rather than start with partial cluster metadata (e.g. an invalid
// or missing row in cluster_metadata): replication and failover routing
m.logger.Fatal("Unable to initialize cluster metadata cache", tag.Error(err))
}
}
if !atomic.CompareAndSwapInt32(&m.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
<-m.refresher.Done()
}
return []pingable.Check{
{
Name: "cluster metadata lock",
// we don't do any persistence ops under clusterLock, use a short timeout
Timeout: 10 * time.Second,
Ping: func() []pingable.Pingable {
m.clusterLock.Lock()
// nolint:staticcheck
m.clusterLock.Unlock()
return nil
},
MetricsName: metrics.DDClusterMetadataLockLatency.Name(),
},
// not persistence ops.
Timeout: 10 * time.Second,
m.clusterCallbackLock.Lock()
// nolint:staticcheck
m.clusterCallbackLock.Unlock()
return nil
},
MetricsName: metrics.DDClusterMetadataCallbackLockLatency.Name(),
},
}
return m.enableGlobalNamespace
}
func (m *metadataImpl) IsMasterCluster() bool {
}
m.clusterLock.RLock()
defer m.clusterLock.RUnlock()
info, ok := m.clusterInfo[m.currentClusterName]
if !ok {
panic(fmt.Sprintf(
"Unknown cluster name: %v with given cluster initial failover version map: %v.",
}
return m.currentClusterName
}
m.clusterLock.RLock()
defer m.clusterLock.RUnlock()
result := make(map[string]ClusterInformation, len(m.clusterInfo))
maps.Copy(result, m.clusterInfo)
return result
}
func (m *metadataImpl) ClusterNameForFailoverVersion(isGlobalNamespace bool, failoverVersion int64) string {
}
func (m *metadataImpl) RegisterMetadataChangeCallback(callbackId any, cb CallbackFn) {
metadata.go
m.clusterCallbackLock.Lock()
m.clusterChangeCallback[callbackId] = cb
m.clusterCallbackLock.Unlock()
oldEntries := make(map[string]*ClusterInformation)
newEntries := make(map[string]*ClusterInformation)
m.clusterLock.RLock()
for clusterName, clusterInfo := range m.clusterInfo {
oldEntries[clusterName] = nil
newEntries[clusterName] = ShallowCopyClusterInformation(&clusterInfo)
}
m.clusterLock.RUnlock()
cb(oldEntries, newEntries)
}
m.clusterCallbackLock.Lock()
delete(m.clusterChangeCallback, callbackId)
m.clusterCallbackLock.Unlock()
}
timer := time.NewTicker(m.refreshDuration())
defer timer.Stop()
for {
select {
return nil
case <-timer.C:
for err := m.refreshClusterMetadata(ctx); err != nil; err = m.refreshClusterMetadata(ctx) {
}
clusterMetadataMap, err := m.listAllClusterMetadataFromDB(ctx)
if err != nil {
return err
}
newEntries := make(map[string]*ClusterInformation)
clusterInfoMap := m.GetAllClusterInfo()
for clusterName, newClusterInfo := range clusterMetadataMap {
oldClusterInfo, ok := clusterInfoMap[clusterName]
if !ok {
// handle new cluster registry
oldEntries[clusterName] = nil
newEntries[clusterName] = ShallowCopyClusterInformation(newClusterInfo)
if newClusterInfo.Enabled == oldClusterInfo.Enabled &&
newClusterInfo.ReplicationEnabled == oldClusterInfo.ReplicationEnabled &&
}
}
if _, ok := clusterMetadataMap[clusterName]; !ok {
// removed cluster registry
oldEntries[clusterName] = &oldClusterInfo
}
// Build a candidate map, validate it, and only commit on success.
// A bad row in cluster_metadata must not be able to crash the refresher
info ClusterInformation,
failoverVersionIncrement int64,
if clusterName == "" {
return errors.New("cluster name must not be empty")
}
return fmt.Errorf("cluster %q: InitialFailoverVersion must be > 0, got %d",
clusterName, info.InitialFailoverVersion)
}
return fmt.Errorf("cluster %q: InitialFailoverVersion (%d) must be < FailoverVersionIncrement (%d)",
clusterName, info.InitialFailoverVersion, failoverVersionIncrement)
}
return fmt.Errorf("cluster %q: RPCAddress must not be empty when Enabled=true", clusterName)
}
}
func updateVersionToClusterName(clusterInfo map[string]ClusterInformation, failoverVersionIncrement int64) (map[int64]string, error) {
metadata.go
versionToClusterName := make(map[int64]string)
for clusterName, info := range clusterInfo {
if err := ValidateClusterInformation(clusterName, info, failoverVersionIncrement); err != nil {
return nil, err
}
return nil, fmt.Errorf(
"duplicate InitialFailoverVersion %d for clusters %q and %q",
info.InitialFailoverVersion, existing, clusterName)
}
}
}
func (m *metadataImpl) listAllClusterMetadataFromDB(
ctx context.Context,
result := make(map[string]*ClusterInformation)
metadataStore := m.clusterMetadataStore
if metadataStore == nil {
return result, nil
}
for iterator.HasNext() {
item, err := iterator.Next()
if err != nil {
return nil, err
}
}
}
ctx context.Context,
metadataStore persistence.ClusterMetadataManager,
paginationFn := func(paginationToken []byte) ([]*persistence.GetClusterMetadataResponse, []byte, error) {
resp, err := metadataStore.ListClusterMetadata(
ctx,
&persistence.ListClusterMetadataRequest{
PageSize: defaultClusterMetadataPageSize,
NextPageToken: paginationToken,
},
)
if err != nil {
return nil, nil, err
}
}
return iterator
}
func ClusterInformationFromDB(getClusterResp *persistence.GetClusterMetadataResponse) *ClusterInformation {
metadata.go
return &ClusterInformation{
Enabled: getClusterResp.GetIsConnectionEnabled(),
InitialFailoverVersion: getClusterResp.GetInitialFailoverVersion(),
RPCAddress: getClusterResp.GetClusterAddress(),
HTTPAddress: getClusterResp.GetHttpAddress(),
ClusterID: getClusterResp.GetClusterId(),
ShardCount: getClusterResp.GetHistoryShardCount(),
Tags: getClusterResp.GetTags(),
ReplicationEnabled: getClusterResp.GetIsReplicationEnabled(),
version: getClusterResp.Version,
}
}
// ShallowCopyClusterInformation returns a shallow copy of the given ClusterInformation. The [ClusterInformation.Tags]
// field is not deep-copied, so you must be careful when modifying it.
func ShallowCopyClusterInformation(information *ClusterInformation) *ClusterInformation {
metadata.go
tmp := *information
return &tmp
}
// IsReplicationEnabledForCluster checks if replication is enabled for a cluster, considering the feature flag.
// When enableSeparateReplicationFlag is false, it falls back to only checking the Enabled flag.
// This is a shared helper function used across history service components.
func IsReplicationEnabledForCluster(clusterInfo ClusterInformation, enableSeparateReplicationFlag bool) bool {
metadata.go
if enableSeparateReplicationFlag {
// New behavior: check both Enabled (for connectivity) and ReplicationEnabled (for replication streams)
return clusterInfo.Enabled && clusterInfo.ReplicationEnabled
}
// Old behavior: only check Enabled flag
}
}
sqliteConfig := config.SQL{
PluginName: sqliteplugin.PluginName,
ConnectAttributes: make(map[string]string),
DatabaseName: cfg.DatabaseFilePath,
}
if cfg.Ephemeral {
sqliteConfig.ConnectAttributes["mode"] = "memory"
sqliteConfig.ConnectAttributes["cache"] = "shared"
// TODO(jlegrone): investigate whether a randomized db name is necessary when running in shared cache mode:
// https://www.sqlite.org/sharedcache.html
sqliteConfig.DatabaseName = fmt.Sprintf("%d", rand.Intn(9999999))
} else {
sqliteConfig.ConnectAttributes["mode"] = "rwc"
}
sqliteConfig.ConnectAttributes["_"+k] = v
}
cfg.FrontendPort = freeport.MustGetFreePort()
}
if cfg.MetricsPort == 0 {
cfg.MetricsPort = freeport.MustGetFreePort()
}
pprofPort := freeport.MustGetFreePort()
serverConfig.Global.Membership = config.Membership{
MaxJoinDuration: 30 * time.Second,
BroadcastAddress: localBroadcastAddress,
}
serverConfig.Global.Metrics = &metrics.Config{
Prometheus: &metrics.PrometheusConfig{
ListenAddress: fmt.Sprintf("%s:%d", cfg.FrontendIP, cfg.MetricsPort),
HandlerPath: "/metrics",
},
}
serverConfig.Global.PProf = config.PProf{Port: pprofPort}
serverConfig.Persistence = config.Persistence{
DefaultStore: sqliteplugin.PluginName,
VisibilityStore: sqliteplugin.PluginName,
NumHistoryShards: 1,
DataStores: map[string]config.DataStore{
sqliteplugin.PluginName: {SQL: &sqliteConfig},
},
}
serverConfig.ClusterMetadata = &cluster.Config{
EnableGlobalNamespace: false,
FailoverVersionIncrement: 10,
MasterClusterName: "active",
CurrentClusterName: "active",
ClusterInformation: map[string]cluster.ClusterInformation{
"active": {
Enabled: true,
InitialFailoverVersion: 1,
RPCAddress: fmt.Sprintf("%s:%d", localBroadcastAddress, cfg.FrontendPort),
},
},
}
serverConfig.DCRedirectionPolicy = config.DCRedirectionPolicy{
Policy: "noop",
}
serverConfig.Services = map[string]config.Service{
"frontend": cfg.mustGetService(0),
"history": cfg.mustGetService(1),
"matching": cfg.mustGetService(2),
"worker": cfg.mustGetService(3),
}
serverConfig.Archival = config.Archival{
History: config.HistoryArchival{
State: "disabled",
EnableRead: false,
Provider: nil,
},
Visibility: config.VisibilityArchival{
State: "disabled",
EnableRead: false,
Provider: nil,
},
}
// TODO(dnr): Figure out why server fails to start when PublicClient is not set with error:
// panic: Client must be created with client.Dial() or client.NewLazyClient()
// See also: https://github.com/temporalio/temporal/pull/4026#discussion_r1149808018
serverConfig.PublicClient = config.PublicClient{
HostPort: fmt.Sprintf("%s:%d", localBroadcastAddress, cfg.FrontendPort),
}
serverConfig.NamespaceDefaults = config.NamespaceDefaults{
Archival: config.ArchivalNamespaceDefaults{
History: config.HistoryArchivalNamespaceDefaults{
State: "disabled",
},
Visibility: config.VisibilityArchivalNamespaceDefaults{
State: "disabled",
},
},
}
}
if cfg.BaseConfig == nil {
cfg.BaseConfig = &config.Config{}
}
if cfg.Logger == nil {
cfg.Logger = log.NewZapLogger(log.BuildZapLogger(log.Config{
Stdout: true,
}
for pragma := range cfg.SQLitePragmas {
if _, ok := supportedPragmas[strings.ToLower(pragma)]; !ok {
return fmt.Errorf("unsupported SQLite pragma %q. allowed pragmas: %v", pragma, getAllowedPragmas())
}
return fmt.Errorf("config option DatabaseFilePath is not supported in ephemeral mode")
}
return fmt.Errorf("config option DatabaseFilePath is required when ephemeral mode disabled")
}
}
// Always use BaseConfig instead of the WithConfig server option, as WithConfig overrides all
// LiteServer specific settings.
func NewLiteServer(liteConfig *LiteServerConfig, opts ...temporal.ServerOption) (*LiteServer, error) {
lite_server.go
liteConfig.applyDefaults()
if err := liteConfig.validate(); err != nil {
return nil, err
}
sqlConfig := liteConfig.BaseConfig.Persistence.DataStores[sqliteplugin.PluginName].SQL
if !liteConfig.Ephemeral {
// Apply migrations if file does not already exist
if _, err := os.Stat(liteConfig.DatabaseFilePath); os.IsNotExist(err) {
// Pre-create namespaces
for _, ns := range liteConfig.Namespaces {
nsConfig, err := sqlite.NewNamespaceConfig(
liteConfig.BaseConfig.ClusterMetadata.CurrentClusterName,
ns,
false,
liteConfig.SearchAttributes,
)
if err != nil {
return nil, fmt.Errorf("error creating namespace config: %w", err)
}
}
return nil, fmt.Errorf("error creating namespaces: %w", err)
}
authorizer, err := authorization.GetAuthorizerFromConfig(&liteConfig.BaseConfig.Global.Authorization)
lite_server.go
if err != nil {
return nil, fmt.Errorf("unable to instantiate authorizer: %w", err)
}
claimMapper, err := authorization.GetClaimMapperFromConfig(&liteConfig.BaseConfig.Global.Authorization, liteConfig.Logger)
lite_server.go
if err != nil {
return nil, fmt.Errorf("unable to instantiate claim mapper: %w", err)
}
temporal.WithConfig(liteConfig.BaseConfig),
temporal.ForServices(temporal.DefaultServices),
temporal.WithLogger(liteConfig.Logger),
temporal.WithAuthorizer(authorizer),
temporal.WithClaimMapper(func(cfg *config.Config) authorization.ClaimMapper {
return claimMapper
}),
}
// To prevent having to code fall-through semantics right now, we currently
// eagerly fail if dynamic config is being configured in two ways
if liteConfig.BaseConfig.DynamicConfigClient != nil {
return nil, fmt.Errorf("unable to have file-based dynamic config and individual dynamic config values")
}
serverOpts = append(serverOpts, temporal.WithDynamicConfigClient(liteConfig.DynamicConfig))
lite_server.go
}
// Apply options from arguments
srv, err := temporal.NewServer(serverOpts...)
if err != nil {
return nil, fmt.Errorf("unable to instantiate server: %w", err)
}
internal: srv,
frontendHostPort: liteConfig.BaseConfig.PublicClient.HostPort,
}
return s, nil
}
// Start temporal server.
// We wrap Server instead of simply embedding it in the LiteServer struct so
// that it's possible to add additional lifecycle hooks here if necessary.
return s.internal.Start()
}
// Stop the server.
// We wrap Server instead of simply embedding it in the LiteServer struct so
// that it's possible to add additional lifecycle hooks here if necessary.
return s.internal.Stop()
}
// NewClient initializes a client ready to communicate with the Temporal
//
// Note that options.HostPort will always be overridden.
func (s *LiteServer) NewClientWithOptions(ctx context.Context, options client.Options) (client.Client, error) {
lite_server.go
options.HostPort = s.frontendHostPort
return client.Dial(options)
}
// FrontendHostPort returns the host:port for this server.
}
func (cfg *LiteServerConfig) mustGetService(frontendPortOffset int) config.Service {
lite_server.go
svc := config.Service{
RPC: config.RPC{
GRPCPort: cfg.FrontendPort + frontendPortOffset,
MembershipPort: freeport.MustGetFreePort(),
BindOnLocalHost: true,
BindOnIP: "",
},
}
// Assign any open port when configured to use dynamic ports
if frontendPortOffset != 0 {
svc.RPC.GRPCPort = freeport.MustGetFreePort()
}
// Optionally bind frontend to IPv4 address
svc.RPC.BindOnLocalHost = false
svc.RPC.BindOnIP = cfg.FrontendIP
}
}
testHooks testhooks.TestHooks,
chasmEngine chasm.Engine,
currentClusterName := shard.GetClusterMetadata().GetCurrentClusterName()
logger := shard.GetLogger()
executionManager := shard.GetExecutionManager()
workflowDeleteManager := deletemanager.NewDeleteManager(
shard,
workflowCache,
config,
shard.GetTimeSource(),
persistenceVisibilityMgr,
)
syncStateRetriever := replication.NewSyncStateRetriever(
shard,
workflowCache,
workflowConsistencyChecker,
eventBlobCache,
shard.GetLogger(),
)
historyEngImpl := &historyEngineImpl{
status: common.DaemonStatusInitialized,
currentClusterName: currentClusterName,
shardContext: shard,
clusterMetadata: shard.GetClusterMetadata(),
timeSource: shard.GetTimeSource(),
executionManager: executionManager,
tokenSerializer: tasktoken.NewSerializer(),
logger: log.With(logger, tag.ComponentHistoryEngine),
throttledLogger: log.With(shard.GetThrottledLogger(), tag.ComponentHistoryEngine),
metricsHandler: shard.GetMetricsHandler(),
eventNotifier: eventNotifier,
config: config,
sdkClientFactory: sdkClientFactory,
matchingClient: matchingClient,
rawMatchingClient: rawMatchingClient,
persistenceVisibilityMgr: persistenceVisibilityMgr,
workflowDeleteManager: workflowDeleteManager,
serializer: serializer,
workflowConsistencyChecker: workflowConsistencyChecker,
versionChecker: headers.NewDefaultVersionChecker(),
tracer: tracerProvider.Tracer(consts.LibraryName),
taskCategoryRegistry: taskCategoryRegistry,
commandHandlerRegistry: commandHandlerRegistry,
chasmWorkflowRegistry: chasmWorkflowRegistry,
workflowCache: workflowCache,
replicationProgressCache: replicationProgressCache,
syncStateRetriever: syncStateRetriever,
outboundQueueCBPool: outboundQueueCBPool,
testHooks: testHooks,
chasmEngine: chasmEngine,
versionCache: versionCache,
workerDeploymentClient: workerDeploymentClient,
routingInfoCache: routingInfoCache,
}
historyEngImpl.queueProcessors = make(map[tasks.Category]queues.Queue)
for _, factory := range queueProcessorFactories {
processor := factory.CreateQueue(shard)
historyEngImpl.queueProcessors[processor.Category()] = processor
}
historyEngImpl.eventsReapplier = ndc.NewEventsReapplier(shard.StateMachineRegistry(), shard.ChasmWorkflowRegistry(), shard.GetMetricsHandler(), logger)
history_engine.go
if shard.GetClusterMetadata().IsGlobalNamespaceEnabled() {
historyEngImpl.replicationAckMgr = replication.NewAckManager(
shard,
)
}
shard,
workflowCache,
logger,
)
historyEngImpl.workflowResetter = ndc.NewWorkflowResetter(
shard,
workflowCache,
logger,
)
historyEngImpl.searchAttributesValidator = searchattribute.NewValidator(
shard.GetSearchAttributesProvider(),
shard.GetSearchAttributesMapperProvider(),
config.SearchAttributesNumberOfKeysLimit,
config.SearchAttributesSizeOfValueLimit,
config.SearchAttributesTotalSizeLimit,
persistenceVisibilityMgr,
visibility.AllowListForValidation(
persistenceVisibilityMgr.GetStoreNames(),
config.VisibilityAllowList,
),
config.SuppressErrorSetSystemSearchAttribute,
shard.GetMetricsHandler(),
logger,
)
historyEngImpl.replicationDLQHandler = replication.NewLazyDLQHandler(
shard,
workflowDeleteManager,
workflowCache,
clientBean,
replicationTaskExecutorProvider,
)
historyEngImpl.replicationProcessorMgr = replication.NewTaskProcessorManager(
config,
shard,
historyEngImpl,
workflowCache,
workflowDeleteManager,
clientBean,
serializer,
replicationTaskFetcherFactory,
replicationTaskExecutorProvider,
testHooks,
dlqWriter,
)
return historyEngImpl
}
// Make sure all the components are loaded lazily so start can return immediately. This is important because
// ShardController calls start sequentially for all the shards for a given host during startup.
if !atomic.CompareAndSwapInt32(
&e.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
defer e.logger.Info("", tag.LifeCycleStarted)
e.registerNamespaceStateChangeCallback()
for _, queueProcessor := range e.queueProcessors {
queueProcessor.Start()
}
e.replicationProcessorMgr.Start()
}
// Stop the service.
if !atomic.CompareAndSwapInt32(
&e.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
defer e.logger.Info("", tag.LifeCycleStopped)
for _, queueProcessor := range e.queueProcessors {
queueProcessor.Stop()
}
e.replicationProcessorMgr.Stop()
// unset the failover callback
e.shardContext.GetNamespaceRegistry().UnregisterStateChangeCallback(e)
}
e.shardContext.GetNamespaceRegistry().RegisterStateChangeCallback(e, func(ns *namespace.Namespace, deletedFromDb bool) {
if e.shardContext.GetClusterMetadata().IsGlobalNamespaceEnabled() {
e.shardContext.UpdateHandoverNamespace(ns, deletedFromDb)
}
return
}
ns.ReplicationPolicy() == namespace.ReplicationPolicyMultiCluster &&
//nolint:forbidigo // namespace state-change callback; FailoverNamespace operates per-namespace, no workflow context
ns.ActiveInCluster(e.currentClusterName) {
for _, queueProcessor := range e.queueProcessors {
func (e *historyEngineImpl) NotifyNewTasks(
newTasks map[tasks.Category][]tasks.Task,
for category, tasksByCategory := range newTasks {
// and get rid of the special case here.
if category == tasks.CategoryReplication {
e.replicationAckMgr.NotifyNewTasks(tasksByCategory)
}
}
proc, ok := e.queueProcessors[category]
if !ok {
// On shard reload it sends fake tasks to wake up the queue processors. Only log if there are "real"
// tasks that can't be processed.
func NewTimerQueueFactory(
params timerQueueFactoryParams,
return &timerQueueFactory{
timerQueueFactoryParams: params,
QueueFactoryBase: QueueFactoryBase{
HostScheduler: queues.NewScheduler(
params.ClusterMetadata.GetCurrentClusterName(),
queues.SchedulerOptions{
WorkerCount: params.Config.TimerProcessorSchedulerWorkerCount,
ActiveNamespaceWeights: params.Config.TimerProcessorSchedulerActiveRoundRobinWeights,
StandbyNamespaceWeights: params.Config.TimerProcessorSchedulerStandbyRoundRobinWeights,
InactiveNamespaceDeletionDelay: params.Config.TaskSchedulerInactiveChannelDeletionDelay,
ExecutionAwareSchedulerOptions: ctasks.ExecutionAwareSchedulerOptions{
Enabled: params.Config.TaskSchedulerEnableExecutionQueueScheduler,
MaxQueues: params.Config.TaskSchedulerExecutionQueueSchedulerMaxQueues,
QueueTTL: params.Config.TaskSchedulerExecutionQueueSchedulerQueueTTL,
QueueConcurrency: params.Config.TaskSchedulerExecutionQueueSchedulerQueueConcurrency,
},
},
params.NamespaceRegistry,
params.Logger,
params.MetricsHandler,
params.TimeSource,
),
HostPriorityAssigner: queues.NewPriorityAssigner(
params.NamespaceRegistry,
params.ClusterMetadata.GetCurrentClusterName(),
),
HostReaderRateLimiter: queues.NewReaderPriorityRateLimiter(
NewHostRateLimiterRateFn(
params.Config.TimerProcessorMaxPollHostRPS,
params.Config.PersistenceMaxQPS,
timerQueuePersistenceMaxRPSRatio,
),
int64(params.Config.TimerQueueMaxReaderCount()),
),
Tracer: params.TracerProvider.Tracer(telemetry.ComponentQueueTimer),
},
}
}
func (f *timerQueueFactory) CreateQueue(
shardContext historyi.ShardContext,
logger := log.With(shardContext.GetLogger(), tag.ComponentTimerQueue)
metricsHandler := f.MetricsHandler.WithTags(metrics.OperationTag(metrics.OperationTimerQueueProcessorScope))
currentClusterName := f.ClusterMetadata.GetCurrentClusterName()
workflowDeleteManager := deletemanager.NewDeleteManager(
shardContext,
f.WorkflowCache,
f.Config,
shardContext.GetTimeSource(),
f.VisibilityManager,
)
shardScheduler := queues.NewRateLimitedScheduler(
f.HostScheduler,
queues.RateLimitedSchedulerOptions{
Enabled: f.Config.TaskSchedulerEnableRateLimiter,
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 := newTimerQueueActiveTaskExecutor(
shardContext,
f.WorkflowCache,
workflowDeleteManager,
logger,
f.MetricsHandler,
f.Config,
f.MatchingRawClient,
f.ChasmEngine,
)
standbyExecutor := newTimerQueueStandbyTaskExecutor(
shardContext,
f.WorkflowCache,
workflowDeleteManager,
f.MatchingRawClient,
f.ChasmEngine,
logger,
f.MetricsHandler,
// note: the cluster name is for calculating time for standby tasks,
// here we are basically using current cluster time
// this field will be deprecated soon, currently exists so that
// we have the option of revert to old behavior
currentClusterName,
f.Config,
f.ClientBean,
)
executor := queues.NewActiveStandbyExecutor(
currentClusterName,
f.NamespaceRegistry,
activeExecutor,
standbyExecutor,
logger,
)
if f.ExecutorWrapper != nil {
executor = f.ExecutorWrapper.Wrap(executor)
}
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.NewScheduledQueue(
shardContext,
tasks.CategoryTimer,
shardScheduler,
rescheduler,
factory,
&queues.Options{
ReaderOptions: queues.ReaderOptions{
BatchSize: f.Config.TimerTaskBatchSize,
MaxPendingTasksCount: f.Config.QueuePendingTaskMaxCount,
PollBackoffInterval: f.Config.TimerProcessorPollBackoffInterval,
MaxPredicateSize: f.Config.QueueMaxPredicateSize,
},
MonitorOptions: queues.MonitorOptions{
PendingTasksCriticalCount: f.Config.QueuePendingTaskCriticalCount,
ReaderStuckCriticalAttempts: f.Config.QueueReaderStuckCriticalAttempts,
SliceCountCriticalThreshold: f.Config.QueueCriticalSlicesCount,
},
MaxPollRPS: f.Config.TimerProcessorMaxPollRPS,
MaxPollInterval: f.Config.TimerProcessorMaxPollInterval,
MaxPollIntervalJitterCoefficient: f.Config.TimerProcessorMaxPollIntervalJitterCoefficient,
CheckpointInterval: f.Config.TimerProcessorUpdateAckInterval,
CheckpointIntervalJitterCoefficient: f.Config.TimerProcessorUpdateAckIntervalJitterCoefficient,
MaxReaderCount: f.Config.TimerQueueMaxReaderCount,
MoveGroupTaskCountBase: f.Config.QueueMoveGroupTaskCountBase,
MoveGroupTaskCountMultiplier: f.Config.QueueMoveGroupTaskCountMultiplier,
ShrinkPredicateMaxPendingKeys: f.Config.QueueShrinkPredicateMaxPendingKeys,
},
f.HostReaderRateLimiter,
logger,
metricsHandler,
)
}
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &shardRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: rateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
// NewExecutionPersistenceRateLimitedClient creates a client to manage executions
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &executionRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
// NewTaskPersistenceRateLimitedClient creates a client to manage tasks
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &taskRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
// NewMetadataPersistenceRateLimitedClient creates a MetadataManager client to manage metadata
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &metadataRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
// NewClusterMetadataPersistenceRateLimitedClient creates a ClusterMetadataManager client to manage cluster metadata
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &clusterMetadataRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
// NewQueuePersistenceRateLimitedClient creates a client to manage queue
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &queueRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
// NewNexusEndpointPersistenceRateLimitedClient creates a NexusEndpointManager to manage nexus endpoints
shardRateLimiter quotas.RequestRateLimiter,
logger log.Logger,
return &nexusEndpointRateLimitedPersistenceClient{
persistence: persistence,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
logger: logger,
}
}
func (p *shardRateLimitedPersistenceClient) GetName() string {
ctx context.Context,
request *GetOrCreateShardRequest,
if err := allow(ctx, "GetOrCreateShard", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return nil, err
}
response, err := p.persistence.GetOrCreateShard(ctx, request)
persistence_rate_limited_clients.go
return response, err
}
ctx context.Context,
request *UpdateShardRequest,
if err := allow(ctx, "UpdateShard", request.ShardInfo.ShardId, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return err
}
}
ctx context.Context,
request *AssertShardOwnershipRequest,
if err := allow(ctx, "AssertShardOwnership", request.ShardID, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return err
}
}
p.persistence.Close()
}
func (p *executionRateLimitedPersistenceClient) GetName() string {
persistence_rate_limited_clients.go
return p.persistence.GetName()
}
func (p *executionRateLimitedPersistenceClient) GetHistoryBranchUtil() HistoryBranchUtil {
ctx context.Context,
request *GetHistoryTasksRequest,
if err := allow(
ctx,
ConstructHistoryTaskAPI("GetHistoryTasks", request.TaskCategory),
request.ShardID,
p.systemRateLimiter,
p.namespaceRateLimiter,
p.shardRateLimiter,
); err != nil {
return nil, err
}
response, err := p.persistence.GetHistoryTasks(ctx, request)
persistence_rate_limited_clients.go
return response, err
}
}
p.persistence.Close()
}
func (p *taskRateLimitedPersistenceClient) GetName() string {
}
p.persistence.Close()
}
func (p *metadataRateLimitedPersistenceClient) GetName() string {
ctx context.Context,
request *GetNamespaceRequest,
if err := allow(ctx, "GetNamespace", CallerSegmentMissing, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return nil, err
}
return response, err
}
ctx context.Context,
request *ListNamespacesRequest,
if err := allow(ctx, "ListNamespaces", CallerSegmentMissing, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return nil, err
}
response, err := p.persistence.ListNamespaces(ctx, request)
persistence_rate_limited_clients.go
return response, err
}
func (p *metadataRateLimitedPersistenceClient) WatchNamespaces(
ctx context.Context,
if err := allow(ctx, "WatchNamespaces", CallerSegmentMissing, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return nil, err
}
}
p.persistence.Close()
}
// AppendHistoryNodes add a node to history node table
}
p.persistence.Close()
}
func (p *queueRateLimitedPersistenceClient) Init(
ctx context.Context,
blob *commonpb.DataBlob,
return p.persistence.Init(ctx, blob)
}
func (c *clusterMetadataRateLimitedPersistenceClient) Close() {
persistence_rate_limited_clients.go
c.persistence.Close()
}
func (c *clusterMetadataRateLimitedPersistenceClient) GetName() string {
ctx context.Context,
request *GetClusterMembersRequest,
if err := allow(ctx, "GetClusterMembers", CallerSegmentMissing, c.systemRateLimiter, c.namespaceRateLimiter, c.shardRateLimiter); err != nil {
return nil, err
}
}
ctx context.Context,
request *UpsertClusterMembershipRequest,
if err := allow(ctx, "UpsertClusterMembership", CallerSegmentMissing, c.systemRateLimiter, c.namespaceRateLimiter, c.shardRateLimiter); err != nil {
return err
}
return c.persistence.UpsertClusterMembership(ctx, request)
persistence_rate_limited_clients.go
}
ctx context.Context,
request *PruneClusterMembershipRequest,
if err := allow(ctx, "PruneClusterMembership", CallerSegmentMissing, c.systemRateLimiter, c.namespaceRateLimiter, c.shardRateLimiter); err != nil {
return err
}
return c.persistence.PruneClusterMembership(ctx, request)
persistence_rate_limited_clients.go
}
ctx context.Context,
request *ListClusterMetadataRequest,
if err := allow(ctx, "ListClusterMetadata", CallerSegmentMissing, c.systemRateLimiter, c.namespaceRateLimiter, c.shardRateLimiter); err != nil {
return nil, err
}
}
func (c *clusterMetadataRateLimitedPersistenceClient) GetCurrentClusterMetadata(
ctx context.Context,
if err := allow(ctx, "GetCurrentClusterMetadata", CallerSegmentMissing, c.systemRateLimiter, c.namespaceRateLimiter, c.shardRateLimiter); err != nil {
return nil, err
}
}
}
func (p *nexusEndpointRateLimitedPersistenceClient) Close() {
persistence_rate_limited_clients.go
p.persistence.Close()
}
func (p *nexusEndpointRateLimitedPersistenceClient) GetNexusEndpoint(
ctx context.Context,
request *ListNexusEndpointsRequest,
if err := allow(ctx, "ListNexusEndpoints", CallerSegmentMissing, p.systemRateLimiter, p.namespaceRateLimiter, p.shardRateLimiter); err != nil {
return nil, err
}
}
namespaceRateLimiter quotas.RequestRateLimiter,
shardRateLimiter quotas.RequestRateLimiter,
callerInfo := headers.GetCallerInfo(ctx)
// namespace-level rate limits has to be applied before system-level rate limits.
now := time.Now().UTC()
quotaRequest := quotas.NewRequest(
api,
RateLimitDefaultToken,
callerInfo.CallerName,
callerInfo.CallerType,
shardID,
callerInfo.CallOrigin,
)
if ok := shardRateLimiter.Allow(now, quotaRequest); !ok {
return ErrPersistenceNamespaceShardLimitExceeded
}
if ok := namespaceRateLimiter.Allow(now, quotaRequest); !ok {
persistence_rate_limited_clients.go
return ErrPersistenceNamespaceLimitExceeded
}
if ok := systemRateLimiter.Allow(now, quotaRequest); !ok {
persistence_rate_limited_clients.go
return ErrPersistenceSystemLimitExceeded
}
}
baseAPI string,
taskCategory tasks.Category,
return baseAPI + taskCategory.Name()
}
func NewTransferQueueFactory(
params transferQueueFactoryParams,
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,
ExecutionAwareSchedulerOptions: ctasks.ExecutionAwareSchedulerOptions{
Enabled: params.Config.TaskSchedulerEnableExecutionQueueScheduler,
MaxQueues: params.Config.TaskSchedulerExecutionQueueSchedulerMaxQueues,
QueueTTL: params.Config.TaskSchedulerExecutionQueueSchedulerQueueTTL,
QueueConcurrency: params.Config.TaskSchedulerExecutionQueueSchedulerQueueConcurrency,
},
},
params.NamespaceRegistry,
params.Logger,
params.MetricsHandler,
params.TimeSource,
),
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,
logger := log.With(shardContext.GetLogger(), tag.ComponentTransferQueue)
metricsHandler := f.MetricsHandler.WithTags(metrics.OperationTag(metrics.OperationTransferQueueProcessorScope))
currentClusterName := f.ClusterMetadata.GetCurrentClusterName()
shardScheduler := queues.NewRateLimitedScheduler(
f.HostScheduler,
queues.RateLimitedSchedulerOptions{
Enabled: f.Config.TaskSchedulerEnableRateLimiter,
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,
f.TestHooks,
)
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)
}
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,
ShrinkPredicateMaxPendingKeys: f.Config.QueueShrinkPredicateMaxPendingKeys,
},
f.HostReaderRateLimiter,
queues.GrouperNamespaceID{},
logger,
metricsHandler,
factory,
nil, // taskPostProcessor
)
}
}
func NewOutboundQueueFactory(params outboundQueueFactoryParams) QueueFactory {
outbound_queue_factory.go
metricsHandler := getOutbountQueueProcessorMetricsHandler(params.MetricsHandler)
rateLimiterPool := collection.NewOnceMap(
func(key tasks.TaskGroupNamespaceIDAndDestination) quotas.RateLimiter {
return quotas.NewDefaultOutgoingRateLimiter(func() float64 {
// This is intentionally not failing the function in case of error. The task
)
f := &outboundQueueFactory{
outboundQueueFactoryParams: params,
hostReaderRateLimiter: queues.NewReaderPriorityRateLimiter(
NewHostRateLimiterRateFn(
params.Config.OutboundProcessorMaxPollHostRPS,
params.Config.PersistenceMaxQPS,
outboundQueuePersistenceMaxRPSRatio,
),
int64(params.Config.OutboundQueueMaxReaderCount()),
),
hostScheduler: &queues.CommonSchedulerWrapper{
Scheduler: ctasks.NewGroupByScheduler(
ctasks.GroupBySchedulerOptions[
tasks.TaskGroupNamespaceIDAndDestination,
queues.Executable,
]{
Logger: params.Logger,
KeyFn: func(e queues.Executable) tasks.TaskGroupNamespaceIDAndDestination {
return grouper.KeyTyped(e.GetTask())
},
},
}
}
// Start implements QueueFactory.
f.hostScheduler.Start()
}
// Stop implements QueueFactory.
f.hostScheduler.Stop()
}
func (f *outboundQueueFactory) CreateQueue(
shardContext historyi.ShardContext,
logger := log.With(shardContext.GetLogger(), tag.ComponentOutboundQueue)
metricsHandler := getOutbountQueueProcessorMetricsHandler(f.MetricsHandler)
currentClusterName := f.ClusterMetadata.GetCurrentClusterName()
scheduler := queues.NewRateLimitedScheduler(
f.hostScheduler,
queues.RateLimitedSchedulerOptions{
Enabled: f.Config.TaskSchedulerEnableRateLimiter,
EnableShadowMode: f.Config.TaskSchedulerEnableRateLimiterShadowMode,
StartupDelay: f.Config.TaskSchedulerRateLimiterStartupDelay,
},
currentClusterName,
f.NamespaceRegistry,
f.SchedulerRateLimiter,
f.TimeSource,
f.ChasmRegistry,
logger,
metricsHandler,
)
rescheduler := queues.NewRescheduler(
scheduler,
shardContext.GetTimeSource(),
logger,
metricsHandler,
)
activeExecutor := newOutboundQueueActiveTaskExecutor(
shardContext,
f.WorkflowCache,
logger,
metricsHandler,
f.ChasmEngine,
f.MatchingClient,
)
standbyExecutor := newOutboundQueueStandbyTaskExecutor(
shardContext,
f.WorkflowCache,
currentClusterName,
logger,
metricsHandler,
f.ChasmEngine,
f.ClientBean,
)
executor := queues.NewActiveStandbyExecutor(
currentClusterName,
f.NamespaceRegistry,
activeExecutor,
standbyExecutor,
logger,
)
if f.ExecutorWrapper != nil {
executor = f.ExecutorWrapper.Wrap(executor)
}
executor,
scheduler,
rescheduler,
queues.NewNoopPriorityAssigner(),
shardContext.GetTimeSource(),
shardContext.GetNamespaceRegistry(),
shardContext.GetClusterMetadata(),
f.ChasmRegistry,
queues.GetTaskTypeTagValue,
logger,
metricsHandler,
f.TracerProvider.Tracer(telemetry.ComponentQueueOutbound),
f.DLQWriter,
f.Config.TaskDLQEnabled,
f.Config.TaskDLQUnexpectedErrorAttempts,
f.Config.TaskDLQInternalErrors,
f.Config.TaskDLQErrorPattern,
)
return queues.NewImmediateQueue(
shardContext,
tasks.CategoryOutbound,
scheduler,
rescheduler,
&queues.Options{
ReaderOptions: queues.ReaderOptions{
BatchSize: f.Config.OutboundTaskBatchSize,
MaxPendingTasksCount: f.Config.OutboundQueuePendingTaskMaxCount,
PollBackoffInterval: f.Config.OutboundProcessorPollBackoffInterval,
MaxPredicateSize: f.Config.OutboundQueueMaxPredicateSize,
},
MonitorOptions: queues.MonitorOptions{
PendingTasksCriticalCount: f.Config.OutboundQueuePendingTaskCriticalCount,
// Shared configuration with other queues.
ReaderStuckCriticalAttempts: f.Config.QueueReaderStuckCriticalAttempts,
SliceCountCriticalThreshold: f.Config.QueueCriticalSlicesCount,
},
MaxPollRPS: f.Config.OutboundProcessorMaxPollRPS,
MaxPollInterval: f.Config.OutboundProcessorMaxPollInterval,
MaxPollIntervalJitterCoefficient: f.Config.OutboundProcessorMaxPollIntervalJitterCoefficient,
CheckpointInterval: f.Config.OutboundProcessorUpdateAckInterval,
CheckpointIntervalJitterCoefficient: f.Config.OutboundProcessorUpdateAckIntervalJitterCoefficient,
MaxReaderCount: f.Config.OutboundQueueMaxReaderCount,
MoveGroupTaskCountBase: f.Config.QueueMoveGroupTaskCountBase,
MoveGroupTaskCountMultiplier: f.Config.QueueMoveGroupTaskCountMultiplier,
ShrinkPredicateMaxPendingKeys: f.Config.QueueShrinkPredicateMaxPendingKeys,
},
f.hostReaderRateLimiter,
queues.GrouperStateMachineNamespaceIDAndDestination{},
logger,
metricsHandler,
factory,
outboundTaskGroupPostProcessor(f.ChasmRegistry),
)
}
func getOutbountQueueProcessorMetricsHandler(handler metrics.Handler) metrics.Handler {
outbound_queue_factory.go
return handler.WithTags(metrics.OperationTag(metrics.OperationOutboundQueueProcessorScope))
}
func StateMachineTask(smRegistry *hsm.Registry, task tasks.Task) (hsm.Ref, hsm.Task, error) {
}
func outboundTaskGroupPostProcessor(registry *chasm.Registry) func([]tasks.Task) {
outbound_queue_factory.go
if registry == nil {
return nil
}
for _, t := range taskSlice {
if ct, ok := t.(*tasks.ChasmTask); ok {
if rt, ok := registry.TaskByID(ct.Info.GetTypeId()); ok {
}
return &sharedScopeCache{
maxSize: maxSize,
scopes: make(map[string]tally.Scope),
handlers: make(map[string]*tallyMetricsHandler),
}
}
func (c *sharedScopeCache) loadOrStoreScope(key string, create func() tally.Scope) tally.Scope {
tally_metrics_handler.go
c.mu.RLock()
if s, ok := c.scopes[key]; ok {
return s
}
s := create()
c.mu.Lock()
defer c.mu.Unlock()
// Double-check: another goroutine may have inserted while we were creating.
if existing, ok := c.scopes[key]; ok {
return existing
}
clear(c.scopes)
}
return s
}
func (c *sharedScopeCache) loadOrStoreHandler(key string, create func() *tallyMetricsHandler) *tallyMetricsHandler {
tally_metrics_handler.go
c.mu.RLock()
if h, ok := c.handlers[key]; ok {
return h
}
h := create()
c.mu.Lock()
defer c.mu.Unlock()
// Double-check: another goroutine may have inserted while we were creating.
if existing, ok := c.handlers[key]; ok {
return existing
}
clear(c.handlers)
}
return h
}
var _ Handler = (*tallyMetricsHandler)(nil)
func NewTallyMetricsHandler(cfg ClientConfig, scope tally.Scope) *tallyMetricsHandler {
tally_metrics_handler.go
perUnitBuckets := make(map[MetricUnit]tally.Buckets)
for unit, boundariesList := range cfg.PerUnitHistogramBoundaries {
perUnitBuckets[MetricUnit(unit)] = tally.ValueBuckets(boundariesList)
tally_metrics_handler.go
}
if maxSize <= 0 {
}
scope: scope,
perUnitBuckets: perUnitBuckets,
excludeTags: configExcludeTags(cfg),
cache: newSharedScopeCache(maxSize),
scopeKey: "",
}
}
// tagsCacheKey builds a compact string key from a tag slice for use as a
// map lookup key.
size := 0
for i := range tags {
size += len(tags[i].Key) + len(tags[i].Value) + 2*binary.MaxVarintLen64
}
var sb strings.Builder
sb.Grow(size)
for _, t := range tags {
appendCacheKeyPart(&sb, t.Key)
appendCacheKeyPart(&sb, t.Value)
}
return sb.String()
}
var lenBuf [binary.MaxVarintLen64]byte
n := binary.PutUvarint(lenBuf[:], uint64(len(value)))
_, _ = sb.Write(lenBuf[:n])
sb.WriteString(value)
}
// WithTags creates a new MetricProvider with provided []Tag
// Tags are merged with registered Tags from the source MetricsHandler.
// Handlers are cached by tag combination so repeated calls avoid allocations.
if len(tags) == 0 {
return tmh
}
normalizedKey := tagsCacheKey(normalizeTagsForCaching(tags, tmh.excludeTags))
tally_metrics_handler.go
key := tmh.scopeKey + normalizedKey
return tmh.cache.loadOrStoreHandler(key, func() *tallyMetricsHandler {
return &tallyMetricsHandler{
scope: tmh.scope.Tagged(tagsToMap(tags, tmh.excludeTags)),
perUnitBuckets: tmh.perUnitBuckets,
excludeTags: tmh.excludeTags,
cache: tmh.cache,
scopeKey: key,
}
})
}
// excludeTags before cache key computation so that different raw values which
// map to the same excluded placeholder share a single cache entry.
func (tmh *tallyMetricsHandler) cachedTaggedScope(tags []Tag) tally.Scope {
tally_metrics_handler.go
if len(tags) == 0 {
}
key := tmh.scopeKey + tagsCacheKey(normalizeTagsForCaching(tags, tmh.excludeTags))
tally_metrics_handler.go
return tmh.cache.loadOrStoreScope(key, func() tally.Scope {
return tmh.scope.Tagged(tagsToMap(tags, tmh.excludeTags))
})
}
// normalizeTag applies excludeTags substitution to a single tag.
// Returns the (possibly modified) tag and whether it was normalized.
if vals, ok := excl[t.Key]; ok {
if _, ok := vals[t.Value]; !ok {
return Tag{Key: t.Key, Value: tagExcludedValue}, true
}
}
}
// canonical tag values for cache key computation. Returns the original slice
// unchanged if no tags need normalization (zero-alloc fast path).
if len(excl) == 0 {
}
var normalized []Tag
for i, t := range tags {
// Counter obtains a counter for the given name.
func (tmh *tallyMetricsHandler) Counter(counter string) CounterIface {
tally_metrics_handler.go
if v, ok := tmh.counters.Load(counter); ok {
return v.(CounterIface) //nolint:revive // type-safe: only CounterIface is stored
tally_metrics_handler.go
}
tmh.cachedTaggedScope(t).Counter(counter).Inc(i)
})
actual, _ := tmh.counters.LoadOrStore(counter, c)
return actual.(CounterIface) //nolint:revive // type-safe: only CounterIface is stored
}
// Gauge obtains a gauge for the given name.
if v, ok := tmh.gauges.Load(gauge); ok {
return v.(GaugeIface) //nolint:revive // type-safe: only GaugeIface is stored
tally_metrics_handler.go
}
tmh.cachedTaggedScope(t).Gauge(gauge).Update(f)
})
actual, _ := tmh.gauges.LoadOrStore(gauge, g)
return actual.(GaugeIface) //nolint:revive // type-safe: only GaugeIface is stored
}
// Timer obtains a timer for the given name.
if v, ok := tmh.timers.Load(timer); ok {
return v.(TimerIface) //nolint:revive // type-safe: only TimerIface is stored
tally_metrics_handler.go
}
tmh.cachedTaggedScope(t).Timer(timer).Record(d)
})
actual, _ := tmh.timers.LoadOrStore(timer, ti)
return actual.(TimerIface) //nolint:revive // type-safe: only TimerIface is stored
}
// Histogram obtains a histogram for the given name.
func (tmh *tallyMetricsHandler) Histogram(histogram string, unit MetricUnit) HistogramIface {
tally_metrics_handler.go
key := histogramCacheKey{name: histogram, unit: unit}
if v, ok := tmh.histograms.Load(key); ok {
return v.(HistogramIface) //nolint:revive // type-safe: only HistogramIface is stored
}
tmh.cachedTaggedScope(t).Histogram(histogram, tmh.perUnitBuckets[unit]).RecordValue(float64(i))
tally_metrics_handler.go
})
return actual.(HistogramIface) //nolint:revive // type-safe: only HistogramIface is stored
}
func (*tallyMetricsHandler) Close() error {
}
if len(t1) == 0 {
return nil
}
for i := range t1 {
nt, _ := normalizeTag(t1[i], e)
m[nt.Key] = nt.Value
}
return m
}
var Module = fx.Provide(
NewTaskFetcherFactory,
return m
},
nsreplication.NewNoopDataMerger,
nsreplication.NewDefaultAdmitter,
ServerSchedulerRateLimiterProvider,
PersistenceRateLimiterProvider,
return serializer
},
replicationTaskConverterFactoryProvider,
replicationTaskExecutorProvider,
metricsHandler metrics.Handler,
testHooks testhooks.TestHooks,
return NewEagerNamespaceRefresher(
metadataManager,
namespaceRegistry,
logger,
clientBean,
nsreplication.NewTaskExecutor(
clusterMetadata.GetCurrentClusterName(),
metadataManager,
dataMerger,
admitter,
logger,
testHooks,
),
clusterMetadata.GetCurrentClusterName(),
metricsHandler,
)
}
func replicationTaskConverterFactoryProvider(
config *configs.Config,
replicationTaskSerializer TaskSerializer,
return func(
historyEngine historyi.Engine,
shardContext historyi.ShardContext,
clientClusterName string,
serializer serialization.Serializer,
) SourceTaskConverter {
return NewSourceTaskConverter(
historyEngine,
}
return func(params TaskExecutorParams) TaskExecutor {
return NewTaskExecutor(
params.RemoteCluster,
queueFactory ctasks.SequentialTaskQueueFactory[TrackableExecutableTask],
lc fx.Lifecycle,
// SequentialScheduler has panic wrapper when executing task,
// if changing the executor, please make sure other executor has panic wrapper
scheduler := ctasks.NewSequentialScheduler[TrackableExecutableTask](
&ctasks.SequentialSchedulerOptions{
QueueSize: config.ReplicationProcessorSchedulerQueueSize(),
WorkerCount: config.ReplicationProcessorSchedulerWorkerCount,
},
WorkflowKeyHashFn,
queueFactory,
logger,
)
taskChannelKeyFn := func(e TrackableExecutableTask) ClusterChannelKey {
return ClusterChannelKey{
ClusterName: e.SourceClusterName(),
}
}
return 1
}
// This creates a per cluster channel.
// They share the same weight so it just does a round-robin on all clusters' tasks.
ctasks.InterleavedWeightedRoundRobinSchedulerOptions[TrackableExecutableTask, ClusterChannelKey]{
TaskChannelKeyFn: taskChannelKeyFn,
ChannelWeightFn: channelWeightFn,
},
scheduler,
logger,
)
lc.Append(fx.StartStopHook(rrScheduler.Start, rrScheduler.Stop))
return rrScheduler
}
metricsHandler metrics.Handler,
lc fx.Lifecycle,
// P-way parallelism for executions of the same workflow (per ReplicationLowPriorityTaskParallelism)
// is modeled as P distinct per-namespace-workflow queue IDs. We bucket by execution (RunID) so all
// low-priority tasks for one execution share a queue; the third field stores the slot index, not
// the run UUID.
queueFactory := func(task TrackableExecutableTask) ctasks.SequentialTaskQueue[TrackableExecutableTask] {
item := task.QueueID()
workflowKey, ok := item.(definition.WorkflowKey)
// SequentialScheduler has panic wrapper when executing task,
// if changing the executor, please make sure other executor has panic wrapper
&ctasks.SequentialSchedulerOptions{
QueueSize: config.ReplicationProcessorSchedulerQueueSize(),
WorkerCount: config.ReplicationLowPriorityProcessorSchedulerWorkerCount,
},
WorkflowKeyHashFn,
queueFactory,
logger,
)
taskChannelKeyFn := func(e TrackableExecutableTask) ClusterChannelKey {
return ClusterChannelKey{
ClusterName: e.SourceClusterName(),
}
}
return 1
}
var taskType string
var nsName namespace.Name
"")
}
replicationTask := t.ReplicationTask()
var taskType string
// This creates a per cluster channel.
// They share the same weight so it just does a round-robin on all clusters' tasks.
ctasks.InterleavedWeightedRoundRobinSchedulerOptions[TrackableExecutableTask, ClusterChannelKey]{
TaskChannelKeyFn: taskChannelKeyFn,
ChannelWeightFn: channelWeightFn,
},
scheduler,
logger,
)
ts := ctasks.NewRateLimitedScheduler[TrackableExecutableTask](
rrScheduler,
rateLimiter,
timeSource,
taskQuotaRequestFn,
taskMetricsTagsFn,
ctasks.RateLimitedSchedulerOptions{
Enabled: config.ReplicationEnableRateLimit,
EnableShadowMode: config.ReplicationEnableRateLimitShadowMode,
},
logger,
metricsHandler,
)
lc.Append(fx.StartStopHook(ts.Start, ts.Stop))
return ts
}
metricsHandler metrics.Handler,
config *configs.Config,
return func(task TrackableExecutableTask) ctasks.SequentialTaskQueue[TrackableExecutableTask] {
if config.EnableReplicationTaskBatching() {
return NewSequentialBatchableTaskQueue(task, nil, logger, metricsHandler)
func executableTaskConverterProvider(
processToolBox ProcessToolBox,
return NewExecutableTaskConverter(processToolBox)
}
func streamReceiverMonitorProvider(
processToolBox ProcessToolBox,
taskConverter ExecutableTaskConverter,
return NewStreamReceiverMonitor(
processToolBox,
taskConverter,
processToolBox.Config.EnableReplicationStream(),
)
}
func resendHandlerProvider(
logger log.Logger,
importer eventhandler.EventImporter,
return eventhandler.NewResendHandler(
namespaceRegistry,
clientBean,
serializer,
clusterMetadata,
func(ctx context.Context, namespaceId namespace.ID, workflowId string) (historyi.Engine, error) {
shardContext, err := shardController.GetShardByNamespaceWorkflow(
namespaceId,
serializer serialization.Serializer,
logger log.Logger,
return eventhandler.NewEventImporter(
historyFetcher,
func(ctx context.Context, namespaceId namespace.ID, workflowId string) (historyi.Engine, error) {
shardContext, err := shardController.GetShardByNamespaceWorkflow(
namespaceId,
replicationTaskSerializer TaskSerializer,
clusterMetadata cluster.Metadata,
return NewDLQWriterAdapter(dlqWriter, replicationTaskSerializer, clusterMetadata.GetCurrentClusterName())
}
func historyEventsHandlerProvider(
shardController shard.Controller,
logger log.Logger,
return eventhandler.NewHistoryEventsHandler(
clusterMetadata,
importer,
shardController,
logger,
)
}
func historyPaginatedFetcherProvider(
serializer serialization.Serializer,
logger log.Logger,
return eventhandler.NewHistoryPaginatedFetcher(
namespaceRegistry,
clientBean,
serializer,
logger,
)
}
enableDataLossMetrics EnableDataLossMetrics,
enableBestEffortDeleteTasksOnWorkflowUpdate EnableBestEffortDeleteTasksOnWorkflowUpdate,
factory := &factoryImpl{
dataStoreFactory: dataStoreFactory,
config: cfg,
serializer: serializer,
eventBlobCache: eventBlobCache,
metricsHandler: metricsHandler,
logger: logger,
clusterName: clusterName,
systemRateLimiter: systemRateLimiter,
namespaceRateLimiter: namespaceRateLimiter,
shardRateLimiter: shardRateLimiter,
healthSignals: healthSignals,
enableDataLossMetrics: dynamicconfig.BoolPropertyFn(enableDataLossMetrics),
enableBestEffortDeleteTasksOnWorkflowUpdate: dynamicconfig.BoolPropertyFn(enableBestEffortDeleteTasksOnWorkflowUpdate),
}
factory.initDependencies()
return factory
}
// NewTaskManager returns a new task manager
taskStore, err := f.dataStoreFactory.NewTaskStore()
if err != nil {
return nil, err
}
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewTaskPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewTaskPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewTaskPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
// NewFairTaskManager returns a new task fairness manager
taskStore, err := f.dataStoreFactory.NewFairTaskStore()
if err != nil {
return nil, err
}
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewTaskPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewTaskPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewTaskPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
// NewShardManager returns a new shard manager
shardStore, err := f.dataStoreFactory.NewShardStore()
if err != nil {
return nil, err
}
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewShardPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewShardPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewShardPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
// NewMetadataManager returns a new metadata manager
store, err := f.dataStoreFactory.NewMetadataStore()
if err != nil {
return nil, err
}
result := persistence.NewMetadataManagerImpl(store, f.serializer, f.logger, f.clusterName)
factory.go
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewMetadataPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewMetadataPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewMetadataPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
// NewClusterMetadataManager returns a new cluster metadata manager
func (f *factoryImpl) NewClusterMetadataManager() (persistence.ClusterMetadataManager, error) {
factory.go
store, err := f.dataStoreFactory.NewClusterMetadataStore()
if err != nil {
return nil, err
}
result := persistence.NewClusterMetadataManagerImpl(store, f.serializer, f.clusterName, f.logger)
factory.go
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewClusterMetadataPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewClusterMetadataPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewClusterMetadataPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
// NewExecutionManager returns a new execution manager
func (f *factoryImpl) NewExecutionManager() (persistence.ExecutionManager, error) {
factory.go
store, err := f.dataStoreFactory.NewExecutionStore()
if err != nil {
return nil, err
}
store,
f.serializer,
f.eventBlobCache,
f.logger,
f.config.TransactionSizeLimit,
f.enableBestEffortDeleteTasksOnWorkflowUpdate,
)
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewExecutionPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewExecutionPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewExecutionPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
func (f *factoryImpl) NewNamespaceReplicationQueue() (persistence.NamespaceReplicationQueue, error) {
factory.go
result, err := f.dataStoreFactory.NewQueue(persistence.NamespaceReplicationQueueType)
if err != nil {
return nil, err
}
result = persistence.NewQueuePersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewQueuePersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewQueuePersistenceRetryableClient(result, namespaceQueueRetryPolicy, IsNamespaceQueueTransientError)
return persistence.NewNamespaceReplicationQueue(result, f.serializer, f.clusterName, f.metricsHandler, f.logger)
}
func (f *factoryImpl) NewHistoryTaskQueueManager() (persistence.HistoryTaskQueueManager, error) {
factory.go
q, err := f.dataStoreFactory.NewQueueV2()
if err != nil {
return nil, err
}
}
func (f *factoryImpl) NewNexusEndpointManager() (persistence.NexusEndpointManager, error) {
factory.go
store, err := f.dataStoreFactory.NewNexusEndpointStore()
if err != nil {
return nil, err
}
if f.systemRateLimiter != nil && f.namespaceRateLimiter != nil {
result = persistence.NewNexusEndpointPersistenceRateLimitedClient(result, f.systemRateLimiter, f.namespaceRateLimiter, f.shardRateLimiter, f.logger)
factory.go
}
result = persistence.NewNexusEndpointPersistenceMetricsClient(result, f.metricsHandler, f.healthSignals, f.logger, f.enableDataLossMetrics)
}
result = persistence.NewNexusEndpointPersistenceRetryableClient(result, retryPolicy, IsPersistenceTransientError)
return result, nil
}
// Close closes this factory
f.dataStoreFactory.Close()
if f.healthSignals != nil {
f.healthSignals.Stop()
}
}
switch err.(type) {
// we retry on DataLoss errors because persistence layer is sometimes unreliable when we immediately read-after-write
case *serviceerror.Unavailable, *serviceerror.DataLoss:
}
if f.metricsHandler == nil && f.healthSignals == nil {
return
}
f.metricsHandler = metrics.NoopMetricsHandler
}
}
}
logger log.Logger,
metricsHandler metrics.Handler,
paginationFnProvider := func(r Range) collection.PaginationFn[tasks.Task] {
ctx, cancel := newQueueIOContext()
defer cancel()
request := &persistence.GetHistoryTasksRequest{
ShardID: shard.GetShardID(),
TaskCategory: category,
InclusiveMinTaskKey: tasks.NewKey(r.InclusiveMin.FireTime, 0),
ExclusiveMaxTaskKey: tasks.NewKey(
r.ExclusiveMax.FireTime.Add(common.ScheduledTaskMinPrecision),
0,
),
BatchSize: options.BatchSize(),
NextPageToken: paginationToken,
}
resp, err := shard.GetHistoryTasks(ctx, request)
if err != nil {
return nil, nil, err
}
resp.Tasks = resp.Tasks[1:]
}
for len(resp.Tasks) > 0 && !r.ContainsKey(resp.Tasks[len(resp.Tasks)-1].GetKey()) {
queue_scheduled.go
resp.Tasks = resp.Tasks[:len(resp.Tasks)-1]
resp.NextPageToken = nil
}
}
}
readerCompletionFn := func(readerID int64) {
return
}
case lookAheadCh <- struct{}{}:
default:
}
}
queueBase: newQueueBase(
shard,
category,
paginationFnProvider,
scheduler,
rescheduler,
executableFactory,
options,
hostRateLimiter,
readerCompletionFn,
GrouperNamespaceID{},
logger,
metricsHandler,
),
timerGate: timer.NewLocalGate(shard.GetTimeSource()),
newTimerCh: make(chan struct{}, 1),
lookAheadCh: lookAheadCh,
lookAheadRateLimitRequest: newReaderRequest(DefaultReaderId),
}
}
if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
defer p.logger.Info("", tag.LifeCycleStarted)
p.queueBase.Start()
p.shutdownWG.Add(1)
go p.processEventLoop()
p.notify(time.Time{})
}
if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
defer p.logger.Info("", tag.LifeCycleStopped)
close(p.shutdownCh)
p.timerGate.Close()
if success := common.AwaitWaitGroup(&p.shutdownWG, time.Minute); !success {
p.logger.Warn("", tag.LifeCycleStopTimedout)
}
}
if len(tasks) == 0 {
return
}
for _, task := range tasks {
ts := task.GetVisibilityTime()
if ts.Before(newTime) {
newTime = ts
}
}
}
defer p.shutdownWG.Done()
for {
select {
case <-p.shutdownCh:
return
}
return
metrics.NewTimerNotifyCounter.With(p.metricsHandler).Record(1)
p.processNewTime()
case <-p.lookAheadCh:
p.lookAheadTask()
case <-p.timerGate.FireCh():
p.processNewRange()
case <-p.checkpointTimer.C:
p.checkpoint()
}
p.newTimeLock.Lock()
defer p.newTimeLock.Unlock()
if !p.newTime.IsZero() && !newTime.Before(p.newTime) {
return
}
select {
case p.newTimerCh <- struct{}{}:
}
}
p.newTimeLock.Lock()
newTime := p.newTime
p.newTime = time.Time{}
p.newTimeLock.Unlock()
p.timerGate.Update(newTime)
}
rateLimitCtx, rateLimitCancel := context.WithTimeout(context.Background(), lookAheadRateLimitDelay)
rateLimitErr := p.readerRateLimiter.Wait(rateLimitCtx, p.lookAheadRateLimitRequest)
rateLimitCancel()
if rateLimitErr != nil {
deadline, _ := rateLimitCtx.Deadline()
p.timerGate.Update(deadline)
}
lookAheadMaxTime := lookAheadMinTime.Add(backoff.Jitter(
p.options.MaxPollInterval(),
p.options.MaxPollIntervalJitterCoefficient(),
))
ctx, cancel := newQueueIOContext()
defer cancel()
request := &persistence.GetHistoryTasksRequest{
ShardID: p.shard.GetShardID(),
TaskCategory: p.category,
InclusiveMinTaskKey: tasks.NewKey(lookAheadMinTime, 0),
ExclusiveMaxTaskKey: tasks.NewKey(lookAheadMaxTime, 0),
BatchSize: 1,
NextPageToken: nil,
}
response, err := p.shard.GetHistoryTasks(ctx, request)
if err != nil {
p.logger.Error("Failed to load look ahead task", tag.Error(err))
if common.IsResourceExhausted(err) {
}
p.timerGate.Update(response.Tasks[0].GetKey().FireTime)
return
// NOTE: with this we don't need a separate max poll timer, loading will be triggerred
// every maxPollInterval + jitter.
}
logger log.Logger,
metricsHandler metrics.Handler,
var readerScopes map[int64][]Scope
var exclusiveReaderHighWatermark tasks.Key
if persistenceState, ok := shard.GetQueueState(category); ok {
queueState := FromPersistenceQueueState(persistenceState)
readerScopes = queueState.readerScopes
exclusiveReaderHighWatermark = queueState.exclusiveReaderHighWatermark
if category.Type() == tasks.CategoryTypeImmediate {
ackLevel = ackLevel.Next()
}
}
monitor := newMonitor(category.Type(), shard.GetTimeSource(), &options.MonitorOptions)
queue_base.go
readerRateLimiter := newShardReaderRateLimiter(
options.MaxPollRPS,
hostReaderRateLimiter,
int64(options.MaxReaderCount()),
)
readerInitializer := func(readerID int64, slices []Slice) Reader {
if readerID != DefaultReaderId {
// non-default reader should not trigger task unloading
// otherwise those readers will keep loading, hit pending task count limit, unload, throttle, load, etc...
}
readerID,
slices,
&readerOptions,
scheduler,
rescheduler,
shard.GetTimeSource(),
readerRateLimiter,
monitor,
completionFn,
logger,
metricsHandler,
)
}
readerGroup := NewReaderGroup(readerInitializer)
for readerID, scopes := range readerScopes {
if len(scopes) == 0 {
continue
}
mitigator := newMitigator(readerGroup, monitor, logger, metricsHandler, options.MaxReaderCount, grouper)
queue_base.go
return &queueBase{
shard: shard,
status: common.DaemonStatusInitialized,
shutdownCh: make(chan struct{}),
category: category,
options: options,
scheduler: scheduler,
rescheduler: rescheduler,
timeSource: shard.GetTimeSource(),
monitor: monitor,
mitigator: mitigator,
grouper: grouper,
logger: logger,
metricsHandler: metricsHandler,
paginationFnProvider: paginationFnProvider,
executableFactory: executableFactory,
lastRangeID: -1, // start from an invalid rangeID
exclusiveDeletionHighWatermark: exclusiveDeletionHighWatermark,
nonReadableScope: NewScope(
NewRange(exclusiveReaderHighWatermark, tasks.MaximumKey),
predicates.Universal[tasks.Task](),
),
readerRateLimiter: readerRateLimiter,
readerGroup: readerGroup,
// pollTimer and checkpointTimer are initialized on Start()
checkpointRetrier: backoff.NewRetrier(
createCheckpointRetryPolicy(),
clock.NewRealTimeSource(),
),
alertCh: monitor.AlertCh(),
}
}
p.rescheduler.Start()
p.readerGroup.Start()
p.checkpointTimer = time.NewTimer(backoff.Jitter(
p.options.CheckpointInterval(),
p.options.CheckpointIntervalJitterCoefficient(),
))
}
p.monitor.Close()
p.readerGroup.Stop()
p.rescheduler.Stop()
p.checkpointTimer.Stop()
}
return p.category
}
func (p *queueBase) FailoverNamespace(
}
newMaxKey := p.shard.GetQueueExclusiveHighReadWatermark(p.category)
slices := make([]Slice, 0, 1)
if p.nonReadableScope.CanSplitByRange(newMaxKey) {
var newReadScope Scope
newReadScope, p.nonReadableScope = p.nonReadableScope.SplitByRange(newMaxKey)
slices = append(slices, NewSlice(
p.paginationFnProvider,
p.executableFactory,
p.monitor,
newReadScope,
p.grouper,
p.options.MaxPredicateSize,
p.options.ShrinkPredicateMaxPendingKeys,
p.metricsHandler,
))
}
if !ok {
p.readerGroup.NewReader(DefaultReaderId, slices...)
return
}
reader.AppendSlices(slices...)
p.nextForceNewSliceTime = now.Add(forceNewSliceDuration)
} else {
reader.MergeSlices(slices...)
}
}
policy := backoff.NewExponentialRetryPolicy(100 * time.Millisecond).
WithMaximumInterval(5 * time.Second).
WithExpirationInterval(backoff.NoInterval)
return policy
}
ctx, cancel := context.WithTimeout(context.Background(), queueIOTimeout)
ctx = headers.SetCallerInfo(ctx, headers.SystemBackgroundHighCallerInfo)
return ctx, cancel
}
logger log.Logger,
serializer serialization.Serializer,
visibilityManager, err := newVisibilityManagerFromDataStoreConfig(
persistenceCfg.GetVisibilityStoreConfig(),
persistenceResolver,
customVisibilityStoreFactory,
esProcessorConfig,
searchAttributesProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
maxReadQPS,
maxWriteQPS,
operatorRPSRatio,
slowQueryThreshold,
visibilityDisableOrderByClause,
visibilityEnableManualPagination,
visibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
if err != nil {
return nil, err
}
logger.Fatal("invalid config: visibility store must be configured")
return nil, nil
}
persistenceCfg.GetSecondaryVisibilityStoreConfig(),
persistenceResolver,
customVisibilityStoreFactory,
esProcessorConfig,
searchAttributesProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
maxReadQPS,
maxWriteQPS,
operatorRPSRatio,
slowQueryThreshold,
visibilityDisableOrderByClause,
visibilityEnableManualPagination,
visibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
if err != nil {
return nil, err
}
managerSelector := newDefaultManagerSelector(
visibilityManager,
searchAttributesMapperProvider searchattribute.MapperProvider,
chasmRegistry *chasm.Registry,
if visStore == nil {
return nil
}
"creating new visibility manager",
tag.String(visibilityPluginNameTag.Key, visibilityPluginNameTag.Value),
tag.String(visibilityIndexNameTag.Key, visibilityIndexNameTag.Value),
)
var visManager manager.VisibilityManager = newVisibilityManagerImpl(
visStore,
logger,
searchAttributesMapperProvider,
chasmRegistry,
)
// wrap with rate limiter
visManager = NewVisibilityManagerRateLimited(
visManager,
maxReadQPS,
maxWriteQPS,
operatorRPSRatio,
)
// wrap with metrics client
visManager = NewVisibilityManagerMetrics(
visManager,
metricsHandler,
logger,
slowQueryThreshold,
visibilityPluginNameTag,
visibilityIndexNameTag,
)
return visManager
}
logger log.Logger,
serializer serialization.Serializer,
visStore, err := newVisibilityStoreFromDataStoreConfig(
dsConfig,
persistenceResolver,
customVisibilityStoreFactory,
esProcessorConfig,
searchAttributesProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
visibilityDisableOrderByClause,
visibilityEnableManualPagination,
visibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
if err != nil {
return nil, err
}
return nil, nil
}
return newVisibilityManager(
visStore,
maxReadQPS,
maxWriteQPS,
operatorRPSRatio,
slowQueryThreshold,
metricsHandler,
metrics.VisibilityPluginNameTag(visStore.GetName()),
metrics.VisibilityIndexNameTag(visStore.GetIndexName()),
logger,
searchAttributesMapperProvider,
chasmRegistry,
), nil
}
logger log.Logger,
serializer serialization.Serializer,
var (
visStore store.VisibilityStore
err error
)
if dsConfig.SQL != nil {
visStore, err = sql.NewSQLVisibilityStore(
*dsConfig.SQL,
persistenceResolver,
searchAttributesProvider,
searchAttributesMapperProvider,
chasmRegistry,
visibilityEnableUnifiedQueryConverter,
logger,
metricsHandler,
serializer,
)
} else if dsConfig.Elasticsearch != nil {
visStore, err = elasticsearch.NewVisibilityStore(
dsConfig.Elasticsearch,
logger,
)
if customVisibilityStoreFactory == nil {
logger.Fatal("custom visibility store factory must be defined")
func NewVisibilityQueueFactory(
params visibilityQueueFactoryParams,
return &visibilityQueueFactory{
visibilityQueueFactoryParams: params,
QueueFactoryBase: QueueFactoryBase{
HostScheduler: queues.NewScheduler(
params.ClusterMetadata.GetCurrentClusterName(),
queues.SchedulerOptions{
WorkerCount: params.Config.VisibilityProcessorSchedulerWorkerCount,
ActiveNamespaceWeights: params.Config.VisibilityProcessorSchedulerActiveRoundRobinWeights,
StandbyNamespaceWeights: params.Config.VisibilityProcessorSchedulerStandbyRoundRobinWeights,
InactiveNamespaceDeletionDelay: params.Config.TaskSchedulerInactiveChannelDeletionDelay,
ExecutionAwareSchedulerOptions: ctasks.ExecutionAwareSchedulerOptions{
Enabled: params.Config.TaskSchedulerEnableExecutionQueueScheduler,
MaxQueues: params.Config.TaskSchedulerExecutionQueueSchedulerMaxQueues,
QueueTTL: params.Config.TaskSchedulerExecutionQueueSchedulerQueueTTL,
QueueConcurrency: params.Config.TaskSchedulerExecutionQueueSchedulerQueueConcurrency,
},
},
params.NamespaceRegistry,
params.Logger,
params.MetricsHandler,
params.TimeSource,
),
HostPriorityAssigner: queues.NewPriorityAssigner(
params.NamespaceRegistry,
params.ClusterMetadata.GetCurrentClusterName(),
),
HostReaderRateLimiter: queues.NewReaderPriorityRateLimiter(
NewHostRateLimiterRateFn(
params.Config.VisibilityProcessorMaxPollHostRPS,
params.Config.PersistenceMaxQPS,
visibilityQueuePersistenceMaxRPSRatio,
),
int64(params.Config.VisibilityQueueMaxReaderCount()),
),
Tracer: params.TracerProvider.Tracer(telemetry.ComponentQueueVisibility),
},
}
}
func (f *visibilityQueueFactory) CreateQueue(
shard historyi.ShardContext,
logger := log.With(shard.GetLogger(), tag.ComponentVisibilityQueue)
metricsHandler := f.MetricsHandler.WithTags(metrics.OperationTag(metrics.OperationVisibilityQueueProcessorScope))
shardScheduler := queues.NewRateLimitedScheduler(
f.HostScheduler,
queues.RateLimitedSchedulerOptions{
Enabled: f.Config.TaskSchedulerEnableRateLimiter,
EnableShadowMode: f.Config.TaskSchedulerEnableRateLimiterShadowMode,
StartupDelay: f.Config.TaskSchedulerRateLimiterStartupDelay,
},
f.ClusterMetadata.GetCurrentClusterName(),
f.NamespaceRegistry,
f.SchedulerRateLimiter,
f.TimeSource,
f.ChasmRegistry,
logger,
metricsHandler,
)
rescheduler := queues.NewRescheduler(
shardScheduler,
shard.GetTimeSource(),
logger,
metricsHandler,
)
executor := newVisibilityQueueTaskExecutor(
shard,
f.WorkflowCache,
f.VisibilityMgr,
logger,
f.MetricsHandler,
f.Config.VisibilityProcessorEnsureCloseBeforeDelete,
f.Config.VisibilityProcessorEnableCloseWorkflowCleanup,
f.Config.VisibilityProcessorRelocateAttributesMinBlobSize,
f.Config.ExternalPayloadsEnabled,
)
if f.ExecutorWrapper != nil {
executor = f.ExecutorWrapper.Wrap(executor)
}
executor,
shardScheduler,
rescheduler,
f.HostPriorityAssigner,
shard.GetTimeSource(),
shard.GetNamespaceRegistry(),
shard.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(
shard,
tasks.CategoryVisibility,
shardScheduler,
rescheduler,
&queues.Options{
ReaderOptions: queues.ReaderOptions{
BatchSize: f.Config.VisibilityTaskBatchSize,
MaxPendingTasksCount: f.Config.QueuePendingTaskMaxCount,
PollBackoffInterval: f.Config.VisibilityProcessorPollBackoffInterval,
MaxPredicateSize: f.Config.QueueMaxPredicateSize,
},
MonitorOptions: queues.MonitorOptions{
PendingTasksCriticalCount: f.Config.QueuePendingTaskCriticalCount,
ReaderStuckCriticalAttempts: f.Config.QueueReaderStuckCriticalAttempts,
SliceCountCriticalThreshold: f.Config.QueueCriticalSlicesCount,
},
MaxPollRPS: f.Config.VisibilityProcessorMaxPollRPS,
MaxPollInterval: f.Config.VisibilityProcessorMaxPollInterval,
MaxPollIntervalJitterCoefficient: f.Config.VisibilityProcessorMaxPollIntervalJitterCoefficient,
CheckpointInterval: f.Config.VisibilityProcessorUpdateAckInterval,
CheckpointIntervalJitterCoefficient: f.Config.VisibilityProcessorUpdateAckIntervalJitterCoefficient,
MaxReaderCount: f.Config.VisibilityQueueMaxReaderCount,
MoveGroupTaskCountBase: f.Config.QueueMoveGroupTaskCountBase,
MoveGroupTaskCountMultiplier: f.Config.QueueMoveGroupTaskCountMultiplier,
ShrinkPredicateMaxPendingKeys: f.Config.QueueShrinkPredicateMaxPendingKeys,
},
f.HostReaderRateLimiter,
queues.GrouperNamespaceID{},
logger,
metricsHandler,
factory,
nil, // taskPostProcessor
)
}
logger log.Logger,
metricsHandler metrics.Handler,
sliceList := list.New()
for _, slice := range slices {
}
rateLimitContext, rateLimitContextCancel := context.WithCancel(context.Background())
return &ReaderImpl{
readerID: readerID,
options: options,
scheduler: scheduler,
rescheduler: rescheduler,
timeSource: timeSource,
ratelimiter: ratelimiter,
monitor: monitor,
completionFn: completionFn,
logger: log.With(logger, tag.QueueReaderID(readerID)),
metricsHandler: metricsHandler,
status: common.DaemonStatusInitialized,
shutdownCh: make(chan struct{}),
slices: sliceList,
nextReadSlice: sliceList.Front(),
notifyCh: make(chan struct{}, 1),
retrier: backoff.NewRetrier(
common.CreateReadTaskRetryPolicy(),
clock.NewRealTimeSource(),
),
rateLimitContext: rateLimitContext,
rateLimitContextCancel: rateLimitContextCancel,
rateLimiterRequest: newReaderRequest(readerID),
}
}
if !atomic.CompareAndSwapInt32(
&r.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
go r.eventLoop()
r.notify()
r.logger.Info("queue reader started", tag.LifeCycleStarted)
}
if !atomic.CompareAndSwapInt32(
&r.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
close(r.shutdownCh)
r.rateLimitContextCancel()
if success := common.AwaitWaitGroup(&r.shutdownWG, time.Minute); !success {
r.logger.Warn("queue reader shutdown timed out waiting for event loop", tag.LifeCycleStopTimedout)
}
}
}
if len(incomingSlices) == 0 {
return
}
if back := r.slices.Back(); back != nil {
lastSliceRange := back.Value.(Slice).Scope().Range
firstIncomingRange := incomingSlices[0].Scope().Range
if lastSliceRange.ExclusiveMax.CompareTo(firstIncomingRange.InclusiveMin) > 0 {
panic(fmt.Sprintf(
"Can not append slice to existing list of slices, incoming slice range: %v, existing slice range: %v ",
}
defer r.Unlock()
for _, incomingSlice := range incomingSlices {
if scope := incomingSlice.Scope(); scope.IsEmpty() {
}
r.slices.PushBack(incomingSlice)
}
r.monitor.SetSliceCount(r.readerID, r.slices.Len())
}
}
defer func() {
}()
// prioritize shutdown
select {
case <-r.shutdownCh:
return
}
return
r.loadAndSubmitTasks()
}
}
}
if err := r.ratelimiter.Wait(r.rateLimitContext, r.rateLimiterRequest); err != nil {
if r.rateLimitContext.Err() != nil {
return
}
defer r.Unlock()
if !r.verifyPendingTaskSize() {
r.pauseLocked(r.options.PollBackoffInterval())
}
return
}
r.completionFn(r.readerID)
return
}
tasks, err := loadSlice.SelectTasks(r.readerID, r.options.BatchSize())
if err != nil {
r.logger.Error("Queue reader unable to retrieve tasks", tag.Error(err))
if common.IsResourceExhausted(err) {
return
}
if len(tasks) != 0 {
for _, task := range tasks {
r.submit(task)
// No more tasks to load, trigger completion callback.
}
r.nextReadSlice = nil
for element := r.slices.Front(); element != nil; element = element.Next() {
if element.Value.(Slice).MoreTasks() {
break
}
}
return
}
// No more tasks to load, trigger completion callback.
}
return r.monitor.GetTotalPendingTaskCount() < r.options.MaxPendingTasksCount()
}
func mergeOrAppendSlice(
func NewConfig(
dc *dynamicconfig.Collection,
return &Config{
PersistenceMaxQPS: dynamicconfig.MatchingPersistenceMaxQPS.Get(dc),
PersistenceGlobalMaxQPS: dynamicconfig.MatchingPersistenceGlobalMaxQPS.Get(dc),
PersistenceNamespaceMaxQPS: dynamicconfig.MatchingPersistenceNamespaceMaxQPS.Get(dc),
PersistenceGlobalNamespaceMaxQPS: dynamicconfig.MatchingPersistenceGlobalNamespaceMaxQPS.Get(dc),
PersistencePerShardNamespaceMaxQPS: dynamicconfig.DefaultPerShardNamespaceRPSMax,
PersistenceDynamicRateLimitingParams: dynamicconfig.MatchingPersistenceDynamicRateLimitingParams.Get(dc),
PersistenceQPSBurstRatio: dynamicconfig.PersistenceQPSBurstRatio.Get(dc),
SyncMatchWaitDuration: dynamicconfig.MatchingSyncMatchWaitDuration.Get(dc),
HistoryMaxPageSize: dynamicconfig.MatchingHistoryMaxPageSize.Get(dc),
EnableDeployments: dynamicconfig.EnableDeployments.Get(dc), // [cleanup-wv-pre-release]
EnableDeploymentVersions: dynamicconfig.EnableDeploymentVersions.Get(dc),
UseRevisionNumberForWorkerVersioning: dynamicconfig.UseRevisionNumberForWorkerVersioning.Get(dc),
MaxTaskQueuesInDeployment: dynamicconfig.MatchingMaxTaskQueuesInDeployment.Get(dc),
MaxVersionsInTaskQueue: dynamicconfig.MatchingMaxVersionsInTaskQueue.Get(dc),
RPS: dynamicconfig.MatchingRPS.Get(dc),
NamespaceRPS: dynamicconfig.MatchingNamespaceRPS.Get(dc),
OperatorRPSRatio: dynamicconfig.OperatorRPSRatio.Get(dc),
PollWaitForNamespaceRateLimitToken: dynamicconfig.PollWaitForNamespaceRateLimitToken.Get(dc),
RangeSize: 100000,
NewMatcherSub: dynamicconfig.MatchingUseNewMatcher.Subscribe(dc),
EnableFairnessSub: dynamicconfig.MatchingEnableFairness.Subscribe(dc),
EnableMigration: dynamicconfig.MatchingEnableMigration.Get(dc),
AutoEnableV2Sub: dynamicconfig.MatchingAutoEnableV2.Subscribe(dc),
GetTasksBatchSize: dynamicconfig.MatchingGetTasksBatchSize.Get(dc),
GetTasksReloadAt: dynamicconfig.MatchingGetTasksReloadAt.Get(dc),
ForceReadTasksOnWrite: dynamicconfig.MatchingForceReadTasksOnWrite.Get(dc),
UpdateAckInterval: dynamicconfig.MatchingUpdateAckInterval.Get(dc),
MetadataUpdateOnAppendInterval: dynamicconfig.MatchingMetadataUpdateOnAppendInterval.Get(dc),
MaxTaskQueueIdleTime: dynamicconfig.MatchingMaxTaskQueueIdleTime.Get(dc),
LongPollExpirationInterval: dynamicconfig.MatchingLongPollExpirationInterval.Get(dc),
BacklogTaskForwardTimeout: dynamicconfig.MatchingBacklogTaskForwardTimeout.Get(dc),
ForwardPollRetryMaxInterval: dynamicconfig.MatchingForwardPollRetryMaxInterval.Get(dc),
MinTaskThrottlingBurstSize: dynamicconfig.MatchingMinTaskThrottlingBurstSize.Get(dc),
MaxTaskDeleteBatchSize: dynamicconfig.MatchingMaxTaskDeleteBatchSize.Get(dc),
TaskDeleteInterval: dynamicconfig.MatchingTaskDeleteInterval.Get(dc),
OutstandingTaskAppendsThreshold: dynamicconfig.MatchingOutstandingTaskAppendsThreshold.Get(dc),
MaxTaskBatchSize: dynamicconfig.MatchingMaxTaskBatchSize.Get(dc),
ThrottledLogRPS: dynamicconfig.MatchingThrottledLogRPS.Get(dc),
NumTaskqueueWritePartitions: dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(dc),
NumTaskqueueReadPartitions: dynamicconfig.MatchingNumTaskqueueReadPartitions.Get(dc),
NumTaskqueueReadPartitionsSub: dynamicconfig.MatchingNumTaskqueueReadPartitions.Subscribe(dc),
BreakdownMetricsByTaskQueue: dynamicconfig.MetricsBreakdownByTaskQueue.Get(dc),
BreakdownMetricsByPartition: dynamicconfig.MetricsBreakdownByPartition.Get(dc),
BreakdownMetricsByBuildID: dynamicconfig.MetricsBreakdownByBuildID.Get(dc),
EnableWorkerPluginMetrics: dynamicconfig.MatchingEnableWorkerPluginMetrics.Get(dc),
EnablePollerAutoscalingMetrics: dynamicconfig.MatchingEnablePollerAutoscalingMetrics.Get(dc),
ExternalPayloadsEnabled: dynamicconfig.ExternalPayloadsEnabled.Get(dc),
WorkerRegistryNumBuckets: dynamicconfig.MatchingWorkerRegistryNumBuckets.Get(dc),
WorkerRegistryEntryTTL: dynamicconfig.MatchingWorkerRegistryEntryTTL.Get(dc),
WorkerRegistryMinEvictAge: dynamicconfig.MatchingWorkerRegistryMinEvictAge.Get(dc),
WorkerRegistryMaxEntries: dynamicconfig.MatchingWorkerRegistryMaxEntries.Get(dc),
WorkerRegistryEvictionInterval: dynamicconfig.MatchingWorkerRegistryEvictionInterval.Get(dc),
ForwarderMaxOutstandingPolls: dynamicconfig.MatchingForwarderMaxOutstandingPolls.Get(dc),
ForwarderMaxOutstandingTasks: dynamicconfig.MatchingForwarderMaxOutstandingTasks.Get(dc),
ForwarderMaxRatePerSecond: dynamicconfig.MatchingForwarderMaxRatePerSecond.Get(dc),
ForwarderMaxChildrenPerNode: dynamicconfig.MatchingForwarderMaxChildrenPerNode.Get(dc),
AlignMembershipChange: dynamicconfig.MatchingAlignMembershipChange.Get(dc),
ShutdownDrainDuration: dynamicconfig.MatchingShutdownDrainDuration.Get(dc),
VersionCompatibleSetLimitPerQueue: dynamicconfig.VersionCompatibleSetLimitPerQueue.Get(dc),
VersionBuildIdLimitPerQueue: dynamicconfig.VersionBuildIdLimitPerQueue.Get(dc),
AssignmentRuleLimitPerQueue: dynamicconfig.AssignmentRuleLimitPerQueue.Get(dc),
RedirectRuleLimitPerQueue: dynamicconfig.RedirectRuleLimitPerQueue.Get(dc),
RedirectRuleMaxUpstreamBuildIDsPerQueue: dynamicconfig.RedirectRuleMaxUpstreamBuildIDsPerQueue.Get(dc),
DeletedRuleRetentionTime: dynamicconfig.MatchingDeletedRuleRetentionTime.Get(dc),
PollerHistoryTTL: dynamicconfig.PollerHistoryTTL.Get(dc),
EnableMatchingFanOutForPollCancellation: dynamicconfig.EnableMatchingFanOutForPollCancellation.Get(dc),
ReachabilityBuildIdVisibilityGracePeriod: dynamicconfig.ReachabilityBuildIdVisibilityGracePeriod.Get(dc),
ReachabilityCacheOpenWFsTTL: dynamicconfig.ReachabilityCacheOpenWFsTTL.Get(dc),
ReachabilityCacheClosedWFsTTL: dynamicconfig.ReachabilityCacheClosedWFsTTL.Get(dc),
TaskQueueLimitPerBuildId: dynamicconfig.TaskQueuesPerBuildIdLimit.Get(dc),
GetUserDataLongPollTimeout: dynamicconfig.MatchingGetUserDataLongPollTimeout.Get(dc), // Use -10 seconds so that we send back empty response instead of timeout
GetUserDataRefresh: dynamicconfig.MatchingGetUserDataRefresh.Get(dc),
EphemeralDataUpdateInterval: dynamicconfig.MatchingEphemeralDataUpdateInterval.Get(dc),
BacklogMetricsEmitInterval: dynamicconfig.MatchingBacklogMetricsEmitInterval.Get(dc),
PriorityBacklogForwarding: dynamicconfig.MatchingPriorityBacklogForwarding.Get(dc),
BacklogNegligibleAge: dynamicconfig.MatchingBacklogNegligibleAge.Get(dc),
MaxWaitForPollerBeforeFwd: dynamicconfig.MatchingMaxWaitForPollerBeforeFwd.Get(dc),
QueryPollerUnavailableWindow: dynamicconfig.QueryPollerUnavailableWindow.Get(dc),
WorkerControllerNoPollerHookWindow: dynamicconfig.WorkerControllerNoPollerHookWindow.Get(dc),
EmitTaskDispatchLatencyAtPoll: dynamicconfig.MatchingEmitTaskDispatchLatencyAtPoll.Get(dc),
QueryWorkflowTaskTimeoutLogRate: dynamicconfig.MatchingQueryWorkflowTaskTimeoutLogRate.Get(dc),
MembershipUnloadDelay: dynamicconfig.MatchingMembershipUnloadDelay.Get(dc),
TaskQueueInfoByBuildIdTTL: dynamicconfig.TaskQueueInfoByBuildIdTTL.Get(dc),
PriorityLevels: dynamicconfig.MatchingPriorityLevels.Get(dc),
RateLimiterRefreshInterval: time.Minute,
FairnessKeyRateLimitCacheSize: dynamicconfig.MatchingFairnessKeyRateLimitCacheSize.Get(dc),
MaxFairnessKeyWeightOverrides: dynamicconfig.MatchingMaxFairnessKeyWeightOverrides.Get(dc),
MaxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
AdminNamespaceToPartitionDispatchRate: dynamicconfig.AdminMatchingNamespaceToPartitionDispatchRate.Get(dc),
AdminNamespaceToPartitionRateSub: dynamicconfig.AdminMatchingNamespaceToPartitionDispatchRate.Subscribe(dc),
AdminNamespaceTaskqueueToPartitionDispatchRate: dynamicconfig.AdminMatchingNamespaceTaskqueueToPartitionDispatchRate.Get(dc),
AdminNamespaceTaskqueueToPartitionRateSub: dynamicconfig.AdminMatchingNamespaceTaskqueueToPartitionDispatchRate.Subscribe(dc),
VisibilityPersistenceMaxReadQPS: dynamicconfig.VisibilityPersistenceMaxReadQPS.Get(dc),
VisibilityPersistenceMaxWriteQPS: dynamicconfig.VisibilityPersistenceMaxWriteQPS.Get(dc),
VisibilityPersistenceSlowQueryThreshold: dynamicconfig.VisibilityPersistenceSlowQueryThreshold.Get(dc),
EnableReadFromSecondaryVisibility: dynamicconfig.EnableReadFromSecondaryVisibility.Get(dc),
VisibilityEnableShadowReadMode: dynamicconfig.VisibilityEnableShadowReadMode.Get(dc),
VisibilityDisableOrderByClause: dynamicconfig.VisibilityDisableOrderByClause.Get(dc),
VisibilityEnableManualPagination: dynamicconfig.VisibilityEnableManualPagination.Get(dc),
VisibilityEnableUnifiedQueryConverter: dynamicconfig.VisibilityEnableUnifiedQueryConverter.Get(dc),
ListNexusEndpointsLongPollTimeout: dynamicconfig.MatchingListNexusEndpointsLongPollTimeout.Get(dc),
NexusEndpointsRefreshInterval: dynamicconfig.MatchingNexusEndpointsRefreshInterval.Get(dc),
MinDispatchTaskTimeout: nexusoperations.MinDispatchTaskTimeout.Get(dc),
PollerScalingBacklogAgeScaleUp: dynamicconfig.MatchingPollerScalingBacklogAgeScaleUp.Get(dc),
PollerScalingWaitTime: dynamicconfig.MatchingPollerScalingWaitTime.Get(dc),
PollerScalingDecisionsPerSecond: dynamicconfig.MatchingPollerScalingDecisionsPerSecond.Get(dc),
PollerScalingTaskAddToDispatchRatio: dynamicconfig.MatchingPollerScalingTaskAddToDispatchRatio.Get(dc),
EnablePollerScalingDecisionMetrics: dynamicconfig.MatchingEnablePollerScalingDecisionMetrics.Get(dc),
FairnessCounter: dynamicconfig.MatchingFairnessCounter.Get(dc),
FairnessPassDither: dynamicconfig.MatchingFairnessPassDither.Get(dc),
PartitionScaleAllowedDrift: dynamicconfig.MatchingPartitionScaleAllowedDrift.Get(dc),
PartitionScaleManagerSettings: dynamicconfig.MatchingPartitionScaleManager.Get(dc),
LogAllReqErrors: dynamicconfig.LogAllReqErrors.Get(dc),
RateLimitFractionProvider: defaultTaskQueueRateLimitFractionProvider,
}
}
func newTaskQueueConfig(tq *tqid.TaskQueue, config *Config, ns namespace.Name) *taskQueueConfig {
taskHookFactories []hooks.TaskHookFactory,
partitionScalerFactory PartitionScalerFactory,
scopedMetricsHandler := metricsHandler.WithTags(metrics.OperationTag(metrics.MatchingEngineScope))
e := &matchingEngineImpl{
status: common.DaemonStatusInitialized,
taskManager: taskManager,
fairTaskManager: fairTaskManager,
historyClient: historyClient,
matchingRawClient: matchingRawClient,
tokenSerializer: tasktoken.NewSerializer(),
workerDeploymentClient: workerDeploymentClient,
historySerializer: historySerializer,
logger: log.With(logger, tag.ComponentMatchingEngine),
throttledLogger: log.With(throttledLogger, tag.ComponentMatchingEngine),
namespaceRegistry: namespaceRegistry,
hostInfoProvider: hostInfoProvider,
serviceResolver: resolver,
membershipChangedCh: make(chan *membership.ChangedEvent, 1), // allow one signal to be buffered while we're working
clusterMeta: clusterMeta,
timeSource: clock.NewRealTimeSource(), // No need to mock this at the moment
visibilityManager: visibilityManager,
nexusEndpointClient: newEndpointClient(config.NexusEndpointsRefreshInterval, nexusEndpointManager),
// nexusEndpointsOwnershipLostCh initialized below
saProvider: saProvider,
saMapperProvider: saMapperProvider,
metricsHandler: scopedMetricsHandler,
partitions: make(map[tqid.PartitionKey]taskQueuePartitionManager),
gaugeMetrics: gaugeMetrics{
loadedTaskQueueFamilyCount: make(map[taskQueueCounterKey]int),
loadedTaskQueueCount: make(map[taskQueueCounterKey]int),
loadedTaskQueuePartitionCount: make(map[taskQueueCounterKey]int),
loadedPhysicalTaskQueueCount: make(map[taskQueueCounterKey]int),
},
config: config,
versionChecker: headers.NewDefaultVersionChecker(),
testHooks: testHooks,
queryResults: collection.NewSyncMap[string, chan *queryResult](),
nexusResults: collection.NewSyncMap[string, chan *nexusResult](),
outstandingPollers: collection.NewSyncMap[string, context.CancelFunc](),
workerInstancePollers: workerPollerTracker{pollers: make(map[string]map[string]context.CancelFunc)},
shutdownWorkers: cache.New(shutdownWorkersCacheMaxSize, &cache.Options{TTL: shutdownWorkersCacheTTL}),
namespaceReplicationQueue: namespaceReplicationQueue,
userDataUpdateBatchers: collection.NewSyncMap[namespace.ID, *stream_batcher.Batcher[*userDataUpdate, error]](),
rateLimiter: rateLimiter,
taskHookFactories: taskHookFactories,
partitionScalerFactory: partitionScalerFactory,
}
e.nexusEndpointsOwnershipLostCh.Store(make(chan struct{}))
e.reachabilityCache = newReachabilityCache(
metrics.NoopMetricsHandler,
visibilityManager,
e.config.ReachabilityCacheOpenWFsTTL(),
e.config.ReachabilityCacheClosedWFsTTL())
return e
}
if !atomic.CompareAndSwapInt32(
&e.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
_ = e.serviceResolver.AddListener(e.listenerKey(), e.membershipChangedCh)
}
if !atomic.CompareAndSwapInt32(
&e.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
close(e.membershipChangedCh)
e.nexusEndpointClient.notifyOwnershipChanged(false)
for _, l := range e.getTaskQueuePartitions(math.MaxInt32) {
l.Stop(unloadCauseShuttingDown)
}
}
return fmt.Sprintf("matchingEngine[%p]", e)
}
self := e.hostInfoProvider.HostInfo().Identity()
rc, ok := e.matchingRawClient.(matching.RoutingClient)
if !ok {
e.logger.Warn("watchMembership found non-routing matching client")
return // this should only happen in unit tests
}
addr, err := rc.Route(p)
// don't take action on lookup error
}
delay := e.config.MembershipUnloadDelay()
if delay == 0 {
continue
}
// Check all our loaded partitions to see if we lost ownership of any of them.
e.partitionsLock.RLock()
partitions := make([]tqid.Partition, 0, len(e.partitions))
for _, pm := range e.partitions {
partitions = append(partitions, pm.Partition())
}
partitions = util.FilterSlice(partitions, ownedByOther)
const batchSize = 100
for i := 0; i < len(partitions); i += batchSize {
// We don't own these anymore, but don't unload them immediately, wait a few seconds to ensure
// the membership update has propagated everywhere so that they won't get immediately re-loaded.
}
func (e *matchingEngineImpl) getTaskQueuePartitions(maxCount int) (lists []taskQueuePartitionManager) {
matching_engine.go
e.partitionsLock.RLock()
defer e.partitionsLock.RUnlock()
lists = make([]taskQueuePartitionManager, 0, len(e.partitions))
count := 0
for _, tlMgr := range e.partitions {
lists = append(lists, tlMgr)
count++
}
func (e *matchingEngineImpl) checkNexusEndpointsOwnership() (bool, <-chan struct{}, error) {
matching_engine.go
// Get the channel before checking the condition to prevent the channel from being closed while we're running this
// check.
ch := e.nexusEndpointsOwnershipLostCh.Load().(chan struct{}) //nolint:revive // type is always chan struct{}
self := e.hostInfoProvider.HostInfo().Identity()
owner, err := e.serviceResolver.Lookup(nexusEndpointsTablePartitionRoutingKey)
if err != nil {
return false, nil, fmt.Errorf("cannot resolve Nexus endpoints partition owner: %w", err)
matching_engine.go
}
}
// We don't care about the channel returned here. This method is ensured to only be called from the single
// watchMembership method and is the only way the channel may be replaced.
isOwner, _, err := e.checkNexusEndpointsOwnership()
if err != nil {
e.logger.Error("Failed to check Nexus endpoints ownership", tag.Error(err))
matching_engine.go
return
}
close(e.nexusEndpointsOwnershipLostCh.Swap(make(chan struct{})).(chan struct{})) //nolint:revive // type is always chan struct{}
}
}
metricsHandler metrics.Handler,
logger log.Logger,
hostRateFn := func() float64 { return float64(hostMaxQPS()) }
// host-level dynamic rate limiter
newPriorityDynamicRateLimiter(
hostRateFn,
requestPriorityFn,
operatorRPSRatio,
burstRatio,
healthSignals,
dynamicParams,
metricsHandler,
logger,
),
// basic host-level rate limiter
newPriorityRateLimiter(
hostRateFn,
requestPriorityFn,
operatorRPSRatio,
burstRatio,
),
)
}
operatorRPSRatio OperatorRPSRatio,
burstRatio PersistenceBurstRatio,
return newPriorityNamespaceRateLimiter(
namespaceMaxQPS,
hostMaxQPS,
requestPriorityFn,
operatorRPSRatio,
burstRatio,
)
}
func NewPriorityNamespaceShardRateLimiter(
operatorRPSRatio OperatorRPSRatio,
burstRatio PersistenceBurstRatio,
return newPerShardPerNamespacePriorityRateLimiter(
perShardNamespaceMaxQPS,
hostMaxQPS,
requestPriorityFn,
operatorRPSRatio,
burstRatio,
)
}
func newPerShardPerNamespacePriorityRateLimiter(
operatorRPSRatio OperatorRPSRatio,
burstRatio PersistenceBurstRatio,
return quotas.NewMapRequestRateLimiter(func(req quotas.Request) quotas.RequestRateLimiter {
if hasCaller(req) && hasCallerSegment(req) {
return newPriorityRateLimiter(func() float64 {
if perShardNamespaceMaxQPS == nil || perShardNamespaceMaxQPS(req.Caller) <= 0 {
)
}
},
perShardPerNamespaceKeyFn,
}
return perShardPerNamespaceKey{
namespaceID: req.Caller,
shardID: req.CallerSegment,
}
}
func newPriorityNamespaceRateLimiter(
operatorRPSRatio OperatorRPSRatio,
burstRatio PersistenceBurstRatio,
return quotas.NewNamespaceRequestRateLimiter(func(req quotas.Request) quotas.RequestRateLimiter {
if hasCaller(req) {
return newPriorityRateLimiter(
func() float64 {
operatorRPSRatio OperatorRPSRatio,
burstRatio PersistenceBurstRatio,
rateLimiters := make(map[int]quotas.RequestRateLimiter)
for priority := range RequestPrioritiesOrdered {
if priority == CallerTypeDefaultPriority[headers.CallerTypeOperator] {
rateLimiters[priority] = quotas.NewRequestRateLimiterAdapter(
quotas.NewDefaultRateLimiter(
operatorRateFn(rateFn, operatorRPSRatio),
quotas.BurstRatioFn(burstRatio),
),
)
} else {
rateLimiters[priority] = quotas.NewRequestRateLimiterAdapter(
quotas.NewDefaultRateLimiter(
rateFn,
quotas.BurstRatioFn(burstRatio),
),
)
}
}
requestPriorityFn,
rateLimiters,
)
}
metricsHandler metrics.Handler,
logger log.Logger,
rateLimiters := make(map[int]quotas.RequestRateLimiter)
for priority := range RequestPrioritiesOrdered {
// TODO: refactor this so dynamic rate adjustment is global for all priorities
if priority == CallerTypeDefaultPriority[headers.CallerTypeOperator] {
rateLimiters[priority] = NewHealthRequestRateLimiterImpl(
healthSignals,
operatorRateFn(rateFn, operatorRPSRatio),
dynamicParams,
burstRatio,
metricsHandler,
logger,
)
} else {
rateLimiters[priority] = NewHealthRequestRateLimiterImpl(
healthSignals,
rateFn,
dynamicParams,
burstRatio,
metricsHandler,
logger,
)
}
}
requestPriorityFn,
rateLimiters,
)
}
switch req.CallerType {
case headers.CallerTypeOperator:
return CallerTypeDefaultPriority[req.CallerType]
}
return CallerTypeDefaultPriority[req.CallerType]
if priority, ok := BackgroundTypeAPIPriorityOverride[req.API]; ok {
return priority
}
return CallerTypeDefaultPriority[req.CallerType]
case headers.CallerTypePreemptable:
return CallerTypeDefaultPriority[req.CallerType]
// default requests to API priority to be consistent with existing behavior
return CallerTypeDefaultPriority[headers.CallerTypeAPI]
}
}
func operatorRateFn(rateFn quotas.RateFn, operatorRPSRatio OperatorRPSRatio) quotas.RateFn {
quotas.go
return func() float64 {
return operatorRPSRatio() * rateFn()
}
}
return req.Caller != "" && req.Caller != headers.CallerNameSystem
}
func hasCallerSegment(req quotas.Request) bool {
)
return grpc.NewServer(grpcServerOptions...)
}
func ConfigProvider(
persistenceConfig config.Persistence,
rateLimitFractionProvider TaskQueueRateLimitFractionProvider,
cfg := NewConfig(dc)
cfg.RateLimitFractionProvider = rateLimitFractionProvider
return cfg
}
func ServiceErrorInterceptorProvider(
dc *dynamicconfig.Collection,
return interceptor.NewServiceErrorInterceptor(
dynamicconfig.MaxServiceErrorMessageLength.Get(dc),
)
}
return interceptor.NewRetryableInterceptor(
common.CreateMatchingHandlerRetryPolicy(),
common.IsServiceHandlerRetryableError,
)
}
func ErrorHandlerProvider(
logger log.Logger,
serviceConfig *Config,
return interceptor.NewRequestErrorHandler(
logger,
serviceConfig.LogAllReqErrors,
)
}
func TelemetryInterceptorProvider(
serviceConfig *Config,
requestErrorHandler *interceptor.RequestErrorHandler,
return interceptor.NewTelemetryInterceptor(
namespaceRegistry,
metricsHandler,
logger,
serviceConfig.LogAllReqErrors,
requestErrorHandler,
)
}
func ThrottledLoggerRpsFnProvider(serviceConfig *Config) resource.ThrottledLoggerRpsFn {
fx.go
return func() float64 { return float64(serviceConfig.ThrottledLogRPS()) }
}
namespaceRegistry namespace.Registry,
metricsHandler metrics.Handler,
namespaceRateFn := func(namespaceName string) float64 {
if namespaceRPS := serviceConfig.NamespaceRPS(namespaceName); namespaceRPS > 0 {
return float64(namespaceRPS)
}
namespaceRegistry,
configs.NewNamespaceRateLimiter(
namespaceRateFn,
serviceConfig.OperatorRPSRatio,
),
map[string]int{}, // no token overrides
configs.PollTaskAPISet, // set of APIs that will wait for token instead of immediate rejection
serviceConfig.PollWaitForNamespaceRateLimitToken,
metricsHandler,
)
}
func RateLimitInterceptorProvider(
serviceConfig *Config,
return interceptor.NewRateLimitInterceptor(
configs.NewPriorityRateLimiter(func() float64 { return float64(serviceConfig.RPS()) }, serviceConfig.OperatorRPSRatio),
map[string]int{
healthpb.Health_Check_FullMethodName: 0, // exclude health check requests from rate limiting.
persistenceLazyLoadedServiceResolver service.PersistenceLazyLoadedServiceResolver,
logger log.SnTaggedLogger,
return service.NewPersistenceRateLimitingParams(
serviceConfig.PersistenceMaxQPS,
serviceConfig.PersistenceGlobalMaxQPS,
serviceConfig.PersistenceNamespaceMaxQPS,
serviceConfig.PersistenceGlobalNamespaceMaxQPS,
serviceConfig.PersistencePerShardNamespaceMaxQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.PersistenceQPSBurstRatio,
serviceConfig.PersistenceDynamicRateLimitingParams,
persistenceLazyLoadedServiceResolver,
logger,
)
}
func ServiceResolverProvider(
membershipMonitor membership.Monitor,
return membershipMonitor.GetResolver(primitives.MatchingService)
}
// TaskQueueReplicatorNamespaceReplicationQueue is used to ensure the replicator only gets set if global namespaces are
chasmRegistry *chasm.Registry,
serializer serialization.Serializer,
return visibility.NewManager(
*persistenceConfig,
persistenceServiceResolver,
customVisibilityStoreFactory,
nil, // matching visibility never writes
saProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
serviceConfig.VisibilityPersistenceMaxReadQPS,
serviceConfig.VisibilityPersistenceMaxWriteQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.VisibilityPersistenceSlowQueryThreshold,
serviceConfig.EnableReadFromSecondaryVisibility,
serviceConfig.VisibilityEnableShadowReadMode,
dynamicconfig.GetStringPropertyFn(visibility.SecondaryVisibilityWritingModeOff), // matching visibility never writes
serviceConfig.VisibilityDisableOrderByClause,
serviceConfig.VisibilityEnableManualPagination,
serviceConfig.VisibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
}
func ContextMetadataInterceptorProvider(logger log.Logger) *interceptor.ContextMetadataInterceptor {
fx.go
return interceptor.NewContextMetadataInterceptor(true, logger)
}
lc.Append(fx.StartStopHook(svc.Start, svc.Stop))
}
func WorkersRegistryProvider(
metricsHandler metrics.Handler,
serviceConfig *Config,
return workers.NewRegistry(lc, workers.RegistryParams{
NumBuckets: serviceConfig.WorkerRegistryNumBuckets,
TTL: serviceConfig.WorkerRegistryEntryTTL,
MinEvictAge: serviceConfig.WorkerRegistryMinEvictAge,
MaxItems: serviceConfig.WorkerRegistryMaxEntries,
EvictionInterval: serviceConfig.WorkerRegistryEvictionInterval,
MetricsHandler: metricsHandler,
MetricsConfig: workers.WorkerMetricsConfig{
EnablePluginMetrics: serviceConfig.EnableWorkerPluginMetrics,
EnablePollerAutoscalingMetrics: serviceConfig.EnablePollerAutoscalingMetrics,
BreakdownMetricsByTaskQueue: serviceConfig.BreakdownMetricsByTaskQueue,
ExternalPayloadsEnabled: serviceConfig.ExternalPayloadsEnabled,
},
})
}
func simplePartitionScalerFactoryProvider(dc *dynamicconfig.Collection) PartitionScalerFactory {
fx.go
return newSimplePartitionScalerFactory(
dynamicconfig.MatchingPartitionScaler.Get(dc),
)
}
monitor membership.Monitor,
tokenProvider auth.TokenProvider,
authHeaderName := "authorization"
requireRemoteClusterAuth := false
if cfg != nil {
requireRemoteClusterAuth = cfg.Global.Authorization.RemoteClusterAuth.Require
}
config: cfg,
serviceName: sName,
logger: logger,
metricsHandler: metricsHandler,
frontendURL: frontendURL,
frontendHTTPURL: frontendHTTPURL,
frontendHTTPPort: frontendHTTPPort,
frontendTLSConfig: frontendTLSConfig,
tlsFactory: tlsProvider,
commonDialOptions: commonDialOptions,
perServiceDialOptions: perServiceDialOptions,
tokenProvider: tokenProvider,
authHeaderName: authHeaderName,
requireRemoteClusterAuth: requireRemoteClusterAuth,
monitor: monitor,
}
f.grpcListener = sync.OnceValue(f.createGRPCListener)
f.localFrontendClient = sync.OnceValues(f.createLocalFrontendHTTPClient)
return f
}
var opts []grpc.ServerOption
if d.tlsFactory != nil {
if err != nil {
return nil, err
}
return opts, nil
}
opts = append(opts, grpc.Creds(credentials.NewTLS(serverConfig)))
}
}
var opts []grpc.ServerOption
if d.EnableInternodeServerKeepalive {
rpcConfig := d.config.Services[string(d.serviceName)].RPC
kep := rpcConfig.KeepAliveServerConfig.GetKeepAliveEnforcementPolicy()
opts = append(opts, grpc.KeepaliveEnforcementPolicy(kep), grpc.KeepaliveParams(kp))
}
if err != nil {
return nil, err
}
return opts, nil
}
opts = append(opts, grpc.Creds(credentials.NewTLS(serverConfig)))
}
// GetGRPCListener returns cached dispatcher for gRPC inbound or creates one
return d.grpcListener()
}
rpcConfig := d.config.Services[string(d.serviceName)].RPC
hostAddress := net.JoinHostPort(getListenIP(&rpcConfig, d.logger).String(), convert.IntToString(rpcConfig.GRPCPort))
grpcListener, err := net.Listen("tcp", hostAddress)
if err != nil || grpcListener == nil || grpcListener.Addr() == nil {
d.logger.Fatal("Failed to start gRPC listener", tag.Error(err), tag.Service(d.serviceName), tag.Address(hostAddress))
}
d.logger.Info("Created gRPC listener", tag.Service(d.serviceName), tag.Address(hostAddress))
rpc.go
return grpcListener
}
if cfg.BindOnLocalHost && len(cfg.BindOnIP) > 0 {
logger.Fatal("ListenIP failed, bindOnLocalHost and bindOnIP are mutually exclusive")
return nil
}
}
ip := net.ParseIP(cfg.BindOnIP)
if ip != nil {
return ip
}
logger.Fatal("ListenIP failed, unable to parse bindOnIP value", tag.Address(cfg.BindOnIP))
return nil
// CreateLocalFrontendGRPCConnection creates connection for internal frontend calls
additionalDialOptions := append([]grpc.DialOption{}, d.perServiceDialOptions[primitives.InternalFrontendService]...)
return d.dial(d.frontendURL, d.frontendTLSConfig, additionalDialOptions...)
}
// createInternodeGRPCConnection creates connection for gRPC calls
}
func (d *RPCFactory) dial(hostName string, tlsClientConfig *tls.Config, dialOptions ...grpc.DialOption) *grpc.ClientConn {
rpc.go
dialOptions = append(d.commonDialOptions, dialOptions...)
connection, err := Dial(hostName, tlsClientConfig, d.logger, d.metricsHandler, dialOptions...)
if err != nil {
d.logger.Fatal("Failed to create gRPC connection", tag.Error(err))
return nil
}
}
// CreateLocalFrontendHTTPClient gets or creates a cached frontend client.
func (d *RPCFactory) CreateLocalFrontendHTTPClient() (*common.FrontendHTTPClient, error) {
rpc.go
return d.localFrontendClient()
}
// createLocalFrontendHTTPClient creates an HTTP client for communicating with the frontend.
// It uses either the provided frontendURL or membership to resolve the frontend address.
func (d *RPCFactory) createLocalFrontendHTTPClient() (*common.FrontendHTTPClient, error) {
rpc.go
// dialer and transport field values copied from http.DefaultTransport.
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
client := http.Client{}
// Default to http unless TLS is configured.
scheme := "http"
if d.frontendTLSConfig != nil {
transport.TLSClientConfig = d.frontendTLSConfig
scheme = "https"
}
if r := serviceResolverFromGRPCURL(d.frontendHTTPURL); r != nil {
resolver: r,
underlying: transport,
httpPort: d.frontendHTTPPort,
}
address = "internal" // This will be replaced by the roundTripper
// Use the URL as-is and leave the transport unmodified.
client.Transport = transport
}
Client: client,
Address: address,
Scheme: scheme,
}, nil
}
// serviceResolverFromGRPCURL returns a ServiceResolver if ustr corresponds to a
// membership url, otherwise nil.
u, err := url.Parse(ustr)
if err != nil {
return nil
}
if err != nil {
return nil
}
}
// NewExponentialRetryPolicy returns an instance of ExponentialRetryPolicy using the provided initialInterval
func NewExponentialRetryPolicy(initialInterval time.Duration) *ExponentialRetryPolicy {
retrypolicy.go
p := &ExponentialRetryPolicy{
initialInterval: initialInterval,
backoffCoefficient: defaultBackoffCoefficient,
maximumInterval: defaultMaximumInterval,
expirationInterval: defaultExpirationInterval,
maximumAttempts: defaultMaximumAttempts,
}
return p
}
// NewRetrier is used for creating a new instance of Retrier
return &retrierImpl{
policy: policy,
timeSource: timeSource,
startTime: timeSource.Now(),
currentAttempt: 1,
}
}
// WithInitialInterval sets the initial interval used by ExponentialRetryPolicy for the very first retry
// All retries are computed using the following formula:
// initialInterval * math.Pow(backoffCoefficient, currentAttempt)
func (p *ExponentialRetryPolicy) WithBackoffCoefficient(backoffCoefficient float64) *ExponentialRetryPolicy {
retrypolicy.go
p.backoffCoefficient = backoffCoefficient
return p
}
// WithMaximumInterval sets the maximum interval for each retry.
// This does *not* cause the policy to stop retrying when the interval between retries reaches the supplied duration.
// That is what WithExpirationInterval does. Instead, this prevents the interval from exceeding maximumInterval.
func (p *ExponentialRetryPolicy) WithMaximumInterval(maximumInterval time.Duration) *ExponentialRetryPolicy {
retrypolicy.go
p.maximumInterval = maximumInterval
return p
}
// WithExpirationInterval sets the absolute expiration interval for all retries
func (p *ExponentialRetryPolicy) WithExpirationInterval(expirationInterval time.Duration) *ExponentialRetryPolicy {
retrypolicy.go
p.expirationInterval = expirationInterval
return p
}
// WithMaximumAttempts sets the maximum number of retry attempts
func (p *ExponentialRetryPolicy) WithMaximumAttempts(maximumAttempts int) *ExponentialRetryPolicy {
retrypolicy.go
p.maximumAttempts = maximumAttempts
return p
}
// ComputeNextDelay returns the next delay interval. This is used by Retrier to delay calling the operation again
func (p *ExponentialRetryPolicy) ComputeNextDelay(elapsedTime time.Duration, numAttempts int, _ error) time.Duration {
retrypolicy.go
// Check to see if we ran out of maximum number of attempts
// NOTE: if maxAttempts is X, return done when numAttempts == X, otherwise there will be attempt X+1
if p.maximumAttempts != noMaximumAttempts && numAttempts >= p.maximumAttempts {
return done
}
// Stop retrying after expiration interval is elapsed
return done
}
nextInterval := float64(p.initialInterval) * math.Pow(p.backoffCoefficient, float64(numAttempts-1))
retrypolicy.go
// Disallow retries if initialInterval is negative or nextInterval overflows
if nextInterval <= 0 {
return done
}
}
remainingTime := float64(math.Max(0, float64(p.expirationInterval-elapsedTime)))
retrypolicy.go
nextInterval = math.Min(remainingTime, nextInterval)
}
// Bail out if the next interval is smaller than initial retry interval
if nextDuration < p.initialInterval {
return done
}
return time.Duration(nextInterval)
}
// add jitter to avoid global synchronization
jitterPortion := max(
// Prevent overflow
int(0.2*nextInterval), 1)
nextInterval = nextInterval*0.8 + float64(getJitterRand().Intn(jitterPortion))
return nextInterval
}
func (r *disabledRetryPolicyImpl) ComputeNextDelay(_ time.Duration, _ int, _ error) time.Duration {
// NewConditionalRetryPolicy returns a policy that delegates to whenTrue when
// predicate(err) is true, and whenFalse otherwise.
func NewConditionalRetryPolicy(predicate func(err error) bool, whenTrue, whenFalse RetryPolicy) *ConditionalRetryPolicy {
retrypolicy.go
return &ConditionalRetryPolicy{
predicate: predicate,
whenTrue: whenTrue,
whenFalse: whenFalse,
}
}
func (p *ConditionalRetryPolicy) ComputeNextDelay(elapsedTime time.Duration, numAttempts int, err error) time.Duration {
retrypolicy.go
if p.predicate(err) {
return p.whenTrue.ComputeNextDelay(elapsedTime, numAttempts, err)
}
}
// Reset will set the Retrier into initial state
r.startTime = r.timeSource.Now()
r.currentAttempt = 1
}
// NextBackOff returns the next delay interval. This is used by Retry to delay calling the operation again
nextInterval := r.policy.ComputeNextDelay(r.getElapsedTime(), r.currentAttempt, err)
// Now increment the current attempt
r.currentAttempt++
return nextInterval
}
return r.timeSource.Now().Sub(r.startTime)
}
var _ RetryPolicy = (*ErrorDependentRetryPolicy)(nil)
var _ RetryPolicy = (*ConstantDelayRetryPolicy)(nil)
func NewConstantDelayRetryPolicy(delay time.Duration) *ConstantDelayRetryPolicy {
retrypolicy.go
return &ConstantDelayRetryPolicy{
maximumAttempts: defaultMaximumAttempts,
jitterPct: defaultJitterPct,
delay: delay,
}
}
func (p *ConstantDelayRetryPolicy) WithMaximumAttempts(maximumAttempts int) *ConstantDelayRetryPolicy {
retrypolicy.go
p.maximumAttempts = maximumAttempts
return p
}
func (p *ConstantDelayRetryPolicy) WithJitter(jitterPct float64) *ConstantDelayRetryPolicy {
}
if r := jitterRand.Load(); r != nil {
}
if !jitterRand.CompareAndSwap(nil, r) {
// Two different goroutines called some top-level
// function at the same time. While the results in
}
r.lk.Lock()
defer r.lk.Unlock()
return r.s.Int63()
}
func (r *RetryLockedSource) Seed(seed int64) {
}
return &RetryLockedSource{
lk: sync.Mutex{},
s: rand.NewSource(time.Now().UnixNano()),
}
}
var ErrNexusDisabled = serviceerror.NewFailedPrecondition("nexus is disabled")
func NewEndpointRegistryConfig(dc *dynamicconfig.Collection) *EndpointRegistryConfig {
endpoint_registry.go
config := &EndpointRegistryConfig{
refreshLongPollTimeout: dynamicconfig.RefreshNexusEndpointsLongPollTimeout.Get(dc),
refreshPageSize: dynamicconfig.NexusEndpointListDefaultPageSize.Get(dc),
refreshMinWait: dynamicconfig.RefreshNexusEndpointsMinWait.Get(dc),
readThroughCacheSize: dynamicconfig.NexusReadThroughCacheSize.Get(dc),
readThroughCacheTTL: dynamicconfig.NexusReadThroughCacheTTL.Get(dc),
refreshOnRead: dynamicconfig.ForceNexusEndpointRefreshOnRead.Get(dc),
}
config.refreshRetryPolicy = backoff.NewExponentialRetryPolicy(config.refreshMinWait()).WithMaximumInterval(config.refreshLongPollTimeout())
return config
}
func NewEndpointRegistry(
logger log.Logger,
metricsHandler metrics.Handler,
return &EndpointRegistryImpl{
config: config,
endpointsByID: make(map[string]*persistencespb.NexusEndpointEntry),
endpointsByName: make(map[string]*persistencespb.NexusEndpointEntry),
matchingClient: matchingClient,
persistence: persistence,
logger: logger,
readThroughCacheByID: cache.NewWithMetrics(config.readThroughCacheSize(), &cache.Options{
TTL: config.readThroughCacheTTL(),
}, metricsHandler.WithTags(metrics.CacheTypeTag(metrics.NexusEndpointRegistryReadThroughCacheTypeTagValue))),
}
}
// StartLifecycle starts this component. It should only be invoked by an fx lifecycle hook.
// Should not be called multiple times or concurrently with StopLifecycle()
r.setEnabled(true)
}
// StopLifecycle stops this component. It should only be invoked by an fx lifecycle hook.
// Should not be called multiple times or concurrently with StartLifecycle()
r.setEnabled(false)
}
oldReady := r.dataReady.Load()
if oldReady == nil && enabled {
backgroundCtx := headers.SetCallerInfo(
context.Background(),
headers.SystemBackgroundHighCallerInfo,
)
newReady := &dataReady{
refresh: goro.NewHandle(backgroundCtx),
ready: make(chan struct{}),
}
if r.dataReady.CompareAndSwap(oldReady, newReady) {
newReady.refresh.Go(func(ctx context.Context) error {
return r.refreshEndpointsLoop(ctx, newReady)
})
}
if r.dataReady.CompareAndSwap(oldReady, nil) {
oldReady.refresh.Cancel()
<-oldReady.refresh.Done()
// If oldReady.ready was not already closed here, callers blocked in waitUntilInitialized
// will block indefinitely (until context timeout). If we wanted to wake them up, we
// could close ready here, but we would need to use a sync.Once to avoid closing it
// twice. Then waitUntilInitialized would need to reload r.dataReady to check that the
// wakeup was due to data being ready rather than this close.
}
}
}
}
func (r *EndpointRegistryImpl) refreshEndpointsLoop(ctx context.Context, dataReady *dataReady) error {
endpoint_registry.go
hasLoadedEndpointData := false
for ctx.Err() == nil {
start := time.Now()
enforceMinWait := true
if !hasLoadedEndpointData {
// Loading endpoints for the first time after being (re)enabled, so load with fallback to persistence
// and unblock any threads waiting on r.dataReady if successful.
err := backoff.ThrottleRetryContext(ctx, r.loadEndpoints, r.config.refreshRetryPolicy, nil)
if err == nil {
hasLoadedEndpointData = true
enforceMinWait = false
r.dataLock.Unlock()
}
minWaitTime := r.config.refreshMinWait()
// In general, we want to start a new call immediately on completion of the previous one. But if the remote is
// broken and returns success immediately, we might end up spinning. So enforce a minimum wait time that
// increases as long as we keep getting very fast replies. Only enforce the min wait if the remote does not
// return new data.
if enforceMinWait && elapsed < minWaitTime {
}
}
}
// loadEndpoints initializes the in-memory view of endpoints data.
// It first tries to load from matching service and falls back to querying persistence directly if matching is unavailable.
func (r *EndpointRegistryImpl) loadEndpoints(ctx context.Context) error {
endpoint_registry.go
tableVersion, endpoints, err := r.getAllEndpointsMatchingWithPersistenceFallback(ctx)
if err != nil {
}
endpointsByID := make(map[string]*persistencespb.NexusEndpointEntry, len(endpoints))
endpointsByName := make(map[string]*persistencespb.NexusEndpointEntry, len(endpoints))
}
func (r *EndpointRegistryImpl) getAllEndpointsMatchingWithPersistenceFallback(ctx context.Context) (int64, []*persistencespb.NexusEndpointEntry, error) {
endpoint_registry.go
tableVersion, endpoints, err := r.getAllEndpointsMatching(ctx)
if err != nil {
r.logger.Error("error from matching when initializing Nexus endpoint cache", tag.Error(err))
tableVersion, endpoints, err = r.getAllEndpointsPersistence(ctx)
}
}
// getAllEndpointsMatching paginates over all endpoints returned by matching. It always does a simple get.
func (r *EndpointRegistryImpl) getAllEndpointsMatching(ctx context.Context) (int64, []*persistencespb.NexusEndpointEntry, error) {
endpoint_registry.go
return r.getAllEndpoints(ctx, func(currentTableVersion int64, currentPageToken []byte) (int64, []byte, []*persistencespb.NexusEndpointEntry, error) {
resp, err := r.matchingClient.ListNexusEndpoints(ctx, &matchingservice.ListNexusEndpointsRequest{
NextPageToken: currentPageToken,
PageSize: int32(r.config.refreshPageSize()),
LastKnownTableVersion: currentTableVersion,
Wait: false,
})
if err != nil {
}
return resp.TableVersion, resp.NextPageToken, resp.Entries, nil
})
// getAllEndpointsPersistence paginates over all endpoints returned by persistence.
// Should only be used as a fall-back if matching service is unavailable during initial load.
func (r *EndpointRegistryImpl) getAllEndpointsPersistence(ctx context.Context) (int64, []*persistencespb.NexusEndpointEntry, error) {
endpoint_registry.go
return r.getAllEndpoints(ctx, func(currentTableVersion int64, currentPageToken []byte) (int64, []byte, []*persistencespb.NexusEndpointEntry, error) {
resp, err := r.persistence.ListNexusEndpoints(ctx, &p.ListNexusEndpointsRequest{
LastKnownTableVersion: currentTableVersion,
// getAllEndpointsPersistence paginates over all endpoints returned by persistence.
// Should only be used as a fall-back if matching service is unavailable during initial load.
func (r *EndpointRegistryImpl) getAllEndpoints(ctx context.Context, getter func(int64, []byte) (int64, []byte, []*persistencespb.NexusEndpointEntry, error)) (int64, []*persistencespb.NexusEndpointEntry, error) {
endpoint_registry.go
var currentPageToken []byte
currentTableVersion := int64(0)
entries := make([]*persistencespb.NexusEndpointEntry, 0)
for ctx.Err() == nil {
respTableVersion, respNextPageToken, respEntries, err := getter(currentTableVersion, currentPageToken)
if err != nil {
if errors.As(err, &fpe) && fpe.Message == p.ErrNexusTableVersionConflict.Error() {
// indicates table was updated during paging, so reset and start from the beginning.
currentPageToken = nil
executableTaskConverter ExecutableTaskConverter,
enableStreaming bool,
return &StreamReceiverMonitorImpl{
ProcessToolBox: processToolBox,
executableTaskConverter: executableTaskConverter,
enableStreaming: enableStreaming,
status: streamStatusInitialized,
shutdownOnce: channel.NewShutdownOnce(),
inboundStreams: make(map[ClusterShardKeyPair]StreamSender),
outboundStreams: make(map[ClusterShardKeyPair]StreamReceiver),
}
}
if !atomic.CompareAndSwapInt32(
&m.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
return
}
go m.statusMonitorLoop()
m.Logger.Info("StreamReceiverMonitor started.")
}
if !atomic.CompareAndSwapInt32(
&m.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
}
return
}
m.Lock()
defer m.Unlock()
for serverKey, stream := range m.outboundStreams {
stream.Stop()
delete(m.outboundStreams, serverKey)
}
stream.Stop()
delete(m.inboundStreams, clientKey)
}
}
}
}
defer m.Stop()
ticker := time.NewTicker(streamReceiverMonitorInterval)
defer ticker.Stop()
clusterMetadataChangeChan := make(chan struct{}, 1)
m.ClusterMetadata.RegisterMetadataChangeCallback(m, func(_ map[string]*cluster.ClusterInformation, _ map[string]*cluster.ClusterInformation) {
select {
case clusterMetadataChangeChan <- struct{}{}:
default:
}
})
m.reconcileOutboundStreams()
Loop:
for !m.shutdownOnce.IsShutdown() {
select {
case <-clusterMetadataChangeChan:
m.reconcileInboundStreams()
m.reconcileOutboundStreams()
case <-ticker.C:
m.reconcileInboundStreams()
m.reconcileOutboundStreams()
break Loop
}
}
}
streamKeys := m.generateInboundStreamKeys()
m.doReconcileInboundStreams(streamKeys)
}
streamKeys := m.generateOutboundStreamKeys()
m.doReconcileOutboundStreams(streamKeys)
}
func (m *StreamReceiverMonitorImpl) generateInboundStreamKeys() map[ClusterShardKeyPair]struct{} {
stream_receiver_monitor.go
allClusterInfo := m.ClusterMetadata.GetAllClusterInfo()
clientClusterIDs := make(map[int32]struct{})
serverClusterID := int32(m.ClusterMetadata.GetClusterID())
clusterIDToShardCount := make(map[int32]int32)
for _, clusterInfo := range allClusterInfo {
clusterIDToShardCount[int32(clusterInfo.InitialFailoverVersion)] = clusterInfo.ShardCount
if !cluster.IsReplicationEnabledForCluster(clusterInfo, m.Config.EnableSeparateReplicationEnableFlag()) || int32(clusterInfo.InitialFailoverVersion) == serverClusterID {
continue
}
clientClusterIDs[int32(clusterInfo.InitialFailoverVersion)] = struct{}{}
}
for _, shardID := range m.ShardController.ShardIDs() {
for clientClusterID := range clientClusterIDs {
serverShardID := shardID
}
}
}
func (m *StreamReceiverMonitorImpl) generateOutboundStreamKeys() map[ClusterShardKeyPair]struct{} {
stream_receiver_monitor.go
allClusterInfo := m.ClusterMetadata.GetAllClusterInfo()
clientClusterID := int32(m.ClusterMetadata.GetClusterID())
serverClusterIDs := make(map[int32]struct{})
clusterIDToShardCount := make(map[int32]int32)
for _, clusterInfo := range allClusterInfo {
clusterIDToShardCount[int32(clusterInfo.InitialFailoverVersion)] = clusterInfo.ShardCount
if !clusterInfo.Enabled || !cluster.IsReplicationEnabledForCluster(clusterInfo, m.Config.EnableSeparateReplicationEnableFlag()) || int32(clusterInfo.InitialFailoverVersion) == clientClusterID {
continue
}
serverClusterIDs[int32(clusterInfo.InitialFailoverVersion)] = struct{}{}
}
for _, shardID := range m.ShardController.ShardIDs() {
for serverClusterID := range serverClusterIDs {
clientShardID := shardID
}
}
}
func (m *StreamReceiverMonitorImpl) doReconcileInboundStreams(
streamKeys map[ClusterShardKeyPair]struct{},
m.Lock()
defer m.Unlock()
if m.shutdownOnce.IsShutdown() {
return
}
if !stream.IsValid() {
stream.Stop()
func (m *StreamReceiverMonitorImpl) doReconcileOutboundStreams(
streamKeys map[ClusterShardKeyPair]struct{},
m.Lock()
defer m.Unlock()
if m.shutdownOnce.IsShutdown() {
return
}
if !stream.IsValid() {
stream.Stop()
}
}
if _, ok := m.outboundStreams[streamKey]; !ok {
stream := NewStreamReceiver(
}
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
m.monitorStreamStatus()
return
}
}
// NewCollection creates a new collection. For subscriptions to work, you must call Start/Stop.
// Get will work without Start/Stop.
// Do this at the first convenient place we have a logger:
logSharedStructureWarnings(logger)
return &Collection{
client: client,
logger: logger,
errCount: -1,
subscriptions: make(map[Key]map[int]any),
convertCache: new(sync.Map),
indexCache: new(sync.Map),
}
}
c.subscriptionLock.Lock()
defer c.subscriptionLock.Unlock()
if notifyingClient, ok := c.client.(NotifyingClient); ok {
c.cancelClientSubscription = notifyingClient.Subscribe(c.keysChanged)
}
}
c.poller.Cancel()
c.poller.Wait()
if c.cancelClientSubscription != nil {
c.cancelClientSubscription()
}
// Implement pingable.Pingable
return []pingable.Check{
{
Name: "dynamic config callbacks",
Timeout: 5 * time.Second,
Ping: func() []pingable.Pingable {
c.subscriptionLock.Lock()
//nolint:staticcheck // SA2001 just checking if we can acquire the lock
c.subscriptionLock.Unlock()
return nil
},
},
}
}
interval := DynamicConfigSubscriptionPollInterval.Get(c)
for ctx.Err() == nil {
util.InterruptibleSleep(ctx, interval())
c.pollOnce()
}
}
c.subscriptionLock.Lock()
defer c.subscriptionLock.Unlock()
for key, subs := range c.subscriptions {
setting := queryRegistry(key)
if setting == nil {
continue
}
cvs := c.client.GetValue(key)
setting.dispatchUpdate(c, sub, cvs)
cvs []ConstrainedValue,
precedence []Constraints,
if len(cvs) == 0 {
return findMatchWithCache(cache, cvs, precedence)
}
convert func(value any) (T, error),
precedence []Constraints,
cvs := c.client.GetValue(key)
v, _ := matchAndConvertCvs(c, key, def, convert, precedence, cvs)
return v
}
func matchAndConvertCvs[T any](
precedence []Constraints,
cvs []ConstrainedValue,
cvp, err := findMatch(c.indexCache, cvs, precedence)
if err != nil {
return def, usingDefaultValue
}
typedVal, err := convertWithCache(c, key, convert, cvp)
prec []Constraints,
callback func(T),
c.subscriptionLock.Lock()
defer c.subscriptionLock.Unlock()
// get one value immediately (note that subscriptionLock is held here so we can't race with
// an update)
cvs := c.client.GetValue(key)
init, raw := matchAndConvertCvs(c, key, def, convert, prec, cvs)
// As a convenience (and for efficiency), you can pass in a nil callback; we just return the
// current value and skip the subscription. The cancellation func returned is also nil.
if callback == nil {
return init, nil
}
id := c.subscriptionIdx
if c.subscriptions[key] == nil {
c.subscriptions[key] = make(map[int]any)
}
prec: prec,
f: callback,
def: def,
raw: raw,
}
return init, func() {
defer c.subscriptionLock.Unlock()
delete(c.subscriptions[key], id)
}
}
// treat the fields independently), or the zero value of its type (if you want to treat the fields
// as a group and default unset fields to zero).
return func(v any) (T, error) {
// if we already have the right type, no conversion is necessary
if typedV, ok := v.(T); ok {
return typedV, nil
}
// Deep-copy the default and decode over it. This allows using e.g. a struct with some
// default fields filled in and a config that only set some fields.
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &out,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructureHookDuration,
mapstructureHookTimestamp,
mapstructureHookProtoEnum,
mapstructureHookGeneric,
),
})
if err != nil {
return out, err
}
return out, err
}
}
ctx context.Context,
request *p.InternalListClusterMetadataRequest,
var clusterName string
if request.NextPageToken != nil {
err := gobDeserialize(request.NextPageToken, &clusterName)
if err != nil {
}
rows, err := s.DB.ListClusterMetadata(ctx, &sqlplugin.ClusterMetadataFilter{ClusterName: clusterName, PageSize: &request.PageSize})
cluster_metadata.go
if err != nil {
if err == sql.ErrNoRows {
return &p.InternalListClusterMetadataResponse{}, nil
}
for _, row := range rows {
resp := &p.InternalGetClusterMetadataResponse{
ClusterMetadata: p.NewDataBlob(row.Data, row.DataEncoding),
Version: row.Version,
}
clusterMetadata = append(clusterMetadata, resp)
}
resp := &p.InternalListClusterMetadataResponse{ClusterMetadata: clusterMetadata}
cluster_metadata.go
if len(rows) >= request.PageSize {
nextPageToken, err := gobSerialize(rows[len(rows)-1].ClusterName)
if err != nil {
ctx context.Context,
request *p.InternalGetClusterMetadataRequest,
row, err := s.DB.GetClusterMetadata(ctx, &sqlplugin.ClusterMetadataFilter{ClusterName: request.ClusterName})
if err != nil {
return nil, convertCommonErrors("GetClusterMetadata", err)
}
ClusterMetadata: p.NewDataBlob(row.Data, row.DataEncoding),
Version: row.Version,
}, nil
}
ctx context.Context,
request *p.InternalSaveClusterMetadataRequest,
err := s.txExecute(ctx, "SaveClusterMetadata", func(tx sqlplugin.Tx) error {
oldClusterMetadata, err := tx.WriteLockGetClusterMetadata(
ctx,
&sqlplugin.ClusterMetadataFilter{ClusterName: request.ClusterName})
var lastVersion int64
if err != nil {
if err != sql.ErrNoRows {
return serviceerror.NewUnavailablef("SaveClusterMetadata operation failed. Error %v", err)
}
lastVersion = oldClusterMetadata.Version
}
return serviceerror.NewUnavailablef("SaveClusterMetadata encountered version mismatch, expected %v but got %v.",
request.Version, oldClusterMetadata.Version)
}
ClusterName: request.ClusterName,
Data: request.ClusterMetadata.Data,
DataEncoding: request.ClusterMetadata.EncodingType.String(),
Version: request.Version,
})
if err != nil {
return convertCommonErrors("SaveClusterMetadata", err)
}
})
return false, serviceerror.NewUnavailable(err.Error())
}
}
ctx context.Context,
request *p.GetClusterMembersRequest,
var lastSeenHostId []byte
if len(request.NextPageToken) == 16 {
lastSeenHostId = request.NextPageToken
return nil, serviceerror.NewInternal("page token is corrupted.")
}
filter := &sqlplugin.ClusterMembershipFilter{
HostIDEquals: request.HostIDEquals,
RoleEquals: request.RoleEquals,
RecordExpiryAfter: now,
SessionStartedAfter: request.SessionStartedAfter,
MaxRecordCount: request.PageSize,
}
if lastSeenHostId != nil && filter.HostIDEquals == nil {
filter.HostIDGreaterThan = lastSeenHostId
}
filter.LastHeartbeatAfter = now.Add(-request.LastHeartbeatWithin)
}
filter.RPCAddressEquals = request.RPCAddressEquals.String()
}
if err != nil {
return nil, convertCommonErrors("GetClusterMembers", err)
}
for _, row := range rows {
HostID: row.HostID,
Role: row.Role,
RPCAddress: net.ParseIP(row.RPCAddress),
RPCPort: row.RPCPort,
SessionStart: row.SessionStart,
LastHeartbeat: row.LastHeartbeat,
RecordExpiry: row.RecordExpiry,
})
}
if request.PageSize > 0 && len(rows) == request.PageSize {
lastRow := rows[len(rows)-1]
nextPageToken = lastRow.HostID
}
return &p.GetClusterMembersResponse{ActiveMembers: convertedRows, NextPageToken: nextPageToken}, nil
cluster_metadata.go
}
ctx context.Context,
request *p.UpsertClusterMembershipRequest,
now := time.Now().UTC()
recordExpiry := now.Add(request.RecordExpiry)
_, err := s.DB.UpsertClusterMembership(ctx, &sqlplugin.ClusterMembershipRow{
Role: request.Role,
HostID: request.HostID,
RPCAddress: request.RPCAddress.String(),
RPCPort: request.RPCPort,
SessionStart: request.SessionStart,
LastHeartbeat: now,
RecordExpiry: recordExpiry})
if err != nil {
return convertCommonErrors("UpsertClusterMembership", err)
}
}
ctx context.Context,
request *p.PruneClusterMembershipRequest,
_, err := s.DB.PruneClusterMembership(
ctx,
&sqlplugin.PruneClusterMembershipFilter{
PruneRecordsBefore: time.Now().UTC(),
},
)
if err != nil {
return convertCommonErrors("PruneClusterMembership", err)
}
}
logger log.Logger,
serializer serialization.Serializer,
return &sqlClusterMetadataManager{
SqlStore: NewSQLStore(db, logger, serializer),
}, nil
}
// Returns true if the Wait() call succeeded before the timeout
// Returns false if the Wait() did not return before the timeout
return BlockWithTimeout(wg.Wait, timeout)
}
// BlockWithTimeout invokes fn and waits for it to complete until the timeout.
// Returns true if the call completed before the timeout, otherwise returns false.
// fn is expected to be a blocking call and will continue to occupy a goroutine until it finally completes.
doneC := make(chan struct{})
go func() {
fn()
close(doneC)
}()
defer timer.Stop()
select {
case <-doneC:
return true
case <-timer.C:
return false
// CreatePersistenceClientRetryPolicy creates a retry policy for calls to persistence
return backoff.NewExponentialRetryPolicy(persistenceClientRetryInitialInterval).
WithMaximumAttempts(persistenceClientRetryMaxAttempts)
}
// CreateFrontendClientRetryPolicy creates a retry policy for calls to frontend service
return backoff.NewExponentialRetryPolicy(frontendClientRetryInitialInterval).
WithMaximumAttempts(frontendClientRetryMaxAttempts)
}
// CreateHistoryClientRetryPolicy creates a retry policy for calls to history service.
// default 1-minute expiration interval and the caller's context. Other errors (and all
// errors when the flag is off) follow the standard cap.
func CreateHistoryClientRetryPolicy(retryUnboundedOnSystemResourceExhausted func() bool) backoff.RetryPolicy {
util.go
return newClientRetryPolicy(historyClientRetryInitialInterval, historyClientRetryMaxAttempts, retryUnboundedOnSystemResourceExhausted)
}
// CreateMatchingClientRetryPolicy creates a retry policy for calls to matching service.
// default 1-minute expiration interval and the caller's context. Other errors (and all
// errors when the flag is off) follow the standard cap.
func CreateMatchingClientRetryPolicy(retryUnboundedOnSystemResourceExhausted func() bool) backoff.RetryPolicy {
util.go
return newClientRetryPolicy(matchingClientRetryInitialInterval, matchingClientRetryMaxAttempts, retryUnboundedOnSystemResourceExhausted)
}
func newClientRetryPolicy(initialInterval time.Duration, maxAttempts int, retryUnboundedOnSystemResourceExhausted func() bool) backoff.RetryPolicy {
util.go
capped := backoff.NewExponentialRetryPolicy(initialInterval).
WithMaximumAttempts(maxAttempts)
// No max-attempts cap; bounded by the default 1-minute expiration interval
// and the caller's context.
extended := backoff.NewExponentialRetryPolicy(initialInterval)
predicate := func(err error) bool {
return retryUnboundedOnSystemResourceExhausted() && isSystemResourceExhausted(err)
}
return backoff.NewConditionalRetryPolicy(predicate, extended, capped)
}
// CreateMatchingClientLongPollRetryPolicy creates a retry policy for poll calls to matching service
// no maximum attempts, using default expiration interval of 1 minute
return backoff.NewExponentialRetryPolicy(matchingClientRetryInitialInterval)
}
// CreateFrontendHandlerRetryPolicy creates a retry policy for calls to frontend service
return backoff.NewExponentialRetryPolicy(frontendHandlerRetryInitialInterval).
WithMaximumInterval(frontendHandlerRetryMaxInterval).
WithMaximumAttempts(frontendHandlerRetryMaxAttempts)
}
// CreateHistoryHandlerRetryPolicy creates a retry policy for calls to history service
return backoff.NewExponentialRetryPolicy(historyHandlerRetryInitialInterval).
WithMaximumAttempts(historyHandlerRetryMaxAttempts)
}
// CreateMatchingHandlerRetryPolicy creates a retry policy for calls to matching service
return backoff.NewExponentialRetryPolicy(matchingHandlerRetryInitialInterval).
WithMaximumAttempts(matchingHandlerRetryMaxAttempts)
}
// CreateReadTaskRetryPolicy creates a retry policy for loading background tasks
return backoff.NewExponentialRetryPolicy(readTaskRetryInitialInterval).
WithMaximumInterval(readTaskRetryMaxInterval).
WithExpirationInterval(readTaskRetryExpirationInterval)
}
// CreateCompleteTaskRetryPolicy creates a retry policy for completing background tasks
// CreateTaskReschedulePolicy creates a retry policy for rescheduling task with errors not equal to ErrTaskRetry
return backoff.NewExponentialRetryPolicy(taskRescheduleInitialInterval).
WithBackoffCoefficient(taskRescheduleBackoffCoefficient).
WithMaximumInterval(taskRescheduleMaxInterval).
WithExpirationInterval(backoff.NoInterval)
}
// CreateDependencyTaskNotCompletedReschedulePolicy creates a retry policy for rescheduling task with
// ErrDependencyTaskNotCompleted
return backoff.NewExponentialRetryPolicy(dependencyTaskNotCompletedRescheduleInitialInterval).
WithBackoffCoefficient(dependencyTaskNotCompletedRescheduleBackoffCoefficient).
WithMaximumInterval(dependencyTaskNotCompletedRescheduleMaxInterval).
WithExpirationInterval(backoff.NoInterval)
}
// CreateTaskNotReadyReschedulePolicy creates a retry policy for rescheduling task with ErrTaskRetry
return backoff.NewExponentialRetryPolicy(taskNotReadyRescheduleInitialInterval).
WithBackoffCoefficient(taskNotReadyRescheduleBackoffCoefficient).
WithMaximumInterval(taskNotReadyRescheduleMaxInterval).
WithExpirationInterval(backoff.NoInterval)
}
// CreateTaskResourceExhaustedReschedulePolicy creates a retry policy for rescheduling task with resource exhausted error
return backoff.NewExponentialRetryPolicy(taskResourceExhaustedRescheduleInitialInterval).
WithBackoffCoefficient(taskResourceExhaustedRescheduleBackoffCoefficient).
WithMaximumInterval(taskResourceExhaustedRescheduleMaxInterval).
WithExpirationInterval(backoff.NoInterval)
}
// CreateSdkClientFactoryRetryPolicy creates a retry policy to handle SdkClientFactory NewClient when frontend service is not ready
return backoff.NewExponentialRetryPolicy(sdkClientFactoryRetryInitialInterval).
WithMaximumInterval(sdkClientFactoryRetryMaxInterval).
WithExpirationInterval(sdkClientFactoryRetryExpirationInterval)
}
// IsPersistenceTransientError checks if the error is a transient persistence error
// IsContextCanceledErr checks if the error is context.Canceled or serviceerror.Canceled error
var canceledSvcErr *serviceerror.Canceled
return errors.Is(err, context.Canceled) ||
errors.As(err, &canceledSvcErr)
}
// IsServiceClientTransientError checks if the error is a transient error.
if IsServiceHandlerRetryableError(err) {
}
if isSystemResourceExhausted(err) {
}
if IsNamespaceHandoverError(err) {
return false
}
case *serviceerror.Internal,
return true
case *serviceerror.MultiOperationExecution:
for _, opErr := range err.OperationErrors() {
}
return err.Error() == ErrNamespaceHandover.Error()
}
func IsStickyWorkerUnavailable(err error) bool {
}
return &Registry{
libraries: make(map[string]Library),
rcByFqn: make(map[string]*RegistrableComponent),
rcByID: make(map[uint32]*RegistrableComponent),
rcByGoType: make(map[reflect.Type]*RegistrableComponent),
rtByFqn: make(map[string]*RegistrableTask),
rtByID: make(map[uint32]*RegistrableTask),
rtByGoType: make(map[reflect.Type]*RegistrableTask),
rcContextValues: make(map[any]valueWithFqn),
nexusServices: make(map[string]*nexus.Service),
NexusEndpointProcessor: NewNexusEndpointProcessor(),
logger: logger,
}
}
if err := r.validateName(lib.Name()); err != nil {
return err
}
return fmt.Errorf("library %s is already registered", lib.Name())
}
for _, c := range lib.Components() {
return err
}
}
return err
}
}
return err
}
}
return err
}
}
}
// RegisterServices registers all gRPC services from all registered libraries.
for _, lib := range r.libraries {
lib.RegisterServices(server)
}
}
lib namer,
rc *RegistrableComponent,
if err := r.validate(rc); err != nil {
return err
}
if err != nil {
return err
}
return fmt.Errorf("component %s is already registered", fqn)
}
return fmt.Errorf("component %s maps to a reserved archetype id %d, please use a different name", fqn, UnspecifiedArchetypeID)
}
return fmt.Errorf("component ID %d collision between %s and %s", id, fqn, existingComponent.fqType())
}
return fmt.Errorf("context value key %v registered by component %s conflicts with component %s", key, fqn, existingValue.fqn)
}
v: value,
fqn: fqn,
}
}
// rc.goType implements Component interface; therefore, it must be a struct.
// This check to protect against the interface itself being registered.
(rc.goType.Kind() == reflect.Pointer && rc.goType.Elem().Kind() == reflect.Struct)) {
return fmt.Errorf("component type %s must be struct or pointer to struct", rc.goType.String())
}
return fmt.Errorf("component type %s is already registered", rc.goType.String())
}
r.rcByFqn[fqn] = rc
r.rcByID[id] = rc
r.rcByGoType[rc.goType] = rc
return nil
}
if err := r.validateName(rc.componentType); err != nil {
return err
}
}
lib namer,
rt *RegistrableTask,
if err := r.validateName(rt.taskType); err != nil {
return err
}
if err != nil {
return err
}
return fmt.Errorf("task %s is already registered", fqn)
}
return fmt.Errorf("task type ID %d collision between %s and %s", id, fqn, existingTask.fqType())
}
(rt.goType.Kind() == reflect.Pointer && rt.goType.Elem().Kind() == reflect.Struct)) {
return fmt.Errorf("task type %s must be struct or pointer to struct", rt.goType.String())
}
return fmt.Errorf("task type %s is already registered", rt.goType.String())
}
(rt.componentGoType.Kind() == reflect.Struct ||
(rt.componentGoType.Kind() == reflect.Pointer && rt.componentGoType.Elem().Kind() == reflect.Struct)) &&
rt.componentGoType.AssignableTo(reflect.TypeFor[Component]())) {
return fmt.Errorf("component type %s must be and interface or struct that implements Component interface", rt.componentGoType.String())
}
r.rtByID[id] = rt
r.rtByGoType[rt.goType] = rt
return nil
}
if n == "" {
return errors.New("name must not be empty")
}
return fmt.Errorf("name %s is invalid. name must follow golang identifier rules: %s", n, nameValidator.String())
}
}
func (r *Registry) validateVisibilityBusinessIDAlias(rc *RegistrableComponent) error {
registry.go
if !hasVisibilityField(rc.goType) {
}
// Archetypes that contain a Field[*Visibility] must specify WithBusinessIDAlias.
return fmt.Errorf("component %s has Field[*Visibility] but no businessID alias; use WithBusinessIDAlias option", rc.componentType)
}
}
var unmanagedFields []string
for f := range unmanagedFieldsOf(rc.goType) {
}
"Warning: CHASM component %s declares state fields that won't be managed by CHASM:\n\t%s",
fqn,
strings.Join(unmanagedFields, "\n\t")))
}
}
if _, ok := r.nexusServices[svc.Name]; ok {
return fmt.Errorf("nexus service %s is already registered", svc.Name)
}
return nil
}
// NexusServices returns all registered Nexus services.
// Return a copy to prevent external modification
services := make(map[string]*nexus.Service, len(r.nexusServices))
maps.Copy(services, r.nexusServices)
return services
}
func (r *Registry) componentContextValue(key any) any {
workerDeploymentReadRateLimiter quotas.RequestRateLimiter,
validator *workflow.RequestValidator,
handler := &WorkflowHandler{
ActivityHandler: activityHandler,
NexusOperationHandler: nexusOperationHandler,
status: common.DaemonStatusInitialized,
callbackValidator: callbackValidator,
config: config,
tokenSerializer: tasktoken.NewSerializer(),
versionChecker: headers.NewDefaultVersionChecker(),
namespaceHandler: newNamespaceHandler(
logger,
persistenceMetadataManager,
namespaceRegistry,
clusterMetadata,
nsreplication.NewReplicator(namespaceReplicationQueue, logger),
archivalMetadata,
archiverProvider,
timeSource,
config,
),
getDefaultWorkflowRetrySettings: config.DefaultWorkflowRetryPolicy,
visibilityMgr: visibilityMgr,
logger: logger,
throttledLogger: throttledLogger,
persistenceExecutionName: persistenceExecutionName,
clusterMetadataManager: clusterMetadataManager,
clusterMetadata: clusterMetadata,
historyClient: historyClient,
matchingClient: matchingClient,
workerDeploymentClient: workerDeploymentClient,
schedulerClient: schedulerClient,
archiverProvider: archiverProvider,
payloadSerializer: payloadSerializer,
namespaceRegistry: namespaceRegistry,
saProvider: saProvider,
saMapperProvider: saMapperProvider,
saValidator: saValidator,
archivalMetadata: archivalMetadata,
healthServer: healthServer,
overrides: NewOverrides(),
membershipMonitor: membershipMonitor,
healthInterceptor: healthInterceptor,
scheduleSpecBuilder: scheduleSpecBuilder,
outstandingPollers: collection.NewSyncMap[string, collection.SyncMap[string, context.CancelFunc]](),
httpEnabled: httpEnabled,
registry: registry,
workerDeploymentReadRateLimiter: workerDeploymentReadRateLimiter,
validator: validator,
}
return handler
}
// Start starts the handler
if atomic.CompareAndSwapInt32(
&wh.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
// Start in NOT_SERVING state and switch to SERVING after membership is ready
wh.healthServer.SetServingStatus(WorkflowServiceName, healthpb.HealthCheckResponse_NOT_SERVING)
go func() {
_ = wh.membershipMonitor.WaitUntilInitialized(context.Background())
wh.healthServer.SetServingStatus(WorkflowServiceName, healthpb.HealthCheckResponse_SERVING)
wh.healthInterceptor.SetHealthy(true)
wh.logger.Info("Frontend is now healthy")
}()
wh.namespaceRegistry.RegisterStateChangeCallback(wh, func(ns *namespace.Namespace, deletedFromDb bool) {
workflow_handler.go
if deletedFromDb {
return
}
ns.ReplicationPolicy() == namespace.ReplicationPolicyMultiCluster &&
//nolint:forbidigo // namespace state-change callback; cancels all pollers on ns deactivation
!ns.ActiveInCluster(wh.clusterMetadata.GetCurrentClusterName()) {
pollers, ok := wh.outstandingPollers.Get(ns.ID().String())
if ok {
// Stop stops the handler
if atomic.CompareAndSwapInt32(
&wh.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
wh.namespaceRegistry.UnregisterStateChangeCallback(wh)
wh.healthServer.SetServingStatus(WorkflowServiceName, healthpb.HealthCheckResponse_NOT_SERVING)
wh.healthInterceptor.SetHealthy(false)
}
}
// GetSystemInfo returns information about the Temporal system.
func (wh *WorkflowHandler) GetSystemInfo(ctx context.Context, request *workflowservice.GetSystemInfoRequest) (_ *workflowservice.GetSystemInfoResponse, retError error) {
workflow_handler.go
defer log.CapturePanic(wh.logger, &retError)
if request == nil {
return nil, errRequestNotSet
}
ServerVersion: headers.ServerVersion,
// Capabilities should be added as needed. In many cases, capabilities are
// hardcoded boolean true values since older servers will respond with a
// form of this message without the field which is implied false.
Capabilities: &workflowservice.GetSystemInfoResponse_Capabilities{
SignalAndQueryHeader: true,
InternalErrorDifferentiation: true,
ActivityFailureIncludeHeartbeat: true,
SupportsSchedules: true,
EncodedFailureAttributes: true,
UpsertMemo: true,
EagerWorkflowStart: true,
SdkMetadata: true,
BuildIdBasedVersioning: true,
CountGroupByExecutionStatus: true,
Nexus: wh.httpEnabled,
ServerScaledDeployments: true,
},
}, nil
}
}
return SearchAttributeFieldBool{
field: resolveFieldName(enumspb.INDEXED_VALUE_TYPE_BOOL, index),
}
}
// SearchAttributeFieldDateTime is a search attribute field for a datetime value.
}
func newSearchAttributeFieldDateTime(index int) SearchAttributeFieldDateTime {
search_attribute.go
return SearchAttributeFieldDateTime{
field: resolveFieldName(enumspb.INDEXED_VALUE_TYPE_DATETIME, index),
}
}
// SearchAttributeFieldInt is a search attribute field for an integer value.
}
return SearchAttributeFieldInt{
field: resolveFieldName(enumspb.INDEXED_VALUE_TYPE_INT, index),
}
}
// SearchAttributeFieldDouble is a search attribute field for a double value.
}
func newSearchAttributeFieldDouble(index int) SearchAttributeFieldDouble {
search_attribute.go
return SearchAttributeFieldDouble{
field: resolveFieldName(enumspb.INDEXED_VALUE_TYPE_DOUBLE, index),
}
}
// SearchAttributeFieldKeyword is a search attribute field for a keyword value.
}
func newSearchAttributeFieldKeyword(index int) SearchAttributeFieldKeyword {
search_attribute.go
return SearchAttributeFieldKeyword{
field: resolveFieldName(enumspb.INDEXED_VALUE_TYPE_KEYWORD, index),
}
}
func newSearchAttributeFieldLowCardinalityKeyword(index int) SearchAttributeFieldKeyword {
search_attribute.go
return SearchAttributeFieldKeyword{
field: fmt.Sprintf("%s%s%02d", sadefs.ReservedPrefix, "LowCardinalityKeyword", index),
}
}
// SearchAttributeFieldKeywordList is a search attribute field for a keyword list value.
}
func newSearchAttributeFieldKeywordList(index int) SearchAttributeFieldKeywordList {
search_attribute.go
return SearchAttributeFieldKeywordList{
field: resolveFieldName(enumspb.INDEXED_VALUE_TYPE_KEYWORD_LIST, index),
}
}
// SearchAttributeFieldText is a search attribute field for a text value.
}
func resolveFieldName(valueType enumspb.IndexedValueType, index int) string {
search_attribute.go
// Columns are named like TemporalBool01, TemporalDatetime01, TemporalDouble01, TemporalInt01.
return fmt.Sprintf("%s%s%02d", sadefs.ReservedPrefix, valueType.String(), index)
}
func (s searchAttributeDefinition) definition() searchAttributeDefinition {
search_attribute.go
return s
}
// SearchAttributeBool is a search attribute for a boolean value.
}
return SearchAttributeBool{
searchAttributeDefinition: searchAttributeDefinition{
alias: field,
field: field,
valueType: enumspb.INDEXED_VALUE_TYPE_BOOL,
},
}
}
// Value sets the boolean value of the search attribute.
// NewSearchAttributeDateTime creates a new date time search attribute given a predefined chasm field
func NewSearchAttributeDateTime(alias string, datetimeField SearchAttributeFieldDateTime) SearchAttributeDateTime {
search_attribute.go
return SearchAttributeDateTime{
searchAttributeDefinition: searchAttributeDefinition{
alias: alias,
field: datetimeField.field,
valueType: enumspb.INDEXED_VALUE_TYPE_DATETIME,
},
}
}
func newSearchAttributeDateTimeByField(field string) SearchAttributeDateTime {
search_attribute.go
return SearchAttributeDateTime{
searchAttributeDefinition: searchAttributeDefinition{
alias: field,
field: field,
valueType: enumspb.INDEXED_VALUE_TYPE_DATETIME,
},
}
}
// Value sets the date time value of the search attribute.
// NewSearchAttributeInt creates a new integer search attribute given a predefined chasm field
func NewSearchAttributeInt(alias string, intField SearchAttributeFieldInt) SearchAttributeInt {
search_attribute.go
return SearchAttributeInt{
searchAttributeDefinition: searchAttributeDefinition{
alias: alias,
field: intField.field,
valueType: enumspb.INDEXED_VALUE_TYPE_INT,
},
}
}
// Value sets the integer value of the search attribute.
// NewSearchAttributeKeyword creates a new keyword search attribute given a predefined chasm field
func NewSearchAttributeKeyword(alias string, keywordField SearchAttributeFieldKeyword) SearchAttributeKeyword {
search_attribute.go
return SearchAttributeKeyword{
searchAttributeDefinition: searchAttributeDefinition{
alias: alias,
field: keywordField.field,
valueType: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
},
}
}
func newSearchAttributeKeywordByField(field string) SearchAttributeKeyword {
search_attribute.go
return SearchAttributeKeyword{
searchAttributeDefinition: searchAttributeDefinition{
alias: field,
field: field,
valueType: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
},
}
}
// Value sets the string value of the search attribute.
}
func newSearchAttributeKeywordListByField(field string) SearchAttributeKeywordList {
search_attribute.go
return SearchAttributeKeywordList{
searchAttributeDefinition: searchAttributeDefinition{
alias: field,
field: field,
valueType: enumspb.INDEXED_VALUE_TYPE_KEYWORD_LIST,
},
}
}
// Value sets the string list value of the search attribute.
// Operation returns tag for Operation
return NewStringTag("operation", operation)
}
// Error returns tag for Error
return ZapTag{
// NOTE: zap already chosen "error" as key
field: zap.Error(err),
}
}
// ServiceErrorType returns tag for ServiceErrorType
return NewStringTag("service-error-type", util.ErrorType(err))
}
// IsRetryable returns tag for IsRetryable
// WorkflowAction returns tag for WorkflowAction
return NewStringTag("wf-action", action)
}
// WorkflowListFilterType returns tag for WorkflowListFilterType
return NewStringTag("wf-list-filter-type", listFilterType)
}
// general
// WorkflowID returns tag for WorkflowID
// TODO: Rename to BusinessID.
return NewStringTag(WorkflowIDKey, workflowID)
}
// WorkflowType returns tag for WorkflowType
return NewStringTag("wf-type", wfType)
}
// WorkflowState returns tag for WorkflowState
// WorkflowNamespace returns tag for WorkflowNamespace
return NewStringTag("wf-namespace", namespace)
}
// WorkflowNamespaceIDs returns tag for WorkflowNamespaceIDs
// Component returns tag for Component
return NewStringTag("component", component)
}
// Lifecycle returns tag for Lifecycle
return NewStringTag("lifecycle", lifecycle)
}
// StoreOperation returns tag for StoreOperation
return NewStringTag("store-operation", storeOperation)
}
// OperationResult returns tag for OperationResult
return NewStringTag("operation-result", operationResult)
}
// ErrorType returns tag for ErrorType
// errorType returns tag for ErrorType given a string
return NewStringTag("error-type", errorType)
}
// Shardupdate returns tag for Shardupdate
return NewStringTag("shard-update", shardupdate)
}
// scope returns a tag for scope
// Pre-defined scope tags are in values.go.
return NewStringTag("scope", scope)
}
// general
// Service returns tag for Service
return NewStringTag("service", string(sv))
}
// Addresses returns tag for Addresses
return NewStringsTag("addresses", ads)
}
// ListenerName returns tag for ListenerName
// Address return tag for Address
return NewStringTag("address", ad)
}
// HostID return tag for HostID
return NewStringTag("hostId", hid)
}
// Env return tag for runtime environment
// Key returns tag for Key
return NewStringTag("key", k)
}
// Name returns tag for Name
return NewStringTag("name", k)
}
// Value returns tag for Value
return NewAnyTag("value", v)
}
// ValueType returns tag for ValueType
// Number returns tag for Number
return NewInt64("number", n)
}
// NextNumber returns tag for NextNumber
return NewInt64("next-number", n)
}
// ServerName returns tag for ServerName
// ShardID returns tag for ShardID
return NewInt32("shard-id", shardID)
}
// ShardTime returns tag for ShardTime
// PreviousShardRangeID returns tag for PreviousShardRangeID
return NewInt64("previous-shard-range-id", id)
}
// ShardRangeID returns tag for ShardRangeID
return NewInt64("shard-range-id", id)
}
// ShardContextState returns tag for ShardContextState
// QueueReaderID returns tag for queue readerID
return NewInt64("queue-reader-id", readerID)
}
// QueueAlert returns tag for queue alert
// NumberProcessed returns tag for NumberProcessed
return NewInt("number-processed", n)
}
// NumberDeleted returns tag for NumberDeleted
return NewInt("number-deleted", n)
}
// NumberChanged returns tag for NumberChanged
return NewInt("number-changed", n)
}
// TimerTaskStatus returns tag for TimerTaskStatus
// BootstrapHostPorts returns tag for bootstrap host ports
return NewStringTag("bootstrap-hostports", s)
}
// TLSCertFile returns tag for TLS cert file name
ctx context.Context,
row *sqlplugin.ClusterMetadataRow,
if row.Version == 0 {
return mdb.conn.ExecContext(ctx,
insertClusterMetadataQry,
constMetadataPartition,
row.ClusterName,
row.Data,
row.DataEncoding,
1,
)
}
return mdb.conn.ExecContext(ctx,
updateClusterMetadataQry,
ctx context.Context,
filter *sqlplugin.ClusterMetadataFilter,
var err error
var rows []sqlplugin.ClusterMetadataRow
switch {
case len(filter.ClusterName) != 0:
err = mdb.conn.SelectContext(ctx,
filter.PageSize,
)
err = mdb.conn.SelectContext(ctx,
&rows,
listClusterMetadataQry,
constMetadataPartition,
filter.PageSize,
)
}
}
ctx context.Context,
filter *sqlplugin.ClusterMetadataFilter,
var row sqlplugin.ClusterMetadataRow
err := mdb.conn.GetContext(ctx,
&row,
getClusterMetadataQry,
constMetadataPartition,
filter.ClusterName,
)
if err != nil {
return nil, err
}
return &row, err
}
ctx context.Context,
filter *sqlplugin.ClusterMetadataFilter,
var row sqlplugin.ClusterMetadataRow
err := mdb.conn.GetContext(ctx,
&row,
writeLockGetClusterMetadataQry,
constMetadataPartition,
filter.ClusterName,
)
if err != nil {
return nil, err
}
return &row, err
}
ctx context.Context,
row *sqlplugin.ClusterMembershipRow,
return mdb.conn.ExecContext(ctx,
templateUpsertActiveClusterMembership,
constMembershipPartition,
row.HostID,
row.RPCAddress,
row.RPCPort,
row.Role,
mdb.converter.ToSQLiteDateTime(row.SessionStart),
mdb.converter.ToSQLiteDateTime(row.LastHeartbeat),
mdb.converter.ToSQLiteDateTime(row.RecordExpiry))
}
func (mdb *db) GetClusterMembers(
ctx context.Context,
filter *sqlplugin.ClusterMembershipFilter,
var queryString strings.Builder
var operands []any
queryString.WriteString(templateGetClusterMembership)
operands = append(operands, constMembershipPartition)
if filter.HostIDEquals != nil {
queryString.WriteString(templateWithHostIDSuffix)
operands = append(operands, filter.HostIDEquals)
}
queryString.WriteString(templateWithRPCAddressSuffix)
operands = append(operands, filter.RPCAddressEquals)
}
queryString.WriteString(templateWithRoleSuffix)
operands = append(operands, filter.RoleEquals)
}
queryString.WriteString(templateWithHeartbeatSinceSuffix)
operands = append(operands, filter.LastHeartbeatAfter)
}
queryString.WriteString(templateWithRecordExpirySuffix)
operands = append(operands, filter.RecordExpiryAfter)
}
queryString.WriteString(templateWithSessionStartSuffix)
operands = append(operands, filter.SessionStartedAfter)
}
queryString.WriteString(templateWithHostIDGreaterSuffix)
operands = append(operands, filter.HostIDGreaterThan)
}
if filter.MaxRecordCount > 0 {
operands = append(operands, filter.MaxRecordCount)
}
var rows []sqlplugin.ClusterMembershipRow
if err := mdb.conn.SelectContext(ctx,
&rows,
compiledQryString,
operands...,
); err != nil {
return nil, err
}
rows[i].SessionStart = mdb.converter.FromSQLiteDateTime(rows[i].SessionStart)
cluster_metadata.go
rows[i].LastHeartbeat = mdb.converter.FromSQLiteDateTime(rows[i].LastHeartbeat)
rows[i].RecordExpiry = mdb.converter.FromSQLiteDateTime(rows[i].RecordExpiry)
}
}
ctx context.Context,
filter *sqlplugin.PruneClusterMembershipFilter,
return mdb.conn.ExecContext(ctx,
templatePruneStaleClusterMembership,
constMembershipPartition,
mdb.converter.ToSQLiteDateTime(filter.PruneRecordsBefore),
)
}
metricsHandler metrics.Handler,
timeSource clock.TimeSource,
var scheduler tasks.Scheduler[Executable]
taskChannelKeyFn := func(e Executable) TaskChannelKey {
return TaskChannelKey{
NamespaceID: e.GetNamespaceID(),
}
}
namespaceWeights := options.ActiveNamespaceWeights
namespaceName := namespace.EmptyName
return weight
}
fifoSchedulerOptions := &tasks.FIFOSchedulerOptions{
QueueSize: prioritySchedulerProcessorQueueSize,
WorkerCount: options.WorkerCount,
}
fifoScheduler := tasks.NewFIFOScheduler[Executable](
fifoSchedulerOptions,
logger,
)
// Wrap the FIFO scheduler with ExecutionAwareScheduler for sequential per-execution processing
executionAwareScheduler := tasks.NewExecutionAwareScheduler[Executable](
fifoScheduler,
options.ExecutionAwareSchedulerOptions,
executableQueueKeyFn,
logger,
metricsHandler,
timeSource,
)
scheduler = tasks.NewInterleavedWeightedRoundRobinScheduler(
tasks.InterleavedWeightedRoundRobinSchedulerOptions[Executable, TaskChannelKey]{
TaskChannelKeyFn: taskChannelKeyFn,
ChannelWeightFn: channelWeightFn,
ChannelWeightUpdateCh: channelWeightUpdateCh,
InactiveChannelDeletionDelay: options.InactiveNamespaceDeletionDelay,
},
executionAwareScheduler,
logger,
)
return &schedulerImpl{
Scheduler: scheduler,
namespaceRegistry: namespaceRegistry,
taskChannelKeyFn: taskChannelKeyFn,
channelWeightFn: channelWeightFn,
channelWeightUpdateCh: channelWeightUpdateCh,
executionAwareScheduler: executionAwareScheduler,
}
}
if s.channelWeightUpdateCh != nil {
s.namespaceRegistry.RegisterStateChangeCallback(s, func(ns *namespace.Namespace, deletedFromDb bool) {
select {
case s.channelWeightUpdateCh <- struct{}{}:
default:
}
})
}
}
if s.channelWeightUpdateCh != nil {
s.namespaceRegistry.UnregisterStateChangeCallback(s)
// note we can't close the channelWeightUpdateCh here
// as callback may still be triggered even after unregister returns
// due to race condition
//
// channelWeightFn is only not nil when using host level scheduler
// so Stop is only called when host is shutting down, and we don't need
// to worry about open channels
}
s.Scheduler.Stop()
}
return s.taskChannelKeyFn
}
// HandleBusyWorkflow implements BusyWorkflowHandler by delegating to the
}
return s.TaskKeyFn
}
func NewRateLimitedScheduler(
logger log.Logger,
metricsHandler metrics.Handler,
if delay := options.StartupDelay(); delay > 0 {
delayedRateLimiter, err := quotas.NewDelayedRequestRateLimiter(
rateLimiter,
delay,
timeSource,
)
if err != nil {
logger.Error("Failed to create delayed rate limited scheduler", tag.Error(err))
return baseScheduler
}
}
namespaceName, err := namespaceRegistry.GetNamespaceName(namespace.ID(e.GetNamespaceID()))
if err != nil {
return quotas.NewRequest(e.GetType().String(), taskSchedulerToken, namespaceName.String(), e.GetPriority().CallerType(), 0, "")
}
return append(
taskBaseMetricTags(e.GetTask(), namespaceRegistry, currentClusterName, chasmRegistry, GetTaskTypeTagValue),
}
baseScheduler,
rateLimiter,
timeSource,
taskQuotaRequestFn,
taskMetricsTagsFn,
tasks.RateLimitedSchedulerOptions{
Enabled: options.Enabled,
EnableShadowMode: options.EnableShadowMode,
},
logger,
metricsHandler,
)
return &rateLimitedSchedulerImpl{
Scheduler: rateLimitedScheduler,
baseScheduler: baseScheduler,
}
}
}
return s.baseScheduler.TaskChannelKeyFn()
}
// HandleBusyWorkflow implements BusyWorkflowHandler by delegating to the
// newFactory builds a ringpop factory
cfg := params.Config
if cfg.BroadcastAddress != "" && net.ParseIP(cfg.BroadcastAddress) == nil {
return nil, fmt.Errorf("%w: %s", errMalformedBroadcastAddress, cfg.BroadcastAddress)
}
cfg.MaxJoinDuration = defaultMaxJoinDuration
}
Config: params.Config,
ServiceName: params.ServiceName,
ServicePortMap: params.ServicePortMap,
Logger: params.Logger,
MetadataManager: params.MetadataManager,
RPCConfig: params.RPCConfig,
TLSFactory: params.TLSFactory,
DC: params.DC,
}, nil
}
// getMonitor returns a membership monitor
factory.monOnce.Do(func() {
ctx, cancel := context.WithTimeout(context.Background(), persistenceOperationTimeout)
defer cancel()
ctx = headers.SetCallerInfo(ctx, headers.SystemBackgroundHighCallerInfo)
currentClusterMetadata, err := factory.MetadataManager.GetCurrentClusterMetadata(ctx)
if err != nil {
factory.Logger.Fatal("Failed to get current cluster ID", tag.Error(err))
}
if currentClusterMetadata.UseClusterIdMembership {
appName = fmt.Sprintf("temporal-%s", currentClusterMetadata.GetClusterId())
}
rp, err := ringpop.New(appName, ringpop.Channel(factory.getTChannel()), ringpop.AddressResolverFunc(factory.broadcastAddressResolver))
if err != nil {
factory.Logger.Fatal("Failed to get new ringpop", tag.Error(err))
}
// Empirically, ringpop updates usually propagate in under a second even in relatively large clusters.
// 3 seconds is an over-estimate to be safer.
maxPropagationTime := dynamicconfig.RingpopApproximateMaxPropagationTime.Get(factory.DC)()
factory.go
replicaPoints := dynamicconfig.RingpopReplicaPoints.Get(factory.DC)()
factory.monitor = newMonitor(
factory.ServiceName,
factory.ServicePortMap,
rp,
factory.Logger,
factory.MetadataManager,
factory.broadcastAddressResolver,
factory.Config.MaxJoinDuration,
maxPropagationTime,
factory.getJoinTime(maxPropagationTime),
replicaPoints,
)
})
}
var alignTime time.Duration
switch factory.ServiceName {
case primitives.MatchingService:
alignTime = dynamicconfig.MatchingAlignMembershipChange.Get(factory.DC)()
case primitives.HistoryService:
alignTime = dynamicconfig.HistoryAlignMembershipChange.Get(factory.DC)()
}
return time.Time{}
}
return util.NextAlignedTime(time.Now().Add(maxPropagationTime), alignTime)
}
return buildBroadcastHostPort(factory.getTChannel().PeerInfo(), factory.Config.BroadcastAddress)
}
factory.chOnce.Do(func() {
ringpopServiceName := fmt.Sprintf("%v-ringpop", factory.ServiceName)
ringpopHostAddress := net.JoinHostPort(factory.getListenIP().String(), convert.IntToString(factory.RPCConfig.MembershipPort))
enableTLS := dynamicconfig.EnableRingpopTLS.Get(factory.DC)()
var tChannel *tchannel.Channel
if enableTLS {
tChannel = factory.getTLSChannel(ringpopHostAddress, ringpopServiceName)
}
})
}
func (factory *factory) getTCPChannel(ringpopHostAddress string, ringpopServiceName string) *tchannel.Channel {
factory.go
listener, err := net.Listen("tcp", ringpopHostAddress)
if err != nil {
factory.Logger.Fatal("Failed to start ringpop listener", tag.Error(err), tag.Address(ringpopHostAddress))
}
tChannel, err := tchannel.NewChannel(ringpopServiceName, &tchannel.ChannelOptions{})
factory.go
if err != nil {
factory.Logger.Fatal("Failed to create ringpop TChannel", tag.Error(err))
}
factory.Logger.Fatal("Failed to serve ringpop listener", tag.Error(err), tag.Address(ringpopHostAddress))
}
}
}
if factory.RPCConfig.BindOnLocalHost && len(factory.RPCConfig.BindOnIP) > 0 {
factory.Logger.Fatal("ListenIP failed, bindOnLocalHost and bindOnIP are mutually exclusive")
return nil
}
}
ip := net.ParseIP(factory.RPCConfig.BindOnIP)
if ip != nil {
return ip
}
factory.Logger.Fatal("ListenIP failed, unable to parse bindOnIP value", tag.Address(factory.RPCConfig.BindOnIP))
// closeTChannel allows fx Stop hook to close channel
if factory.channel != nil {
factory.getTChannel().Close()
factory.channel = nil
}
}
func (factory *factory) getHostInfoProvider() (membership.HostInfoProvider, error) {
factory.go
address, err := factory.broadcastAddressResolver()
if err != nil {
return nil, err
}
if !ok {
return nil, membership.ErrUnknownService
}
// ringpop messages. We use a different port for the service, so we
// replace that portion.
if err != nil {
return nil, err
}
return membership.NewHostInfoProvider(hostInfo), nil
}
hostInfo membership.HostInfo,
serializer serialization.Serializer,
return &Scanner{
context: scannerContext{
cfg: cfg,
sdkClientFactory: sdkClientFactory,
logger: logger,
metricsHandler: metricsHandler,
executionManager: executionManager,
taskManager: taskManager,
visibilityManager: visibilityManager,
metadataManager: metadataManager,
historyClient: historyClient,
matchingClient: matchingClient,
adminClient: adminClient,
namespaceRegistry: registry,
currentClusterName: currentClusterName,
hostInfo: hostInfo,
serializer: serializer,
},
}
}
// Start starts the scanner
ctx := context.WithValue(context.Background(), scannerContextKey, s.context)
ctx = headers.SetCallerInfo(ctx, headers.SystemBackgroundHighCallerInfo)
ctx, s.lifecycleCancel = context.WithCancel(ctx)
workerOpts := worker.Options{
Identity: "temporal-system@" + s.context.hostInfo.Identity(),
MaxConcurrentActivityExecutionSize: s.context.cfg.MaxConcurrentActivityExecutionSize(),
MaxConcurrentWorkflowTaskExecutionSize: s.context.cfg.MaxConcurrentWorkflowTaskExecutionSize(),
MaxConcurrentActivityTaskPollers: s.context.cfg.MaxConcurrentActivityTaskPollers(),
MaxConcurrentWorkflowTaskPollers: s.context.cfg.MaxConcurrentWorkflowTaskPollers(),
BackgroundActivityContext: ctx,
}
var workerTaskQueueNames []string
if s.context.cfg.Persistence.DefaultStoreType() != config.StoreTypeSQL && s.context.cfg.ExecutionsScannerEnabled() {
s.wg.Add(1)
go s.startWorkflowWithRetry(ctx, executionsScannerWFStartOptions, executionsScannerWFTypeName)
workerTaskQueueNames = append(workerTaskQueueNames, executionsScannerTaskQueueName)
s.context.logger.Info("ExecutionsScanner is not supported for SQL store")
}
if s.context.cfg.Persistence.DefaultStoreType() == config.StoreTypeSQL && s.context.cfg.TaskQueueScannerEnabled() {
scanner.go
go s.startWorkflowWithRetry(ctx, tlScannerWFStartOptions, tqScannerWFTypeName)
workerTaskQueueNames = append(workerTaskQueueNames, tqScannerTaskQueueName)
}
go s.startWorkflowWithRetry(ctx, historyScannerWFStartOptions, historyScannerWFTypeName)
workerTaskQueueNames = append(workerTaskQueueNames, historyScannerTaskQueueName)
}
s.wg.Add(1)
go s.startWorkflowWithRetry(ctx, build_ids.BuildIdScavengerWFStartOptions, build_ids.BuildIdScavangerWorkflowName)
}
if siOpts.OverdueNextActionTimeEnabled || siOpts.StuckOpenEnabled || siOpts.UnknownStateEnabled {
scheduleActivities := scheduleinvariants.NewActivities(
s.context.logger,
// TODO: There's no reason to register all activities and workflows on every task queue.
work := s.context.sdkClientFactory.NewWorker(s.context.sdkClientFactory.GetSystemClient(), tl, workerOpts)
scanner.go
work.RegisterWorkflowWithOptions(TaskQueueScannerWorkflow, workflow.RegisterOptions{Name: tqScannerWFTypeName})
work.RegisterWorkflowWithOptions(HistoryScannerWorkflow, workflow.RegisterOptions{Name: historyScannerWFTypeName})
work.RegisterWorkflowWithOptions(ExecutionsScannerWorkflow, workflow.RegisterOptions{Name: executionsScannerWFTypeName})
work.RegisterActivityWithOptions(TaskQueueScavengerActivity, activity.RegisterOptions{Name: taskQueueScavengerActivityName})
work.RegisterActivityWithOptions(HistoryScavengerActivity, activity.RegisterOptions{Name: historyScavengerActivityName})
work.RegisterActivityWithOptions(ExecutionsScavengerActivity, activity.RegisterOptions{Name: executionsScavengerActivityName})
// TODO: Nothing is gracefully stopping these workers or listening for fatal errors.
if err := work.Start(); err != nil {
}
}
}
s.lifecycleCancel()
s.wg.Wait()
}
// startWorkflowWithRetry starts a scanner workflow, retrying until it succeeds or the
// scanner shuts down. workflowType may be either a registered type-name string or the
// workflow function itself (registered under its Go function name).
func (s *Scanner) startWorkflowWithRetry(ctx context.Context, options sdkclient.StartWorkflowOptions, workflowType string, workflowArgs ...any) {
scanner.go
defer s.wg.Done()
policy := backoff.NewExponentialRetryPolicy(time.Second).
WithMaximumInterval(time.Minute).
WithExpirationInterval(backoff.NoInterval)
err := backoff.ThrottleRetryContext(ctx, func(ctx context.Context) error {
return s.startWorkflow(
ctx,
s.context.sdkClientFactory.GetSystemClient(),
options,
workflowType,
workflowArgs...,
)
}, policy, func(err error) bool {
})
// if the scanner shuts down before the workflow is started, then the error will be context canceled
s.context.logger.Fatal("unable to start scanner", tag.WorkflowType(workflowType), tag.Error(err))
scanner.go
}
}
workflowType string,
workflowArgs ...any,
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
_, err := client.ExecuteWorkflow(ctx, options, workflowType, workflowArgs...)
cancel()
if err != nil {
return nil
}
s.context.logger.Error("error starting workflow", tag.WorkflowType(workflowType), tag.Error(err))
scanner.go
return err
}
s.context.logger.Info("workflow successfully started", tag.WorkflowType(workflowType))
// TLSInfoFromContext extracts TLS information from the context's peer value.
p, ok := peer.FromContext(ctx)
if !ok {
return nil
}
return &tlsInfo
}
}
// PeerCert extracts an x509 certificate from given tlsInfo.
if tlsInfo == nil || len(tlsInfo.State.VerifiedChains) == 0 || len(tlsInfo.State.VerifiedChains[0]) == 0 {
}
// The assumption here is that we only expect a single verified chain of certs (first[0]).
// It's unclear how we should handle a situation when more than one chain is presented,
enablePrincipalPropagation dynamicconfig.BoolPropertyFnWithNamespaceFilter,
disableStreamingAuthorizer dynamicconfig.BoolPropertyFn,
return &Interceptor{
claimMapper: claimMapper,
authorizer: authorizer,
logger: logger,
namespaceChecker: namespaceChecker,
metricsHandler: metricsHandler,
authHeaderName: cmp.Or(authHeaderName, defaultAuthHeaderName),
authExtraHeaderName: cmp.Or(authExtraHeaderName, defaultAuthExtraHeaderName),
audienceGetter: audienceGetter,
exposeAuthorizerErrors: exposeAuthorizerErrors,
enableCrossNamespaceCommands: enableCrossNamespaceCommands,
enablePrincipalPropagation: enablePrincipalPropagation,
disableStreamingAuthorizer: disableStreamingAuthorizer,
}
}
func (a *Interceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
tlsConnection := TLSInfoFromContext(ctx)
authInfo := a.GetAuthInfo(tlsConnection, headers.NewGRPCHeaderGetter(ctx), func() string {
if a.audienceGetter != nil {
return a.audienceGetter.Audience(ctx, req, info)
})
if authInfo != nil {
var err error
claims, err = a.GetClaims(authInfo)
// Always strip inbound principal headers to prevent external callers from
// spoofing principal identity, regardless of whether the authorizer is enabled.
if a.authorizer != nil {
requestWithNamespace, ok := req.(hasNamespace)
if ok {
namespace = requestWithNamespace.GetNamespace()
}
ct := &CallTarget{
Namespace: namespace,
APIName: info.FullMethod,
Request: req,
}
principal, err := a.Authorize(ctx, claims, ct)
if err != nil {
}
if a.enablePrincipalPropagation != nil && a.enablePrincipalPropagation(namespace) && principal != nil {
interceptor.go
ctx = headers.SetPrincipal(ctx, principal)
}
// Authorize target namespaces in cross-namespace commands
if err := a.authorizeTargetNamespaces(ctx, claims, namespace, req); err != nil {
interceptor.go
return nil, err
}
}
}
// Returns nil if either the policy's claimMapper or authorizer are nil or when there is no auth information in the
// provided TLS info or headers.
func (a *Interceptor) GetAuthInfo(tlsConnection *credentials.TLSInfo, header headers.HeaderGetter, audienceGetter func() string) *AuthInfo {
interceptor.go
if a.claimMapper == nil || a.authorizer == nil {
return nil
}
var authHeader string
var authExtraHeader string
if header != nil {
authHeader = header.Get(a.authHeaderName)
authExtraHeader = header.Get(a.authExtraHeaderName)
}
clientCert := PeerCert(tlsConnection)
if clientCert != nil {
tlsSubject = &clientCert.Subject
}
if cm, ok := a.claimMapper.(ClaimMapperWithAuthInfoRequired); ok {
authInfoRequired = cm.AuthInfoRequired()
}
// Add auth info to context only if there's some auth info
}
return &AuthInfo{
// Logs and emits metrics when unauthorized.
// Returns the principal identity and any authorization error.
func (a *Interceptor) Authorize(ctx context.Context, claims *Claims, ct *CallTarget) (*commonpb.Principal, error) {
interceptor.go
if a.authorizer == nil {
return nil, nil
}
startTime := time.Now().UTC()
result, err := a.authorizer.Authorize(ctx, claims, ct)
metrics.ServiceAuthorizationLatency.With(mh).Record(time.Since(startTime))
if err != nil {
metrics.ServiceErrAuthorizeFailedCounter.With(mh).Record(1)
a.logger.Error("Authorization error", tag.Error(err))
return nil, errUnauthorized // return a generic error to the caller without disclosing details
}
// if a reason is included in the result, include it in the error message
if result.Reason != "" {
return nil, serviceerror.NewPermissionDenied(RequestUnauthorized, result.Reason)
}
return nil, errUnauthorized // return a generic error to the caller without disclosing details
interceptor.go
}
}
// getMetricsHandler returns a metrics handler with a namespace tag
nsTag := metrics.NamespaceUnknownTag()
if nsName != "" {
// Note that this is before the namespace state validation interceptor, so this
interceptor.go
// namespace name is not validated. We should only use it as a metric tag if it's a
// real namespace, to avoid unbounded cardinality issues.
if a.namespaceChecker.Exists(namespace.Name(nsName)) == nil {
}
}
return a.metricsHandler.WithTags(metrics.OperationTag(metrics.AuthorizationScope), nsTag)
interceptor.go
}
sourceNamespace string,
req any,
// Skip if cross-namespace commands are not enabled
if !a.enableCrossNamespaceCommands() {
}
// Cross-namespace commands can only be initiated via RespondWorkflowTaskCompletedRequest.
metricsHandler metrics.Handler,
serializer serialization.Serializer,
s := &ServerImpl{
so: opts,
stoppedCh: stoppedCh,
logger: logger,
namespaceLogger: namespaceLogger,
persistenceConfig: persistenceConfig,
clusterMetadata: clusterMetadata,
persistenceFactoryProvider: persistenceFactoryProvider,
metricsHandler: metricsHandler,
}
for _, svcMeta := range servicesGroup.Services {
if svcMeta != nil {
s.servicesMetadata = append(s.servicesMetadata, svcMeta)
}
}
// Store serializer for use in Start()
return s
}
s.logger.Info("Starting server for services", tag.Value(s.so.serviceNames))
s.logger.Debug(s.so.config.String())
if err := initSystemNamespaces(
ctx,
&s.persistenceConfig,
s.clusterMetadata.CurrentClusterName,
s.so.persistenceServiceResolver,
s.persistenceFactoryProvider,
s.logger,
s.so.customDataStoreFactory,
s.metricsHandler,
s.serializer,
); err != nil {
return fmt.Errorf("unable to initialize system namespace: %w", err)
}
}
close(s.stoppedCh)
svcs := slices.Clone(s.servicesMetadata)
slices.SortFunc(svcs, func(a, b *ServicesMetadata) int {
return -cmp.Compare(initOrder[a.serviceName], initOrder[b.serviceName]) // note negative
})
for _, svc := range svcs {
svc.Stop(ctx)
}
s.so.metricHandler.Stop(s.logger)
}
}
// The membership join time may exceed the configured max join duration.
// Double the service start timeout to make sure there is enough time for start logic.
timeout := max(serviceStartTimeout, 2*s.so.config.Global.Membership.MaxJoinDuration)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
svcs := slices.Clone(s.servicesMetadata)
slices.SortFunc(svcs, func(a, b *ServicesMetadata) int {
return cmp.Compare(initOrder[a.serviceName], initOrder[b.serviceName])
})
for _, svc := range svcs {
err := svc.app.Start(ctx)
if err != nil {
allErrs = multierr.Append(allErrs, fmt.Errorf("failed to start service %v: %w", svc.serviceName, err))
}
}
}
metricsHandler metrics.Handler,
serializer serialization.Serializer,
clusterName := persistenceClient.ClusterName(currentClusterName)
metricsHandler = metricsHandler.WithTags(metrics.ServiceNameTag(primitives.ServerService))
dataStoreFactory := persistenceClient.DataStoreFactoryProvider(
clusterName,
persistenceServiceResolver,
cfg,
customDataStoreFactory,
logger,
metricsHandler,
telemetry.NoopTracerProvider,
serializer,
)
factory := persistenceFactoryProvider(persistenceClient.NewFactoryParams{
DataStoreFactory: dataStoreFactory,
Cfg: cfg,
PersistenceMaxQPS: nil,
PersistenceNamespaceMaxQPS: nil,
ClusterName: persistenceClient.ClusterName(currentClusterName),
MetricsHandler: metricsHandler,
Logger: logger,
Serializer: serializer,
})
defer factory.Close()
metadataManager, err := factory.NewMetadataManager()
if err != nil {
return fmt.Errorf("unable to initialize metadata manager: %w", err)
}
ctx, cancel := context.WithTimeout(
headers.SetCallerInfo(ctx, headers.SystemBackgroundHighCallerInfo),
30*time.Second,
)
defer cancel()
if err = metadataManager.InitializeSystemNamespaces(ctx, currentClusterName); err != nil {
return fmt.Errorf("unable to register system namespace: %w", err)
}
}
logger log.Logger,
clusterName string,
return &metadataManagerImpl{
serializer: serializer,
persistence: persistence,
logger: logger,
clusterName: clusterName,
}
}
func (m *metadataManagerImpl) GetName() string {
ctx context.Context,
request *CreateNamespaceRequest,
datablob, err := m.serializer.NamespaceDetailToBlob(request.Namespace)
if err != nil {
return nil, err
}
ID: request.Namespace.Info.Id,
Name: request.Namespace.Info.Name,
IsGlobal: request.IsGlobalNamespace,
Namespace: datablob,
})
}
ctx context.Context,
request *GetNamespaceRequest,
resp, err := m.persistence.GetNamespace(ctx, request)
if err != nil {
return nil, err
}
return ConvertInternalGetNamespaceResponse(m.serializer, m.clusterName, resp)
metadata_manager.go
}
}
func ConvertInternalGetNamespaceResponse(serializer serialization.Serializer, currentClusterName string, d *InternalGetNamespaceResponse) (*GetNamespaceResponse, error) {
metadata_manager.go
ns, err := serializer.NamespaceDetailFromBlob(d.Namespace)
if err != nil {
return nil, err
}
}
if ns.Config.BadBinaries == nil || ns.Config.BadBinaries.Binaries == nil {
metadata_manager.go
ns.Config.BadBinaries = &namespacepb.BadBinaries{Binaries: map[string]*namespacepb.BadBinaryInfo{}}
metadata_manager.go
}
ns.ReplicationConfig.ActiveClusterName = GetOrUseDefaultActiveCluster(currentClusterName, ns.ReplicationConfig.ActiveClusterName)
metadata_manager.go
ns.ReplicationConfig.Clusters = GetOrUseDefaultClusters(currentClusterName, ns.ReplicationConfig.Clusters)
return &GetNamespaceResponse{
Namespace: ns,
IsGlobalNamespace: d.IsGlobal,
NotificationVersion: d.NotificationVersion,
}, nil
}
ctx context.Context,
request *ListNamespacesRequest,
var namespaces []*GetNamespaceResponse
nextPageToken := request.NextPageToken
pageSize := request.PageSize
for {
resp, err := m.persistence.ListNamespaces(ctx, &InternalListNamespacesRequest{
PageSize: pageSize,
NextPageToken: nextPageToken,
})
if err != nil {
return nil, err
}
for _, d := range resp.Namespaces {
ret, err := ConvertInternalGetNamespaceResponse(m.serializer, m.clusterName, d)
metadata_manager.go
if err != nil {
return nil, err
}
if ret.Namespace.Info.State == enumspb.NAMESPACE_STATE_DELETED && !request.IncludeDeleted {
metadata_manager.go
deletedNamespacesCount++
continue
}
}
if len(nextPageToken) == 0 {
// Page wasn't full, no more namespaces in DB.
break
}
if deletedNamespacesCount == 0 {
}
Namespaces: namespaces,
NextPageToken: nextPageToken,
}, nil
}
ctx context.Context,
currentClusterName string,
_, err := m.CreateNamespace(ctx, &CreateNamespaceRequest{
Namespace: &persistencespb.NamespaceDetail{
Info: &persistencespb.NamespaceInfo{
Id: primitives.SystemNamespaceID,
Name: primitives.SystemLocalNamespace,
State: enumspb.NAMESPACE_STATE_REGISTERED,
Description: "Temporal internal system namespace",
},
Config: &persistencespb.NamespaceConfig{
Retention: durationpb.New(primitives.SystemNamespaceRetention),
HistoryArchivalState: enumspb.ARCHIVAL_STATE_DISABLED,
VisibilityArchivalState: enumspb.ARCHIVAL_STATE_DISABLED,
},
ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
ActiveClusterName: currentClusterName,
Clusters: []string{currentClusterName},
},
FailoverVersion: common.EmptyVersion,
FailoverNotificationVersion: -1,
},
IsGlobalNamespace: false,
})
if err != nil {
if _, ok := err.(*serviceerror.NamespaceAlreadyExists); !ok {
return err
}
}
}
}
m.persistence.Close()
}
func (m *metadataManagerImpl) WatchNamespaces(context.Context) (<-chan *NamespaceWatchEvent, error) {
metadata_manager.go
return nil, ErrWatchNotSupported
}
}
*x = ShardInfo{}
mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ShardInfo) String() string {
func (*ShardInfo) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_executions_proto_msgTypes[0]
if x != nil {
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
if x != nil {
return x.ShardId
}
return 0
}
if x != nil {
return x.RangeId
}
return 0
}
if x != nil {
return x.Owner
}
return ""
}
}
if x != nil {
return x.ReplicationDlqAckLevel
}
return nil
}
if x != nil {
return x.QueueStates
}
return nil
}
}
func init() { file_temporal_server_api_persistence_v1_executions_proto_init() }
executions.pb.go
func file_temporal_server_api_persistence_v1_executions_proto_init() {
if File_temporal_server_api_persistence_v1_executions_proto != nil {
return
}
file_temporal_server_api_persistence_v1_chasm_proto_init()
file_temporal_server_api_persistence_v1_hsm_proto_init()
file_temporal_server_api_persistence_v1_queues_proto_init()
file_temporal_server_api_persistence_v1_update_proto_init()
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[1].OneofWrappers = []any{
(*WorkflowExecutionInfo_LastWorkflowTaskFailureCause)(nil),
(*WorkflowExecutionInfo_LastWorkflowTaskTimedOutType)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[8].OneofWrappers = []any{
(*TransferTaskInfo_CloseExecutionTaskDetails_)(nil),
(*TransferTaskInfo_ChasmTaskInfo)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[10].OneofWrappers = []any{
(*VisibilityTaskInfo_ChasmTaskInfo)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[11].OneofWrappers = []any{
(*TimerTaskInfo_ChasmTaskInfo)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[13].OneofWrappers = []any{
(*OutboundTaskInfo_StateMachineInfo)(nil),
(*OutboundTaskInfo_ChasmTaskInfo)(nil),
(*OutboundTaskInfo_WorkerCommandsTask)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[17].OneofWrappers = []any{
(*ActivityInfo_UseWorkflowBuildIdInfo_)(nil),
(*ActivityInfo_LastIndependentlyAssignedBuildId)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[23].OneofWrappers = []any{
(*Callback_Nexus_)(nil),
(*Callback_Hsm)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[40].OneofWrappers = []any{
(*ActivityInfo_PauseInfo_Manual_)(nil),
(*ActivityInfo_PauseInfo_RuleId)(nil),
}
file_temporal_server_api_persistence_v1_executions_proto_msgTypes[46].OneofWrappers = []any{
(*CallbackInfo_Trigger_WorkflowClosed)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_executions_proto_rawDesc), len(file_temporal_server_api_persistence_v1_executions_proto_rawDesc)),
NumEnums: 0,
NumMessages: 47,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_executions_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_executions_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_executions_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_executions_proto = out.File
file_temporal_server_api_persistence_v1_executions_proto_goTypes = nil
file_temporal_server_api_persistence_v1_executions_proto_depIdxs = nil
}
)
return ClusterName(config.CurrentClusterName)
}
func EventBlobCacheProvider(
logger log.Logger,
serializer serialization.Serializer,
return persistence.NewEventsBlobCache(
dynamicconfig.XDCCacheMaxSizeBytes.Get(dc)(),
20*time.Second,
logger,
)
}
func EnableDataLossMetricsProvider(
dc *dynamicconfig.Collection,
return EnableDataLossMetrics(dynamicconfig.EnableDataLossMetrics.Get(dc))
}
func EnableBestEffortDeleteTasksOnWorkflowUpdateProvider(
dc *dynamicconfig.Collection,
return EnableBestEffortDeleteTasksOnWorkflowUpdate(dynamicconfig.EnableBestEffortDeleteTasksOnWorkflowUpdate.Get(dc))
}
func FactoryProvider(
params NewFactoryParams,
var systemRequestRateLimiter, namespaceRequestRateLimiter, shardRequestRateLimiter quotas.RequestRateLimiter
if params.PersistenceMaxQPS != nil && params.PersistenceMaxQPS() > 0 {
systemRequestRateLimiter = NewPriorityRateLimiter(
params.PersistenceMaxQPS,
RequestPriorityFn,
params.OperatorRPSRatio,
params.PersistenceBurstRatio,
params.HealthSignals,
params.DynamicRateLimitingParams,
params.MetricsHandler,
params.Logger,
)
namespaceRequestRateLimiter = NewPriorityNamespaceRateLimiter(
params.PersistenceMaxQPS,
params.PersistenceNamespaceMaxQPS,
RequestPriorityFn,
params.OperatorRPSRatio,
params.PersistenceBurstRatio,
)
shardRequestRateLimiter = NewPriorityNamespaceShardRateLimiter(
params.PersistenceMaxQPS,
params.PersistencePerShardNamespaceMaxQPS,
RequestPriorityFn,
params.OperatorRPSRatio,
params.PersistenceBurstRatio,
)
}
params.DataStoreFactory,
params.Cfg,
systemRequestRateLimiter,
namespaceRequestRateLimiter,
shardRequestRateLimiter,
params.Serializer,
params.EventBlobCache,
string(params.ClusterName),
params.MetricsHandler,
params.Logger,
params.HealthSignals,
params.EnableDataLossMetrics,
params.EnableBestEffortDeleteTasksOnWorkflowUpdate,
)
}
metricsHandler metrics.Handler,
logger log.ThrottledLogger,
if dynamicconfig.PersistenceHealthSignalMetricsEnabled.Get(dynamicCollection)() {
aggregator := persistence.NewHealthSignalAggregator(
dynamicconfig.PersistenceHealthSignalAggregationEnabled.Get(dynamicCollection)(),
dynamicconfig.PersistenceHealthSignalPercentilesEnabled.Get(dynamicCollection),
dynamicconfig.PersistenceHealthSignalWindowSize.Get(dynamicCollection)(),
dynamicconfig.PersistenceHealthSignalBufferSize.Get(dynamicCollection)(),
metricsHandler,
logger,
dynamicconfig.PersistenceHealthSignalLatencyWindowSize.Get(dynamicCollection)(),
dynamicconfig.PersistenceHealthSignalLatencyWindowCount.Get(dynamicCollection)(),
)
lc.Append(fx.StopHook(aggregator.Stop))
return aggregator
}
return persistence.NoopHealthSignalAggregator
tracerProvider trace.TracerProvider,
serializer serialization.Serializer,
var dataStoreFactory persistence.DataStoreFactory
defaultStoreCfg := cfg.DataStores[cfg.DefaultStore]
switch {
case defaultStoreCfg.Cassandra != nil:
dataStoreFactory = cassandra.NewFactory(*defaultStoreCfg.Cassandra, r, string(clusterName), logger, metricsHandler, serializer)
dataStoreFactory = sql.NewFactory(*defaultStoreCfg.SQL, r, string(clusterName), logger, metricsHandler, serializer)
case defaultStoreCfg.CustomDataStoreConfig != nil:
dataStoreFactory = abstractDataStoreFactory.NewFactory(*defaultStoreCfg.CustomDataStoreConfig, r, string(clusterName), logger, metricsHandler, serializer)
}
dataStoreFactory = faultinjection.NewFaultInjectionDatastoreFactory(defaultStoreCfg.FaultInjection, dataStoreFactory)
}
if otel.IsEnabled(tracer) {
dataStoreFactory = telemetry.NewTelemetryDataStoreFactory(dataStoreFactory, logger, tracer)
}
}
lc.Append(fx.StopHook(f.Close))
}
func managerProvider[T persistence.Closeable](newManagerFn func(Factory) (T, error)) func(Factory, fx.Lifecycle) (T, error) {
fx.go
return func(f Factory, lc fx.Lifecycle) (T, error) {
if err != nil {
var unimpl *serviceerror.Unimplemented
if errors.As(err, &unimpl) {
return nilT, err
}
return manager, nil
}
}
ctx context.Context,
request *p.GetHistoryTasksRequest,
switch request.TaskCategory.Type() {
return m.getHistoryImmediateTasks(ctx, request)
return m.getHistoryScheduledTasks(ctx, request)
default:
return nil, serviceerror.NewInternalf("Unknown task category type: %v", request.TaskCategory)
ctx context.Context,
request *p.GetHistoryTasksRequest,
// This is for backward compatiblity.
// These task categories exist before the general history_immediate_tasks table is created,
// so they have their own tables.
categoryID := request.TaskCategory.ID()
switch categoryID {
return m.getTransferTasks(ctx, request)
return m.getVisibilityTasks(ctx, request)
case tasks.CategoryIDReplication:
return m.getReplicationTasks(ctx, request)
}
inclusiveMinTaskID, exclusiveMaxTaskID, err := getImmediateTaskReadRange(request)
execution_tasks.go
if err != nil {
return nil, err
}
rows, err := m.DB.RangeSelectFromHistoryImmediateTasks(ctx, sqlplugin.HistoryImmediateTasksRangeFilter{
execution_tasks.go
ShardID: request.ShardID,
CategoryID: int32(categoryID),
InclusiveMinTaskID: inclusiveMinTaskID,
ExclusiveMaxTaskID: exclusiveMaxTaskID,
PageSize: request.BatchSize,
})
if err != nil {
if err != sql.ErrNoRows {
return nil, serviceerror.NewUnavailablef(
}
}
Tasks: make([]p.InternalHistoryTask, len(rows)),
}
if len(rows) == 0 {
return resp, nil
}
for i, row := range rows {
ctx context.Context,
request *p.GetHistoryTasksRequest,
// This is for backward compatiblity.
// These task categories exist before the general history_scheduled_tasks table is created,
// so they have their own tables.
categoryID := request.TaskCategory.ID()
if categoryID == tasks.CategoryIDTimer {
}
pageToken := &scheduledTaskPageToken{TaskID: math.MinInt64, Timestamp: request.InclusiveMinTaskKey.FireTime}
ctx context.Context,
request *p.GetHistoryTasksRequest,
inclusiveMinTaskID, exclusiveMaxTaskID, err := getImmediateTaskReadRange(request)
if err != nil {
return nil, err
}
rows, err := m.DB.RangeSelectFromTransferTasks(ctx, sqlplugin.TransferTasksRangeFilter{
execution_tasks.go
ShardID: request.ShardID,
InclusiveMinTaskID: inclusiveMinTaskID,
ExclusiveMaxTaskID: exclusiveMaxTaskID,
PageSize: request.BatchSize,
})
if err != nil {
if err != sql.ErrNoRows {
return nil, serviceerror.NewUnavailablef("GetTransferTasks operation failed. Select failed. Error: %v", err)
}
}
Tasks: make([]p.InternalHistoryTask, len(rows)),
}
if len(rows) == 0 {
}
for i, row := range rows {
ctx context.Context,
request *p.GetHistoryTasksRequest,
pageToken := &scheduledTaskPageToken{TaskID: math.MinInt64, Timestamp: request.InclusiveMinTaskKey.FireTime}
if len(request.NextPageToken) > 0 {
if err := pageToken.deserialize(request.NextPageToken); err != nil {
return nil, serviceerror.NewInternalf("error deserializing timerTaskPageToken: %v", err)
}
rows, err := m.DB.RangeSelectFromTimerTasks(ctx, sqlplugin.TimerTasksRangeFilter{
execution_tasks.go
ShardID: request.ShardID,
InclusiveMinVisibilityTimestamp: pageToken.Timestamp,
InclusiveMinTaskID: pageToken.TaskID,
ExclusiveMaxVisibilityTimestamp: request.ExclusiveMaxTaskKey.FireTime,
PageSize: request.BatchSize,
})
if err != nil && err != sql.ErrNoRows {
return nil, serviceerror.NewUnavailablef("GetTimerTasks operation failed. Select failed. Error: %v", err)
}
resp := &p.InternalGetHistoryTasksResponse{Tasks: make([]p.InternalHistoryTask, 0, len(rows))}
execution_tasks.go
for _, row := range rows {
resp.Tasks = append(resp.Tasks, p.InternalHistoryTask{
Key: tasks.NewKey(row.VisibilityTimestamp, row.TaskID),
}
pageToken = &scheduledTaskPageToken{
TaskID: rows[request.BatchSize-1].TaskID + 1,
func getImmediateTaskReadRange(
request *p.GetHistoryTasksRequest,
inclusiveMinTaskID = request.InclusiveMinTaskKey.TaskID
if len(request.NextPageToken) > 0 {
inclusiveMinTaskID, err = deserializePageToken(request.NextPageToken)
if err != nil {
}
}
ctx context.Context,
request *p.GetHistoryTasksRequest,
inclusiveMinTaskID, exclusiveMaxTaskID, err := getImmediateTaskReadRange(request)
if err != nil {
return nil, err
}
rows, err := m.DB.RangeSelectFromVisibilityTasks(ctx, sqlplugin.VisibilityTasksRangeFilter{
execution_tasks.go
ShardID: request.ShardID,
InclusiveMinTaskID: inclusiveMinTaskID,
ExclusiveMaxTaskID: exclusiveMaxTaskID,
PageSize: request.BatchSize,
})
if err != nil {
if err != sql.ErrNoRows {
return nil, serviceerror.NewUnavailablef("GetVisibilityTasks operation failed. Select failed. Error: %v", err)
}
}
Tasks: make([]p.InternalHistoryTask, len(rows)),
}
if len(rows) == 0 {
}
for i, row := range rows {
fx.Provide(schedulerpb.NewSchedulerServiceLayeredClient),
fx.Provide(
return c
},
func(m cluster.Metadata) dlq.CurrentClusterName {
return dlq.CurrentClusterName(m.GetCurrentClusterName())
},
func(b client.Bean) dlq.TaskClientDialer {
return dlq.TaskClientDialerFn(func(_ context.Context, address string) (dlq.TaskClient, error) {
c, err := b.GetRemoteAdminClient(address)
if err != nil {
logger log.Logger,
testHooks testhooks.TestHooks,
return nsreplication.NewTaskExecutor(
clusterMetadata.GetCurrentClusterName(),
metadataManager,
dataMerger,
admitter,
logger,
testHooks,
)
}),
fx.Provide(nsreplication.NewNoopDataMerger),
fx.Provide(nsreplication.NewDefaultAdmitter),
)
func ThrottledLoggerRpsFnProvider(serviceConfig *Config) resource.ThrottledLoggerRpsFn {
fx.go
return func() float64 { return float64(serviceConfig.ThrottledLogRPS()) }
}
persistenceLazyLoadedServiceResolver service.PersistenceLazyLoadedServiceResolver,
logger log.SnTaggedLogger,
return service.NewPersistenceRateLimitingParams(
serviceConfig.PersistenceMaxQPS,
serviceConfig.PersistenceGlobalMaxQPS,
serviceConfig.PersistenceNamespaceMaxQPS,
serviceConfig.PersistenceGlobalNamespaceMaxQPS,
serviceConfig.PersistencePerShardNamespaceMaxQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.PersistenceQPSBurstRatio,
serviceConfig.PersistenceDynamicRateLimitingParams,
persistenceLazyLoadedServiceResolver,
logger,
)
}
hn, err := os.Hostname()
return membership.NewHostInfoFromAddress(hn), err
}
func ServiceResolverProvider(
membershipMonitor membership.Monitor,
return membershipMonitor.GetResolver(primitives.WorkerService)
}
func ConfigProvider(
dc *dynamicconfig.Collection,
persistenceConfig *config.Persistence,
return NewConfig(
dc,
persistenceConfig,
)
}
func VisibilityManagerProvider(
chasmRegistry *chasm.Registry,
serializer serialization.Serializer,
return visibility.NewManager(
*persistenceConfig,
persistenceServiceResolver,
customVisibilityStoreFactory,
nil, // worker visibility never write
saProvider,
searchAttributesMapperProvider,
namespaceRegistry,
chasmRegistry,
serviceConfig.VisibilityPersistenceMaxReadQPS,
serviceConfig.VisibilityPersistenceMaxWriteQPS,
serviceConfig.OperatorRPSRatio,
serviceConfig.VisibilityPersistenceSlowQueryThreshold,
serviceConfig.EnableReadFromSecondaryVisibility,
serviceConfig.VisibilityEnableShadowReadMode,
dynamicconfig.GetStringPropertyFn(visibility.SecondaryVisibilityWritingModeOff), // worker visibility never write
serviceConfig.VisibilityDisableOrderByClause,
serviceConfig.VisibilityEnableManualPagination,
serviceConfig.VisibilityEnableUnifiedQueryConverter,
metricsHandler,
logger,
serializer,
)
}
lc.Append(fx.StartStopHook(svc.Start, svc.Stop))
}
type perNamespaceWorkerManagerInitParams struct {
}
func PerNamespaceWorkerManagerProvider(params perNamespaceWorkerManagerInitParams) *PerNamespaceWorkerManager {
fx.go
return NewPerNamespaceWorkerManager(
params.Logger,
params.SdkClientFactory,
params.NamespaceRegistry,
params.HostName,
params.Config,
params.ClusterMetadata,
params.Components,
primitives.PerNSWorkerTaskQueue,
)
}
opts, err := rpcFactory.GetInternodeGRPCServerOptions()
if err != nil {
logger.Fatal("Failed to get gRPC server options", tag.Error(err))
}
}
taskQueueFactory SequentialTaskQueueFactory[T],
logger log.Logger,
return &SequentialScheduler[T]{
status: common.DaemonStatusInitialized,
shutdownChan: make(chan struct{}),
options: options,
logger: logger,
queueFactory: taskQueueFactory,
queueChan: make(chan SequentialTaskQueue[T], options.QueueSize),
queues: collection.NewShardedConcurrentTxMap(1024, taskQueueHashFn),
}
}
if !atomic.CompareAndSwapInt32(
&s.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
initialWorkerCount, workerCountSubscriptionCancelFn := s.options.WorkerCount(s.updateWorkerCount)
sequential_scheduler.go
s.workerCountSubscriptionCancelFn = workerCountSubscriptionCancelFn
s.updateWorkerCount(initialWorkerCount)
s.logger.Info("sequential scheduler started")
}
if !atomic.CompareAndSwapInt32(
&s.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
s.workerCountSubscriptionCancelFn()
s.updateWorkerCount(0)
// must be called after the close of the shutdownChan
s.drainTasks()
go func() {
if success := common.AwaitWaitGroup(&s.shutdownWG, time.Minute); !success {
s.logger.Warn("sequential scheduler timed out waiting for workers")
}
}()
}
}
func (s *SequentialScheduler[T]) updateWorkerCount(targetWorkerNum int) {
sequential_scheduler.go
s.workerLock.Lock()
defer s.workerLock.Unlock()
if s.isStopped() {
// in case there's a race condition between subscription callback invocation
// and the invocation made from Stop()
targetWorkerNum = 0
}
s.logger.Error("Target worker pool size is negative. Please fix the dynamic config.", tag.Key("worker-pool-size"), tag.Value(targetWorkerNum))
return
}
if targetWorkerNum == currentWorkerNum {
return
}
s.startWorkers(targetWorkerNum - currentWorkerNum)
} else {
}
s.logger.Info("Update worker pool size", tag.Key("worker-pool-size"), tag.Value(targetWorkerNum))
sequential_scheduler.go
}
func (s *SequentialScheduler[T]) startWorkers(
count int,
for range count {
shutdownCh := make(chan struct{})
s.workerShutdownCh = append(s.workerShutdownCh, shutdownCh)
s.shutdownWG.Add(1)
go s.pollTaskQueue(shutdownCh)
}
}
func (s *SequentialScheduler[T]) stopWorkers(
count int,
shutdownChToClose := s.workerShutdownCh[:count]
s.workerShutdownCh = s.workerShutdownCh[count:]
for _, shutdownCh := range shutdownChToClose {
close(shutdownCh)
}
}
func (s *SequentialScheduler[T]) pollTaskQueue(workerShutdownCh <-chan struct{}) {
sequential_scheduler.go
defer s.shutdownWG.Done()
for {
select {
s.drainTasks()
return
case <-workerShutdownCh:
return
}
LoopDrainQueues:
for {
select {
case queue := <-s.queueChan:
LoopDrainSingleQueue:
}
}
break LoopDrainQueues
}
}
}
return atomic.LoadInt32(&s.status) == common.DaemonStatusStopped
}
options *FIFOSchedulerOptions,
logger log.Logger,
return &FIFOScheduler[T]{
status: common.DaemonStatusInitialized,
options: options,
logger: logger,
tasksChan: make(chan T, options.QueueSize),
}
}
if !atomic.CompareAndSwapInt32(
&f.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
initialWorkerCount, workerCountSubscriptionCancelFn := f.options.WorkerCount(f.updateWorkerCount)
fifo_scheduler.go
f.workerCountSubscriptionCancelFn = workerCountSubscriptionCancelFn
f.updateWorkerCount(initialWorkerCount)
f.logger.Info("fifo scheduler started")
}
if !atomic.CompareAndSwapInt32(
&f.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
f.updateWorkerCount(0)
f.drainTasks()
go func() {
if success := common.AwaitWaitGroup(&f.shutdownWG, time.Minute); !success {
f.logger.Warn("fifo scheduler timed out waiting for workers")
}
}()
}
}
f.workerLock.Lock()
defer f.workerLock.Unlock()
if f.isStopped() {
// in case there's a race condition between subscription callback invocation
// and the invocation made from Stop()
targetWorkerNum = 0
}
f.logger.Error("Target worker pool size is negative. Please fix the dynamic config.", tag.Key("worker-pool-size"), tag.Value(targetWorkerNum))
return
}
if targetWorkerNum == currentWorkerNum {
return
}
f.startWorkers(targetWorkerNum - currentWorkerNum)
} else {
}
f.logger.Info("Update worker pool size", tag.Key("worker-pool-size"), tag.Value(targetWorkerNum))
fifo_scheduler.go
}
func (f *FIFOScheduler[T]) startWorkers(
count int,
for range count {
shutdownCh := make(chan struct{})
f.workerShutdownCh = append(f.workerShutdownCh, shutdownCh)
f.shutdownWG.Add(1)
go f.processTask(shutdownCh)
}
}
func (f *FIFOScheduler[T]) stopWorkers(
count int,
shutdownChToClose := f.workerShutdownCh[:count]
f.workerShutdownCh = f.workerShutdownCh[count:]
for _, shutdownCh := range shutdownChToClose {
close(shutdownCh)
}
}
func (f *FIFOScheduler[T]) processTask(
shutdownCh chan struct{},
defer f.shutdownWG.Done()
for {
if f.isStopped() {
return
}
case <-shutdownCh:
return
}
case task := <-f.tasksChan:
f.executeTask(task)
return
}
}
}
LoopDrain:
for {
select {
case task := <-f.tasksChan:
task.Abort()
break LoopDrain
}
}
}
return atomic.LoadInt32(&f.status) == common.DaemonStatusStopped
}
func LoadAndSplitQueryFromReaders(
readers []io.Reader,
result := make([]string, 0, querySliceDefaultSize)
for _, r := range readers {
content, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("error reading contents: %w", err)
}
contentStr := string(bytes.ToLower(content))
for i, j := 0, 0; i < n; i = j {
// stack to keep track of open parenthesis/blocks
var st []byte
var stmtBuilder strings.Builder
stmtLoop:
for ; j < n; j++ {
switch contentStr[j] {
if len(st) == 0 {
j++
break stmtLoop
}
st = append(st, sqlLeftParenthesis)
if len(st) == 0 || st[len(st)-1] != sqlLeftParenthesis {
return nil, fmt.Errorf("error reading contents: unmatched right parenthesis")
}
case sqlDoubleDollarKeyword[0]:
}
if !hasWordAt(contentStr, sqlIfKeyword, j) {
continue
}
if hasWordsBefore(contentStr, j-1, sqlAddKeyword, sqlColumnKeyword) ||
j += len(sqlIfKeyword) - 1
if !hasWordAt(contentStr, sqlLoopKeyword, j) {
continue
}
st = append(st, sqlLoopKeyword[0])
j += len(sqlLoopKeyword) - 1
if hasWordAt(contentStr, sqlBeginKeyword, j) {
j += len(sqlBeginKeyword) - 1
}
if !hasWordAt(contentStr, sqlEndKeyword, j) {
continue
}
if ok, after := hasWordAfter(contentStr, sqlIfKeyword, j+len(sqlEndKeyword)); ok {
query_util.go
if len(st) == 0 || st[len(st)-1] != sqlIfKeyword[0] {
return nil, errors.New("error reading contents: unmatched `END IF` keyword")
st = st[:len(st)-1]
j = after + len(sqlIfKeyword) - 1
} else if ok, after := hasWordAfter(contentStr, sqlLoopKeyword, j+len(sqlEndKeyword)); ok {
query_util.go
//nolint:revive
if len(st) == 0 || st[len(st)-1] != sqlLoopKeyword[0] {
st = st[:len(st)-1]
j = after + len(sqlLoopKeyword) - 1
if len(st) == 0 || st[len(st)-1] != sqlBeginKeyword[0] {
return nil, errors.New("error reading contents: unmatched `END` keyword")
}
j += len(sqlEndKeyword) - 1
}
quote := contentStr[j]
j++
for j < n && contentStr[j] != quote {
j++
}
if j == n {
return nil, fmt.Errorf("error reading contents: unmatched quotes")
}
if j+len(sqlLineComment) <= n && contentStr[j:j+len(sqlLineComment)] == sqlLineComment {
_, _ = stmtBuilder.Write(bytes.TrimRight(content[i:j], " "))
for j < n && contentStr[j] != '\n' {
j++
}
i = j
}
// no-op: generic character
}
}
switch st[len(st)-1] {
case sqlLeftParenthesis:
}
stmt := strings.TrimSpace(stmtBuilder.String())
if stmt == "" {
}
}
}
}
// hasWordAt is a simple test to check if it matches the whole word:
// it checks if the adjacent characters are not alphanumeric if they exist.
if pos+len(word) > len(s) || s[pos:pos+len(word)] != word {
return false
}
}
}
}
// hasWordAfter checks if the given word appears after position pos in s,
// separated by at least one space, and is a whole word.
after := pos
for after < len(s) && unicode.IsSpace(rune(s[after])) {
after++
}
}
return hasWordAt(s, word, after), after
}
}
return unicode.IsLetter(rune(c)) || unicode.IsDigit(rune(c))
}
// Current priority order is:
// statsd > prometheus
if c.Statsd != nil {
return newStatsdScope(logger, c)
}
if err != nil {
logger.Fatal("invalid sanitize options input on prometheus config", tag.Error(err))
return nil
}
logger = log.NewThrottledLogger(logger, func() float64 { return c.Prometheus.LoggerRPS })
}
logger,
convertPrometheusConfigToTally(&c.ClientConfig, c.Prometheus),
sanitizeOptions,
&c.ClientConfig,
)
}
return tally.NoopScope
}
func convertSanitizeOptionsToTally(config *PrometheusConfig) (tally.SanitizeOptions, error) {
config.go
if config.SanitizeOptions == nil {
}
return config.SanitizeOptions.toTally()
clientConfig *ClientConfig,
config *PrometheusConfig,
defaultObjectives := make([]prometheus.SummaryObjective, len(config.DefaultSummaryObjectives))
for i, item := range config.DefaultSummaryObjectives {
defaultObjectives[i].AllowedError = item.AllowedError
defaultObjectives[i].Percentile = item.Percentile
}
HandlerPath: config.HandlerPath,
ListenNetwork: config.ListenNetwork,
ListenAddress: config.ListenAddress,
TimerType: "histogram",
DefaultHistogramBuckets: buildTallyTimerHistogramBuckets(clientConfig, config),
DefaultSummaryObjectives: defaultObjectives,
OnError: config.OnError,
}
}
clientConfig *ClientConfig,
config *PrometheusConfig,
if len(config.DefaultHistogramBuckets) > 0 {
result := make([]prometheus.HistogramObjective, len(config.DefaultHistogramBuckets))
for i, item := range config.DefaultHistogramBuckets {
}
result := make([]prometheus.HistogramObjective, 0, len(config.DefaultHistogramBoundaries))
for _, value := range config.DefaultHistogramBoundaries {
}
result := make([]prometheus.HistogramObjective, 0, len(boundaries))
for _, boundary := range boundaries {
Upper: boundary / float64(time.Second/time.Millisecond), // convert milliseconds to seconds
})
}
}
buckets := maps.Clone(defaultPerUnitHistogramBoundaries)
// In config, when overwrite default buckets, we use [dimensionless / miliseconds / bytes] as keys.
// But in code, we use [1 / ms / By] as key (to align with otel unit definition). So we do conversion here.
if bucket, ok := clientConfig.PerUnitHistogramBoundaries[UnitNameDimensionless]; ok {
buckets[Dimensionless] = bucket
}
if bucket, ok := clientConfig.PerUnitHistogramBoundaries[UnitNameMilliseconds]; ok {
config.go
buckets[Milliseconds] = bucket
}
buckets[Bytes] = bucket
}
for idx, boundary := range buckets[Milliseconds] {
bucketInSeconds[idx] = boundary / float64(time.Second/time.Millisecond)
}
buckets[Seconds] = bucketInSeconds
clientConfig.PerUnitHistogramBoundaries = buckets
}
sanitizeOptions tally.SanitizeOptions,
clientConfig *ClientConfig,
reporter, err := config.NewReporter(
prometheus.ConfigurationOptions{
Registry: prom.NewRegistry(),
OnError: func(err error) {
logger.Warn("error in prometheus reporter", tag.Error(err))
},
},
)
logger.Fatal("error creating prometheus reporter", tag.Error(err))
}
Tags: clientConfig.Tags,
CachedReporter: reporter,
Separator: prometheus.DefaultSeparator,
SanitizeOptions: &sanitizeOptions,
Prefix: clientConfig.Prefix,
}
scope, _ := tally.NewRootScope(scopeOpts, time.Second)
return scope
}
// MetricsHandlerFromConfig is used at startup to construct a MetricsHandler
if c == nil {
return NoopMetricsHandler, nil
}
fatalOnListenerError := true
if c.Statsd != nil && c.Statsd.Framework == FrameworkOpentelemetry {
// create opentelemetry provider with just statsd
otelProvider, err := NewOpenTelemetryProviderWithStatsd(logger, c.Statsd, &c.ClientConfig)
}
// create opentelemetry provider with just prometheus
otelProvider, err := NewOpenTelemetryProviderWithPrometheus(logger, c.Prometheus, &c.ClientConfig, fatalOnListenerError)
// fallback to tally if no framework is specified
c.ClientConfig,
NewScope(logger, c),
), nil
}
tagsToFilter := make(map[string]map[string]struct{})
for key, val := range cfg.ExcludeTags {
exclusions := make(map[string]struct{})
for _, val := range val {
func (*Predicate) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
}
func (*UniversalPredicateAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*EmptyPredicateAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*AndPredicateAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*OrPredicateAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*NotPredicateAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*NamespaceIdPredicateAttributes) ProtoMessage() {}
func (x *NamespaceIdPredicateAttributes) ProtoReflect() protoreflect.Message {
predicates.pb.go
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*TaskTypePredicateAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*DestinationPredicateAttributes) ProtoMessage() {}
func (x *DestinationPredicateAttributes) ProtoReflect() protoreflect.Message {
predicates.pb.go
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*OutboundTaskGroupPredicateAttributes) ProtoMessage() {}
func (x *OutboundTaskGroupPredicateAttributes) ProtoReflect() protoreflect.Message {
predicates.pb.go
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*OutboundTaskPredicateAttributes) ProtoMessage() {}
func (x *OutboundTaskPredicateAttributes) ProtoReflect() protoreflect.Message {
predicates.pb.go
mi := &file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func init() { file_temporal_server_api_persistence_v1_predicates_proto_init() }
predicates.pb.go
func file_temporal_server_api_persistence_v1_predicates_proto_init() {
if File_temporal_server_api_persistence_v1_predicates_proto != nil {
return
}
file_temporal_server_api_persistence_v1_predicates_proto_msgTypes[0].OneofWrappers = []any{
(*Predicate_UniversalPredicateAttributes)(nil),
(*Predicate_EmptyPredicateAttributes)(nil),
(*Predicate_AndPredicateAttributes)(nil),
(*Predicate_OrPredicateAttributes)(nil),
(*Predicate_NotPredicateAttributes)(nil),
(*Predicate_NamespaceIdPredicateAttributes)(nil),
(*Predicate_TaskTypePredicateAttributes)(nil),
(*Predicate_DestinationPredicateAttributes)(nil),
(*Predicate_OutboundTaskGroupPredicateAttributes)(nil),
(*Predicate_OutboundTaskPredicateAttributes)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_predicates_proto_rawDesc), len(file_temporal_server_api_persistence_v1_predicates_proto_rawDesc)),
NumEnums: 0,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_predicates_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_predicates_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_predicates_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_predicates_proto = out.File
file_temporal_server_api_persistence_v1_predicates_proto_goTypes = nil
file_temporal_server_api_persistence_v1_predicates_proto_depIdxs = nil
}
// NewFactoryProvider creates a default implementation of FactoryProvider.
return &factoryProviderImpl{}
}
// NewFactory creates an instance of client factory that knows how to dispatch RPC calls.
logger log.Logger,
throttledLogger log.Logger,
return &rpcClientFactory{
rpcFactory: rpcFactory,
monitor: monitor,
metricsHandler: metricsHandler,
dynConfig: dc,
testHooks: testHooks,
numberOfHistoryShards: numberOfHistoryShards,
logger: logger,
throttledLogger: throttledLogger,
}
}
func (cf *rpcClientFactory) NewHistoryClientWithTimeout(timeout time.Duration) (historyservice.HistoryServiceClient, error) {
clientfactory.go
resolver, err := cf.monitor.GetResolver(primitives.HistoryService)
if err != nil {
return nil, err
}
cf.dynConfig,
resolver,
cf.logger,
cf.numberOfHistoryShards,
cf.rpcFactory,
timeout,
)
if cf.metricsHandler != nil {
client = history.NewMetricClient(client, cf.metricsHandler, cf.logger, cf.throttledLogger)
}
return client, nil
}
timeout time.Duration,
longPollTimeout time.Duration,
resolver, err := cf.monitor.GetResolver(primitives.MatchingService)
if err != nil {
return nil, err
}
clientProvider := func(clientKey string) (any, func() error, error) {
connection := cf.rpcFactory.CreateMatchingGRPCConnection(clientKey)
return matchingservice.NewMatchingServiceClient(connection), connection.Close, nil
}
timeout,
longPollTimeout,
common.NewClientCache(keyResolver, clientProvider, cf.logger),
cf.metricsHandler,
cf.logger,
matching.NewLoadBalancer(namespaceIDToName, cf.dynConfig, cf.testHooks),
dynamicconfig.MatchingSpreadRoutingBatchSize.Get(cf.dynConfig),
resolver,
dynamicconfig.MatchingConnectionCloseDelay.Get(cf.dynConfig),
)
if cf.metricsHandler != nil {
client = matching.NewMetricClient(client, cf.metricsHandler, cf.logger, cf.throttledLogger)
}
return client, nil
}
timeout time.Duration,
longPollTimeout time.Duration,
connection := cf.rpcFactory.CreateLocalFrontendGRPCConnection()
client := workflowservice.NewWorkflowServiceClient(connection)
return connection, cf.newFrontendClient(client, timeout, longPollTimeout), nil
}
func (cf *rpcClientFactory) NewRemoteAdminClientWithTimeout(
timeout time.Duration,
longPollTimeout time.Duration,
connection := cf.rpcFactory.CreateLocalFrontendGRPCConnection()
client := adminservice.NewAdminServiceClient(connection)
return cf.newAdminClient(client, timeout, longPollTimeout), nil
}
func (cf *rpcClientFactory) newAdminClient(
timeout time.Duration,
longPollTimeout time.Duration,
client = admin.NewClient(timeout, longPollTimeout, client)
if cf.metricsHandler != nil {
client = admin.NewMetricClient(client, cf.metricsHandler, cf.throttledLogger)
}
return client
}
timeout time.Duration,
longPollTimeout time.Duration,
client = frontend.NewClient(timeout, longPollTimeout, client)
if cf.metricsHandler != nil {
client = frontend.NewMetricClient(client, cf.metricsHandler, cf.throttledLogger)
}
return client
}
func newServiceKeyResolver(resolver membership.ServiceResolver) *serviceKeyResolverImpl {
clientfactory.go
return &serviceKeyResolverImpl{
resolver: resolver,
}
}
// Lookup returns the address for a node within a batch. key contains the key (including batch
// number), and index is the index within the batch. If not using batches, index should be 0.
// Note that Lookup(key) and LookupN(key, n)[0] are equal.
func (r *serviceKeyResolverImpl) Lookup(key string, index int) (string, error) {
clientfactory.go
hosts := r.resolver.LookupN(key, index+1)
if len(hosts) == 0 {
}
if index >= len(hosts) {
index %= len(hosts)
factory ExecutableFactory,
taskPostProcessor taskPostProcessorFn,
paginationFnProvider := func(r Range) collection.PaginationFn[tasks.Task] {
return func(paginationToken []byte) ([]tasks.Task, []byte, error) {
ctx, cancel := newQueueIOContext()
defer cancel()
request := &persistence.GetHistoryTasksRequest{
ShardID: shard.GetShardID(),
TaskCategory: category,
InclusiveMinTaskKey: r.InclusiveMin,
ExclusiveMaxTaskKey: r.ExclusiveMax,
BatchSize: options.BatchSize(),
NextPageToken: paginationToken,
}
resp, err := shard.GetHistoryTasks(ctx, request)
if err != nil {
return nil, nil, err
}
taskPostProcessor(resp.Tasks)
}
}
}
queueBase: newQueueBase(
shard,
category,
paginationFnProvider,
scheduler,
rescheduler,
factory,
options,
hostRateLimiter,
NoopReaderCompletionFn,
grouper,
logger,
metricsHandler,
),
notifyCh: make(chan struct{}, 1),
}
}
if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
defer p.logger.Info("", tag.LifeCycleStarted)
p.queueBase.Start()
p.shutdownWG.Add(1)
go p.processEventLoop()
p.notify()
}
if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
defer p.logger.Info("", tag.LifeCycleStopped)
close(p.shutdownCh)
if success := common.AwaitWaitGroup(&p.shutdownWG, time.Minute); !success {
p.logger.Warn("", tag.LifeCycleStopTimedout)
}
}
if len(tasks) == 0 {
return
}
}
defer p.shutdownWG.Done()
pollTimer := time.NewTimer(backoff.Jitter(
p.options.MaxPollInterval(),
p.options.MaxPollIntervalJitterCoefficient(),
))
defer pollTimer.Stop()
for {
select {
case <-p.shutdownCh:
return
}
return
p.processNewRange()
case <-pollTimer.C:
p.processPollTimer(pollTimer)
}
select {
case p.notifyCh <- struct{}{}:
}
}
metricsHandler metrics.Handler,
serializer serialization.Serializer,
return &Factory{
cfg: cfg,
clusterName: clusterName,
logger: logger,
serializer: serializer,
mainDBConn: NewRefCountedDBConn(sqlplugin.DbKindMain, &cfg, r, logger, metricsHandler),
}
}
// GetDB return a new SQL DB connection
// NewTaskStore returns a new task store
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
return newTaskPersistence(conn, f.cfg.TaskScanPartitions, f.logger, false, f.serializer)
factory.go
}
// NewFairTaskStore returns a new task store
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
return newTaskPersistence(conn, f.cfg.TaskScanPartitions, f.logger, true, f.serializer)
factory.go
}
// NewShardStore returns a new shard store
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// NewMetadataStore returns a new metadata store
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// NewClusterMetadataStore returns a new ClusterMetadata store
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// NewExecutionStore returns a new ExecutionStore
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// NewQueue returns a new queue backed by sql
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// NewQueueV2 returns a new data-access object for queues and messages.
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// NewNexusEndpointStore returns a new NexusEndpointStore
conn, err := f.mainDBConn.Get()
if err != nil {
return nil, err
}
}
// Close closes the factory
f.mainDBConn.ForceClose()
}
// NewRefCountedDBConn returns a logical mysql connection that
logger log.Logger,
metricsHandler metrics.Handler,
return DbConn{
dbKind: dbKind,
cfg: cfg,
resolver: r,
metrics: metricsHandler,
logger: logger,
}
}
// Get returns a db connection and increments a reference count.
// This method will create a new connection, if an existing connection
// does not exist
c.Lock()
defer c.Unlock()
if c.refCnt == 0 {
conn, err := NewSQLDB(c.dbKind, c.cfg, c.resolver, c.logger, c.metrics)
if err != nil {
return nil, err
}
}
return c, nil
}
// ForceClose ignores reference counts and shutsdown the underlying connection pool
c.Lock()
defer c.Unlock()
if c.DB != nil {
err := c.DB.Close()
if err != nil {
fmt.Println("failed to close database connection, may leak some connection", err)
}
}
}
// Close closes the underlying connection if the reference count becomes zero
c.Lock()
defer c.Unlock()
c.refCnt--
if c.refCnt == 0 {
return c.DB.Close()
}
}
}
*x = ClusterMetadata{}
mi := &file_temporal_server_api_persistence_v1_cluster_metadata_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ClusterMetadata) String() string {
func (*ClusterMetadata) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_cluster_metadata_proto_msgTypes[0]
if x != nil {
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
if x != nil {
return x.ClusterName
}
return ""
}
if x != nil {
return x.HistoryShardCount
}
return 0
}
if x != nil {
return x.ClusterId
}
return ""
}
}
if x != nil {
return x.ClusterAddress
}
return ""
}
if x != nil {
return x.HttpAddress
}
return ""
}
}
if x != nil {
return x.InitialFailoverVersion
}
return 0
}
}
if x != nil {
return x.IsConnectionEnabled
}
return false
}
}
if x != nil {
return x.Tags
}
return nil
}
if x != nil {
return x.IsReplicationEnabled
}
return false
}
func (*IndexSearchAttributes) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_cluster_metadata_proto_msgTypes[1]
if x != nil {
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
}
}
func init() { file_temporal_server_api_persistence_v1_cluster_metadata_proto_init() }
cluster_metadata.pb.go
func file_temporal_server_api_persistence_v1_cluster_metadata_proto_init() {
if File_temporal_server_api_persistence_v1_cluster_metadata_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_cluster_metadata_proto_rawDesc), len(file_temporal_server_api_persistence_v1_cluster_metadata_proto_rawDesc)),
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_cluster_metadata_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_cluster_metadata_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_cluster_metadata_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_cluster_metadata_proto = out.File
file_temporal_server_api_persistence_v1_cluster_metadata_proto_goTypes = nil
file_temporal_server_api_persistence_v1_cluster_metadata_proto_depIdxs = nil
}
// NewClientBean provides a collection of clients
func NewClientBean(factory Factory, clusterMetadata cluster.Metadata) (Bean, error) {
client_bean.go
historyClient, err := factory.NewHistoryClientWithTimeout(history.DefaultTimeout)
if err != nil {
return nil, err
}
frontendClients := map[string]frontendClient{}
currentClusterName := clusterMetadata.GetCurrentClusterName()
// Init local cluster client with membership info
adminClient, err := factory.NewLocalAdminClientWithTimeout(
admin.DefaultTimeout,
admin.DefaultLargeTimeout,
)
if err != nil {
return nil, err
}
frontend.DefaultTimeout,
frontend.DefaultLongPollTimeout,
)
if err != nil {
return nil, err
}
frontendClients[currentClusterName] = frontendClient{
connection: conn,
WorkflowServiceClient: client,
}
bean := &clientBeanImpl{
factory: factory,
historyClient: historyClient,
clusterMetadata: clusterMetadata,
adminClients: adminClients,
frontendClients: frontendClients,
}
bean.registerClientEviction()
return bean, nil
}
currentCluster := h.clusterMetadata.GetCurrentClusterName()
h.clusterMetadata.RegisterMetadataChangeCallback(
h,
func(oldClusterMetadata map[string]*cluster.ClusterInformation, newClusterMetadata map[string]*cluster.ClusterInformation) {
for clusterName := range newClusterMetadata {
if clusterName == currentCluster {
continue
}
h.adminClientsLock.Lock()
// Close releases the resources held by the bean's clients. See the Bean
// interface for details. It is safe to call more than once.
h.clusterMetadata.UnRegisterMetadataChangeCallback(h)
// The history and matching client wrapper chains implement Stop();
// stopping them releases their daemon goroutines and cached gRPC
// connections.
if s, ok := h.historyClient.(interface{ Stop() }); ok {
s.Stop()
}
if mc := h.matchingClient.Load(); mc != nil {
if s, ok := mc.(interface{ Stop() }); ok {
s.Stop()
}
}
}
func (h *clientBeanImpl) GetHistoryClient() historyservice.HistoryServiceClient {
client_bean.go
return h.historyClient
}
func (h *clientBeanImpl) GetMatchingClient(namespaceIDToName NamespaceIDToNameFunc) (matchingservice.MatchingServiceClient, error) {
client_bean.go
if client := h.matchingClient.Load(); client != nil {
return client.(matchingservice.MatchingServiceClient), nil
}
}
func (h *clientBeanImpl) GetFrontendClient() workflowservice.WorkflowServiceClient {
client_bean.go
return h.frontendClients[h.clusterMetadata.GetCurrentClusterName()]
}
func (h *clientBeanImpl) GetRemoteAdminClient(cluster string) (adminservice.AdminServiceClient, error) {
client_bean.go
h.adminClientsLock.RLock()
client, ok := h.adminClients[cluster]
h.adminClientsLock.RUnlock()
if ok {
return client, nil
}
clusterInfo, clusterFound := h.clusterMetadata.GetAllClusterInfo()[cluster]
}
func (h *clientBeanImpl) lazyInitMatchingClient(namespaceIDToName NamespaceIDToNameFunc) (matchingservice.MatchingServiceClient, error) {
client_bean.go
h.Lock()
defer h.Unlock()
if cached := h.matchingClient.Load(); cached != nil {
return cached.(matchingservice.MatchingServiceClient), nil
}
client, err := h.factory.NewMatchingClientWithTimeout(namespaceIDToName, matching.DefaultTimeout, matching.DefaultLongPollTimeout)
client_bean.go
if err != nil {
return nil, err
}
return client, nil
}
healthServer *health.Server,
chasmRegistry *chasm.Registry,
return &Service{
server: server,
handler: handler,
visibilityManager: visibilityMgr,
config: serviceConfig,
logger: logger,
grpcListener: grpcListener,
membershipMonitor: membershipMonitor,
metricsHandler: metricsHandler,
healthServer: healthServer,
chasmRegistry: chasmRegistry,
}
}
// Start starts the service
s.logger.Info("history starting")
metrics.RestartCount.With(s.metricsHandler).Record(1)
s.handler.Start()
historyservice.RegisterHistoryServiceServer(s.server, s.handler)
healthpb.RegisterHealthServer(s.server, s.healthServer)
s.chasmRegistry.RegisterServices(s.server)
// start as NOT_SERVING, update to SERVING after initial shards acquired
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_NOT_SERVING)
readinessCtx, readinessCancel := context.WithCancel(context.Background())
s.readinessCancel = readinessCancel
go func() {
if s.handler.controller.InitialShardsAcquired(readinessCtx) == nil {
// add a few seconds for stabilization
if util.InterruptibleSleep(readinessCtx, 5*time.Second) == nil {
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_SERVING)
}
}()
go func() {
s.logger.Info("Starting to serve on history listener")
if err := s.server.Serve(s.grpcListener); err != nil {
s.logger.Fatal("Failed to serve on history listener", tag.Error(err))
}
// As soon as we join membership, other hosts will send requests for shards that we own,
// so we should try to start this after starting the gRPC server.
if delay := s.config.StartupMembershipJoinDelay(); delay > 0 {
// In some situations, like rolling upgrades of the history service,
// pausing before joining membership can help separate the shard movement
time.Sleep(delay)
}
}()
}
// Stop stops the service
s.readinessCancel()
// remove self from membership ring and wait for traffic to drain
var err error
var waitTime time.Duration
if align := s.config.AlignMembershipChange(); align > 0 {
propagation := s.membershipMonitor.ApproximateMaxPropagationTime()
asOf := util.NextAlignedTime(time.Now().Add(propagation), align)
s.logger.Info("ShutdownHandler: Evicting self from membership ring as of", tag.Timestamp(asOf))
waitTime, err = s.membershipMonitor.EvictSelfAt(asOf)
s.logger.Info("ShutdownHandler: Evicting self from membership ring immediately")
err = s.membershipMonitor.EvictSelf()
}
if err != nil {
s.logger.Error("ShutdownHandler: Failed to evict self from membership ring", tag.Error(err))
}
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_NOT_SERVING)
service.go
s.logger.Info("ShutdownHandler: Waiting for drain")
if waitTime > 0 {
time.Sleep(
waitTime + // wait for membership change
s.config.ShardFinalizerTimeout(), // and then take this long to run a finalizer
)
time.Sleep(s.config.ShutdownDrainDuration())
}
enableCloseInboundReplicationStreamOnShutdown := s.config.EnableCloseInboundReplicationStreamOnShutdown()
service.go
// When enabled, stop handler components (including the replication stream monitor) before
// waiting for gRPC handlers to return. This signals inbound stream senders on the peer to
// stop, allowing their handler goroutines to unblock and return cleanly before GracefulStop.
// Without this, those goroutines block indefinitely and the gRPC server falls back to a
// forceful Stop(), causing unclean H2 teardowns on the peer.
// Guarded by feature flag so the ordering change can be reverted if needed.
if enableCloseInboundReplicationStreamOnShutdown {
s.logger.Info("ShutdownHandler: Initiating handler shutdown")
s.handler.Stop()
} else {
s.logger.Info("ShutdownHandler: Initiating shardController shutdown")
s.handler.controller.Stop()
// All grpc handlers should be cancelled now. Give them a little time to return.
s.logger.Info("ShutdownHandler: Drain time expired, stopping all traffic")
s.server.Stop()
})
t.Stop()
if !enableCloseInboundReplicationStreamOnShutdown {
s.handler.Stop()
}
s.logger.Info("history stopped")
}
fx.Provide(
QueueSchedulerRateLimiterProvider,
return tqm
},
queues.NewDLQWriter,
fx.Annotated{
outboundParams outboundQueueFactoryParams,
config *configs.Config,
factories := []QueueFactory{}
if _, ok := registry.GetCategoryByID(tasks.CategoryIDArchival); ok {
factories = append(factories, NewArchivalQueueFactory(archivalParams))
}
return additionalQueueFactories{
Factories: factories,
}
}
timeSource clock.TimeSource,
logger log.SnTaggedLogger,
return queues.NewPrioritySchedulerRateLimiter(
calculator.NewLoggedNamespaceCalculator(
shard.NewOwnershipAwareNamespaceQuotaCalculator(
ownershipBasedQuotaScaler,
serviceResolver,
config.TaskSchedulerNamespaceMaxQPS,
config.TaskSchedulerGlobalNamespaceMaxQPS,
),
log.With(logger, tag.ComponentTaskScheduler, tag.ScopeNamespace),
).GetQuota,
calculator.NewLoggedCalculator(
shard.NewOwnershipAwareQuotaCalculator(
ownershipBasedQuotaScaler,
serviceResolver,
config.TaskSchedulerMaxQPS,
config.TaskSchedulerGlobalMaxQPS,
),
log.With(logger, tag.ComponentTaskScheduler, tag.ScopeHost),
).GetQuota,
// TODO: reuse persistence rate limit calculator in PersistenceRateLimitingParamsProvider
shard.NewOwnershipAwareNamespaceQuotaCalculator(
ownershipBasedQuotaScaler,
serviceResolver,
config.PersistenceNamespaceMaxQPS,
config.PersistenceGlobalNamespaceMaxQPS,
).GetQuota,
shard.NewOwnershipAwareQuotaCalculator(
ownershipBasedQuotaScaler,
serviceResolver,
config.PersistenceMaxQPS,
config.PersistenceGlobalMaxQPS,
).GetQuota,
)
}
func QueueFactoryLifetimeHooks(
params QueueFactoriesLifetimeHookParams,
params.Lifecycle.Append(
fx.Hook{
OnStart: func(context.Context) error {
factory.Start()
}
return nil
},
for _, factory := range params.Factories {
factory.Stop()
}
return nil
},
},
}
if f.HostScheduler != nil {
f.HostScheduler.Start()
}
}
if f.HostScheduler != nil {
f.HostScheduler.Stop()
}
}
persistenceMaxRPS dynamicconfig.IntPropertyFn,
persistenceMaxRPSRatio float64,
// TODO: reuse persistence rate limit calculator in PersistenceRateLimitingParamsProvider
return func() float64 {
if maxPollHostRps := hostRPS(); maxPollHostRps > 0 {
return float64(maxPollHostRps)
}
// ensure queue loading won't consume all persistence tokens
// especially upon host restart when we need to perform a load
// for all shards
return float64(pMaxRPS) * persistenceMaxRPSRatio
}
// persistenceMaxQPS=0 means "unlimited" — use a high default to avoid
func newComponent(
params componentParams,
return &deleteNamespaceComponent{
atWorkerCfg: dynamicconfig.WorkerDeleteNamespaceActivityLimits.Get(params.DynamicCollection)(),
visibilityManager: params.VisibilityManager,
metadataManager: params.MetadataManager,
clusterMetadata: params.ClusterMetadata,
nexusEndpointManager: params.NexusEndpointManager,
historyClient: params.HistoryClient,
metricsHandler: params.MetricsHandler,
logger: params.Logger,
protectedNamespaces: dynamicconfig.ProtectedNamespaces.Get(params.DynamicCollection),
allowDeleteNamespaceIfNexusEndpointTarget: dynamicconfig.AllowDeleteNamespaceIfNexusEndpointTarget.Get(params.DynamicCollection),
nexusEndpointListDefaultPageSize: dynamicconfig.NexusEndpointListDefaultPageSize.Get(params.DynamicCollection),
deleteActivityRPS: dynamicconfig.DeleteNamespaceDeleteActivityRPS.Subscribe(params.DynamicCollection),
useChasmDeleteExecution: dynamicconfig.DeleteNamespaceUseChasmDeleteExecution.Get(params.DynamicCollection),
namespaceCacheRefreshInterval: dynamicconfig.NamespaceCacheRefreshInterval.Get(params.DynamicCollection),
}
}
registry.RegisterWorkflowWithOptions(DeleteNamespaceWorkflow, workflow.RegisterOptions{Name: WorkflowName})
registry.RegisterActivity(wc.deleteNamespaceLocalActivities())
registry.RegisterWorkflowWithOptions(reclaimresources.ReclaimResourcesWorkflow, workflow.RegisterOptions{Name: reclaimresources.WorkflowName})
registry.RegisterActivity(wc.reclaimResourcesLocalActivities())
registry.RegisterWorkflowWithOptions(deleteexecutions.DeleteExecutionsWorkflow, workflow.RegisterOptions{Name: deleteexecutions.WorkflowName})
registry.RegisterActivity(wc.deleteExecutionsLocalActivities())
}
func (wc *deleteNamespaceComponent) DedicatedWorkflowWorkerOptions() *workercommon.DedicatedWorkerOptions {
fx.go
// use default worker
return nil
}
registry.RegisterActivity(wc.reclaimResourcesActivities())
registry.RegisterActivity(wc.deleteExecutionsActivities())
}
func (wc *deleteNamespaceComponent) DedicatedActivityWorkerOptions() *workercommon.DedicatedWorkerOptions {
fx.go
return &workercommon.DedicatedWorkerOptions{
TaskQueue: primitives.DeleteNamespaceActivityTQ,
Options: sdkworker.Options{
BackgroundActivityContext: headers.SetCallerType(context.Background(), headers.CallerTypePreemptable),
MaxConcurrentActivityExecutionSize: wc.atWorkerCfg.MaxConcurrentActivityExecutionSize,
TaskQueueActivitiesPerSecond: wc.atWorkerCfg.TaskQueueActivitiesPerSecond,
WorkerActivitiesPerSecond: wc.atWorkerCfg.WorkerActivitiesPerSecond,
MaxConcurrentActivityTaskPollers: wc.atWorkerCfg.MaxConcurrentActivityTaskPollers,
},
}
}
return newLocalActivities(
wc.metadataManager,
wc.clusterMetadata,
wc.nexusEndpointManager,
wc.logger,
wc.protectedNamespaces,
wc.allowDeleteNamespaceIfNexusEndpointTarget,
wc.nexusEndpointListDefaultPageSize)
}
func (wc *deleteNamespaceComponent) reclaimResourcesActivities() *reclaimresources.Activities {
fx.go
return reclaimresources.NewActivities(wc.visibilityManager, wc.logger)
}
func (wc *deleteNamespaceComponent) reclaimResourcesLocalActivities() *reclaimresources.LocalActivities {
fx.go
return reclaimresources.NewLocalActivities(wc.visibilityManager, wc.metadataManager, wc.namespaceCacheRefreshInterval, wc.logger)
}
func (wc *deleteNamespaceComponent) deleteExecutionsActivities() *deleteexecutions.Activities {
fx.go
return deleteexecutions.NewActivities(
wc.visibilityManager,
wc.historyClient,
wc.deleteActivityRPS,
wc.useChasmDeleteExecution,
wc.metricsHandler,
wc.logger,
)
}
func (wc *deleteNamespaceComponent) deleteExecutionsLocalActivities() *deleteexecutions.LocalActivities {
fx.go
return deleteexecutions.NewLocalActivities(wc.visibilityManager, wc.metricsHandler, wc.logger)
}
logger log.Logger,
serializer serialization.Serializer,
return &sqlMetadataManagerV2{
SqlStore: NewSQLStore(db, logger, serializer),
activeClusterName: currentClusterName,
}, nil
}
func (m *sqlMetadataManagerV2) CreateNamespace(
ctx context.Context,
request *persistence.InternalCreateNamespaceRequest,
idBytes, err := primitives.ParseUUID(request.ID)
if err != nil {
return nil, err
}
err = m.txExecute(ctx, "CreateNamespace", func(tx sqlplugin.Tx) error {
metadata, err := lockMetadata(ctx, tx)
if err != nil {
return err
}
Name: request.Name,
ID: idBytes,
Data: request.Namespace.Data,
DataEncoding: request.Namespace.EncodingType.String(),
IsGlobal: request.IsGlobal,
NotificationVersion: metadata.NotificationVersion,
}); err != nil {
if m.DB.IsDupEntryError(err) {
return serviceerror.NewNamespaceAlreadyExistsf("name: %v", request.Name)
return err
}
tx,
metadata.NotificationVersion,
); err != nil {
return err
}
return nil
})
}
ctx context.Context,
request *persistence.GetNamespaceRequest,
idBytes, err := primitives.ParseUUID(request.ID)
if err != nil {
return nil, err
}
switch {
case request.Name != "" && request.ID != "":
return nil, serviceerror.NewInvalidArgument("GetNamespace operation failed. Both ID and Name specified in request.")
filter.Name = &request.Name
case len(request.ID) != 0:
filter.ID = &idBytes
}
if err != nil {
switch err {
case sql.ErrNoRows:
}
if err != nil {
return nil, err
}
}
func (m *sqlMetadataManagerV2) namespaceRowToGetNamespaceResponse(row *sqlplugin.NamespaceRow) (*persistence.InternalGetNamespaceResponse, error) {
metadata.go
return &persistence.InternalGetNamespaceResponse{
Namespace: persistence.NewDataBlob(row.Data, row.DataEncoding),
IsGlobal: row.IsGlobal,
NotificationVersion: row.NotificationVersion,
}, nil
}
func (m *sqlMetadataManagerV2) UpdateNamespace(
ctx context.Context,
request *persistence.InternalListNamespacesRequest,
var pageToken *primitives.UUID
if request.NextPageToken != nil {
token := primitives.UUID(request.NextPageToken)
pageToken = &token
}
GreaterThanID: pageToken,
PageSize: &request.PageSize,
})
if err != nil {
if err == sql.ErrNoRows {
return &persistence.InternalListNamespacesResponse{}, nil
}
for _, row := range rows {
if err != nil {
return nil, err
}
}
if len(rows) >= request.PageSize {
resp.NextPageToken = rows[len(rows)-1].ID
}
}
tx sqlplugin.Tx,
oldNotificationVersion int64,
result, err := tx.UpdateNamespaceMetadata(ctx, &sqlplugin.NamespaceMetadataRow{
NotificationVersion: oldNotificationVersion,
})
if err != nil {
return serviceerror.NewUnavailablef("Failed to update namespace metadata. Error: %v", err)
}
if err != nil {
return serviceerror.NewUnavailablef("Could not verify whether namespace metadata update occurred. Error: %v", err)
return serviceerror.NewUnavailablef("Failed to update namespace metadata. <>1 rows affected. Error: %v", err)
}
}
ctx context.Context,
tx sqlplugin.Tx,
row, err := tx.LockNamespaceMetadata(ctx)
if err != nil {
return nil, serviceerror.NewUnavailablef("Failed to lock namespace metadata. Error: %v", err)
}
}
fifoScheduler Scheduler[T],
logger log.Logger,
iwrrChannels := atomic.Value{}
iwrrChannels.Store(WeightedChannels[T]{})
return &InterleavedWeightedRoundRobinScheduler[T, K]{
status: common.DaemonStatusInitialized,
ts: clock.NewRealTimeSource(),
fifoScheduler: fifoScheduler,
logger: logger,
options: options,
notifyChan: make(chan struct{}, 1),
shutdownChan: make(chan struct{}),
numInflightTask: 0,
weightedChannels: make(map[K]*WeightedChannel[T]),
iwrrChannels: iwrrChannels,
}
}
func (s *InterleavedWeightedRoundRobinScheduler[T, K]) Start() {
interleaved_weighted_round_robin.go
if !atomic.CompareAndSwapInt32(
&s.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
s.shutdownWG.Add(1)
go s.eventLoop()
s.shutdownWG.Add(1)
go s.cleanupLoop()
s.logger.Info("interleaved weighted round robin task scheduler started")
}
func (s *InterleavedWeightedRoundRobinScheduler[T, K]) Stop() {
interleaved_weighted_round_robin.go
if !atomic.CompareAndSwapInt32(
&s.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
s.fifoScheduler.Stop()
s.abortTasks()
if success := common.AwaitWaitGroup(&s.shutdownWG, time.Minute); !success {
s.logger.Warn("interleaved weighted round robin task scheduler timed out on shutdown.")
}
s.logger.Info("interleaved weighted round robin task scheduler stopped")
interleaved_weighted_round_robin.go
}
}
func (s *InterleavedWeightedRoundRobinScheduler[T, K]) eventLoop() {
interleaved_weighted_round_robin.go
defer s.shutdownWG.Done()
for {
select {
case <-s.notifyChan:
s.dispatchTasksWithWeight()
return
}
}
}
func (s *InterleavedWeightedRoundRobinScheduler[T, K]) cleanupLoop() {
interleaved_weighted_round_robin.go
defer s.shutdownWG.Done()
if s.options.InactiveChannelDeletionDelay == nil {
}
ch, _ := s.ts.NewTimer(s.options.InactiveChannelDeletionDelay())
interleaved_weighted_round_robin.go
for {
select {
case <-ch:
s.doCleanup()
ch, _ = s.ts.NewTimer(s.options.InactiveChannelDeletionDelay())
return
}
}
}
func (s *InterleavedWeightedRoundRobinScheduler[T, K]) abortTasks() {
interleaved_weighted_round_robin.go
s.RLock()
defer s.RUnlock()
numTasks := int64(0)
DrainLoop:
for _, channel := range s.weightedChannels {
for {
select {
}
}
}
}
return &deadlockDetector{
logger: params.Logger,
healthServer: params.HealthServer,
metricsHandler: params.MetricsHandler.WithTags(metrics.OperationTag(metrics.DeadlockDetectorScope)),
config: config{
DumpGoroutines: dynamicconfig.DeadlockDumpGoroutines.Get(params.Collection),
FailHealthCheck: dynamicconfig.DeadlockFailHealthCheck.Get(params.Collection),
AbortProcess: dynamicconfig.DeadlockAbortProcess.Get(params.Collection),
Interval: dynamicconfig.DeadlockInterval.Get(params.Collection),
MaxWorkersPerRoot: dynamicconfig.DeadlockMaxWorkersPerRoot.Get(params.Collection),
},
roots: params.Roots,
}
}
for _, root := range dd.roots {
pool := goro.NewAdaptivePool(
clock.NewRealTimeSource(),
0,
dd.config.MaxWorkersPerRoot(),
100*time.Millisecond,
10,
)
dd.pools = append(dd.pools, pool)
loopCtx := &loopContext{
dd: dd,
root: root,
p: pool,
}
dd.loops.Go(loopCtx.run)
}
return nil
}
for _, pool := range dd.pools {
pool.Stop()
}
dd.loops.Cancel()
// don't wait for workers to exit, they may be blocked
return nil
}
}
for {
// ping blocks until it has passed all checks to a worker goroutine (using an
// unbuffered channel).
lc.ping(ctx, []pingable.Pingable{lc.root})
timer := time.NewTimer(lc.dd.config.Interval())
select {
case <-timer.C:
timer.Stop()
return ctx.Err()
}
}
}
for _, pingable := range pingables {
lc.p.Do(func() { lc.check(ctx, check) })
}
}
}
lc.dd.logger.Debug("starting ping check", tag.Name(check.Name))
startTime := time.Now().UTC()
resolved := make(chan struct{})
// Using AfterFunc is cheaper than creating another goroutine to be the waiter, since
// we expect to always cancel it. If the go runtime is so messed up that it can't
// create a goroutine, that's a bigger problem than we can handle.
t := time.AfterFunc(check.Timeout, func() {
if ctx.Err() != nil {
// deadlock detector was stopped
lc.dd.adjustCurrent(-1)
})
t.Stop()
if len(check.MetricsName) > 0 {
}
lc.dd.logger.Debug("ping check succeeded", tag.Name(check.Name))
lc.ping(ctx, newPingables)
}
BackfillerTaskHandler *BackfillerTaskHandler,
MigrateToWorkflowTaskHandler *SchedulerMigrateToWorkflowTaskHandler,
return &Library{
config: config,
handler: handler,
SchedulerIdleTaskHandler: SchedulerIdleTaskHandler,
SchedulerCallbacksTaskHandler: SchedulerCallbacksTaskHandler,
GeneratorTaskHandler: GeneratorTaskHandler,
InvokerExecuteTaskHandler: InvokerExecuteTaskHandler,
InvokerProcessBufferTaskHandler: InvokerProcessBufferTaskHandler,
BackfillerTaskHandler: BackfillerTaskHandler,
MigrateToWorkflowTaskHandler: MigrateToWorkflowTaskHandler,
}
}
return chasm.SchedulerLibraryName
}
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*Scheduler](
chasm.SchedulerComponentName,
chasm.WithBusinessIDAlias("ScheduleId"),
chasm.WithSearchAttributes(
executionStatusSearchAttribute,
scheduleNextActionTimeSearchAttribute,
scheduleIdleCloseTimeSearchAttribute,
scheduleRunningWorkflowCountSearchAttribute,
scheduleBufferedStartsCountSearchAttribute,
),
// Exposes Tweakables to scheduler components via the CHASM context
// (see tweakablesFromContext).
chasm.WithContextValues(l.config.contextValues()),
),
chasm.NewRegistrableComponent[*Generator]("generator"),
chasm.NewRegistrableComponent[*Invoker]("invoker"),
chasm.NewRegistrableComponent[*Backfiller]("backfiller"),
chasm.NewRegistrableComponent[*EventLog]("eventlog"),
}
}
return []*chasm.RegistrableTask{
chasm.NewRegistrablePureTask(
"idle",
l.SchedulerIdleTaskHandler,
),
chasm.NewRegistrableSideEffectTask(
"callbacks",
l.SchedulerCallbacksTaskHandler,
),
chasm.NewRegistrablePureTask(
"generate",
l.GeneratorTaskHandler,
),
chasm.NewRegistrableSideEffectTask(
"execute",
l.InvokerExecuteTaskHandler,
),
chasm.NewRegistrablePureTask(
"processBuffer",
l.InvokerProcessBufferTaskHandler,
),
chasm.NewRegistrablePureTask(
"backfill",
l.BackfillerTaskHandler,
),
chasm.NewRegistrableSideEffectTask(
"migrateToWorkflow",
l.MigrateToWorkflowTaskHandler,
),
}
}
server.RegisterService(&schedulerpb.SchedulerService_ServiceDesc, l.handler)
}
logAllReqErrors dynamicconfig.BoolPropertyFnWithNamespaceFilter,
requestErrorHandler ErrorHandler,
return &TelemetryInterceptor{
namespaceRegistry: namespaceRegistry,
metricsHandler: metricsHandler,
logger: logger,
workflowTags: logtags.NewWorkflowTags(tasktoken.NewSerializer(), logger),
logAllReqErrors: logAllReqErrors,
requestErrorHandler: requestErrorHandler,
}
}
// telemetryUnaryOverrideOperationTag is used to override scope used for reporting a metric.
// Ideally this method should never be used.
func telemetryUnaryOverrideOperationTag(fullName, operation string, req any) string {
telemetry.go
if strings.HasPrefix(fullName, api.WorkflowServicePrefix) {
// Current plan is to eventually split GetWorkflowExecutionHistory into two APIs,
// remove this "if" case when that is done.
if operation == metrics.FrontendGetWorkflowExecutionHistoryScope {
if request, ok := req.(*workflowservice.GetWorkflowExecutionHistoryRequest); ok {
if request.GetWaitNewEvent() {
}
}
} else if strings.HasPrefix(fullName, api.HistoryServicePrefix) {
// Special handling for Nexus operations to include service and operation in metric tag since the API is generic for all Nexus operations.
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
methodName := api.MethodName(info.FullMethod)
nsName := MustGetNamespaceName(ti.namespaceRegistry, req)
metricsHandler, logTags := ti.unaryMetricsHandlerLogTags(req, info.FullMethod, methodName, nsName)
ctx = AddTelemetryContext(ctx, metricsHandler)
metrics.ServiceRequests.With(metricsHandler).Record(1)
startTime := time.Now().UTC()
defer func() {
ti.RecordLatencyMetrics(ctx, startTime, metricsHandler)
}()
if configs.IsAPIOperation(info.FullMethod) {
1,
metrics.TaskTypeTag(""), // Added to make tags consistent with history task executor.
)
}
ti.requestErrorHandler.HandleError(req, info.FullMethod, metricsHandler, logTags, err, nsName)
// emit action metrics only after successful calls
ti.emitActionMetric(methodName, info.FullMethod, req, metricsHandler, resp)
}
}
func AddTelemetryContext(ctx context.Context, metricsHandler metrics.Handler) context.Context {
telemetry.go
return context.WithValue(ctx, metricsCtxKey, metricsHandler)
}
func (ti *TelemetryInterceptor) RecordLatencyMetrics(ctx context.Context, startTime time.Time, metricsHandler metrics.Handler) {
telemetry.go
userLatencyDuration := time.Duration(0)
if val, ok := metrics.ContextCounterGet(ctx, metrics.HistoryWorkflowExecutionCacheLatency.Name()); ok {
userLatencyDuration = time.Duration(val)
metrics.ServiceLatencyUserLatency.With(metricsHandler).Record(userLatencyDuration)
}
metrics.ServiceLatency.With(metricsHandler).Record(latency)
noUserLatency := max(0, latency-userLatencyDuration)
metrics.ServiceLatencyNoUserLatency.With(metricsHandler).Record(noUserLatency)
}
metricsHandler metrics.Handler,
result any,
if _, ok := grpcActions[methodName]; !ok || !strings.HasPrefix(fullName, api.WorkflowServicePrefix) {
// grpcActions checks that methodName is the one that we care about, and we only care about WorkflowService.
telemetry.go
return
}
switch methodName {
methodName string,
nsName namespace.Name,
overridedMethodName := telemetryUnaryOverrideOperationTag(fullMethod, methodName, req)
if nsName == "" {
return baseMetricsHandler.WithTags(metrics.OperationTag(overridedMethodName), metrics.NamespaceUnknownTag()),
[]tag.Tag{tag.Operation(overridedMethodName)}
}
return baseMetricsHandler.WithTags(metrics.OperationTag(overridedMethodName), metrics.NamespaceTag(nsName.String())),
[]tag.Tag{tag.Operation(overridedMethodName), tag.WorkflowNamespace(nsName.String())}
fullMethod string,
methodName string,
return CreateUnaryMetricsHandlerLogTags(ti.metricsHandler, req, fullMethod, methodName, nsName)
}
func (ti *TelemetryInterceptor) streamMetricsHandlerLogTags(
ctx context.Context,
logger log.Logger,
handler, ok := ctx.Value(metricsCtxKey).(metrics.Handler)
if !ok {
logger.Error("unable to get metrics scope")
return metrics.NoopMetricsHandler
}
}
args NewAdminHandlerArgs,
namespaceDLQHandler nsreplication.DLQMessageHandler,
historyHealthChecker := NewHealthChecker(
primitives.HistoryService,
args.MembershipMonitor,
args.Config.HistoryHostErrorPercentage,
args.Config.HistoryHostSelfErrorProportion,
func(ctx context.Context, hostAddress string) (*historyservice.DeepHealthCheckResponse, error) {
return args.HistoryClient.DeepHealthCheck(ctx, &historyservice.DeepHealthCheckRequest{HostAddress: hostAddress})
},
)
logger: args.Logger,
status: common.DaemonStatusInitialized,
numberOfHistoryShards: args.PersistenceConfig.NumHistoryShards,
config: args.Config,
namespaceDLQHandler: namespaceDLQHandler,
eventSerializer: args.EventSerializer,
visibilityMgr: args.visibilityMgr,
persistenceExecutionName: args.PersistenceExecutionManager.GetName(),
namespaceReplicationQueue: args.NamespaceReplicationQueue,
taskManager: args.TaskManager,
fairTaskManager: args.FairTaskManager,
clusterMetadataManager: args.ClusterMetadataManager,
persistenceMetadataManager: args.PersistenceMetadataManager,
clientFactory: args.ClientFactory,
clientBean: args.ClientBean,
historyClient: args.HistoryClient,
sdkClientFactory: args.sdkClientFactory,
membershipMonitor: args.MembershipMonitor,
hostInfoProvider: args.HostInfoProvider,
metricsHandler: args.MetricsHandler,
namespaceRegistry: args.NamespaceRegistry,
saProvider: args.SaProvider,
saManager: args.SaManager,
saMapperProvider: args.SaMapperProvider,
saValidator: searchattribute.NewValidator(
args.SaProvider,
args.SaMapperProvider,
args.Config.SearchAttributesNumberOfKeysLimit,
args.Config.SearchAttributesSizeOfValueLimit,
args.Config.SearchAttributesTotalSizeLimit,
args.visibilityMgr,
visibility.AllowListForValidation(
args.visibilityMgr.GetStoreNames(),
args.Config.VisibilityAllowList,
),
args.Config.SuppressErrorSetSystemSearchAttribute,
args.MetricsHandler,
args.Logger,
),
clusterMetadata: args.ClusterMetadata,
healthServer: args.HealthServer,
historyHealthChecker: historyHealthChecker,
taskCategoryRegistry: args.CategoryRegistry,
matchingClient: args.matchingClient,
chasmRegistry: args.ChasmRegistry,
schedulerClient: args.SchedulerClient,
}
}
// Start starts the handler
if atomic.CompareAndSwapInt32(
&adh.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
adh.healthServer.SetServingStatus(AdminServiceName, grpchealthspb.HealthCheckResponse_SERVING)
}
}
// Stop stops the handler
if atomic.CompareAndSwapInt32(
&adh.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
adh.healthServer.SetServingStatus(AdminServiceName, grpchealthspb.HealthCheckResponse_NOT_SERVING)
}
}
var PersistenceLazyLoadedServiceResolverModule = fx.Options(
return PersistenceLazyLoadedServiceResolver{
Value: &atomic.Value{},
}
}),
fx.Invoke(initPersistenceLazyLoadedServiceResolver),
)
serviceResolver membership.ServiceResolver,
lazyLoadedServiceResolver PersistenceLazyLoadedServiceResolver,
lazyLoadedServiceResolver.Store(serviceResolver)
logger.Info("Initialized service resolver for persistence rate limiting", tag.Service(serviceName))
}
func (p PersistenceLazyLoadedServiceResolver) AvailableMemberCount() int {
lazyLoadedServiceResolver PersistenceLazyLoadedServiceResolver,
logger log.Logger,
hostCalculator := calculator.NewLoggedCalculator(
calculator.ClusterAwareQuotaCalculator{
MemberCounter: lazyLoadedServiceResolver,
PerInstanceQuota: maxQps,
GlobalQuota: globalMaxQps,
},
log.With(logger, tag.ComponentPersistence, tag.ScopeHost),
)
namespaceCalculator := calculator.NewLoggedNamespaceCalculator(
calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: lazyLoadedServiceResolver,
PerInstanceQuota: namespaceMaxQps,
GlobalQuota: globalNamespaceMaxQps,
},
log.With(logger, tag.ComponentPersistence, tag.ScopeNamespace),
)
return PersistenceRateLimitingParams{
PersistenceMaxQps: func() int {
return int(hostCalculator.GetQuota())
},
PersistenceNamespaceMaxQps: func(namespace string) int {
return int(namespaceCalculator.GetQuota(namespace))
func GrpcServerOptionsProvider(
params GrpcServerOptionsParams,
grpcServerOptions, err := params.RPCFactory.GetInternodeGRPCServerOptions()
if err != nil {
params.Logger.Fatal("creating gRPC server options failed", tag.Error(err))
}
if params.TracingStatsHandler != nil {
multiStats = append(multiStats, params.TracingStatsHandler)
}
multiStats = append(multiStats, params.MetricsStatsHandler)
}
if len(multiStats) > 0 {
grpcServerOptions = append(grpcServerOptions, grpc.StatsHandler(multiStats))
}
params.TelemetryInterceptor.StreamIntercept,
interceptor.CustomErrorStreamInterceptor,
}
if len(params.AdditionalStreamInterceptors) > 0 {
streamInterceptors = append(streamInterceptors, params.AdditionalStreamInterceptors...)
}
grpcServerOptions,
grpc.ChainUnaryInterceptor(getUnaryInterceptors(params)...),
grpc.ChainStreamInterceptor(streamInterceptors...),
)
}
func getUnaryInterceptors(params GrpcServerOptionsParams) []grpc.UnaryServerInterceptor {
fx.go
interceptors := []grpc.UnaryServerInterceptor{
params.ServiceErrorInterceptor.Intercept,
metrics.NewServerMetricsContextInjectorInterceptor(),
metrics.NewServerMetricsTrailerPropagatorInterceptor(params.Logger),
params.TelemetryInterceptor.UnaryIntercept,
}
interceptors = append(interceptors, params.AdditionalInterceptors...)
if params.NamespaceRateLimitInterceptor != nil {
interceptors = append(interceptors, params.NamespaceRateLimitInterceptor.Intercept)
}
if params.ContextMetadataInterceptor != nil {
interceptors = append(interceptors, params.ContextMetadataInterceptor.Intercept)
}
}
componentType string,
opts ...RegistrableComponentOption,
rc := &RegistrableComponent{
componentType: componentType,
goType: reflect.TypeFor[C](),
}
for _, opt := range opts {
}
}
// If a registrable component is not detached by default, a component definition
// can specify its child as detached via ComponentFieldDetached() option.
return func(rc *RegistrableComponent) {
rc.detached = true
}
}
func WithBusinessIDAlias(
alias string,
return func(rc *RegistrableComponent) {
if rc.searchAttributesMapper == nil {
}
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: business ID alias %q is already defined as a search attribute", alias))
}
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: business ID alias %q is already defined as a system search attribute", alias))
}
rc.searchAttributesMapper.systemAliasToField[alias] = sadefs.WorkflowID
registrable_component.go
rc.searchAttributesMapper.fieldToAlias[sadefs.WorkflowID] = alias
rc.searchAttributesMapper.saTypeMap[sadefs.WorkflowID] = enumspb.INDEXED_VALUE_TYPE_KEYWORD
}
}
func WithSearchAttributes(
searchAttributes ...SearchAttribute,
return func(rc *RegistrableComponent) {
if len(searchAttributes) == 0 {
return
}
}
alias := sa.definition().alias
field := sa.definition().field
valueType := sa.definition().valueType
// An identity-mapped system search attribute (alias == field, e.g. TaskQueue,
// ExecutionTime) overrides that system column directly, so it is recorded only in
// overriddenSystemFields; queries resolve via the system column.
if field == alias && sadefs.IsSystem(field) {
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: system search attribute %q cannot be overridden by a CHASM component", field))
}
if _, ok := rc.searchAttributesMapper.overriddenSystemFields[field]; ok {
registrable_component.go
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: system search attribute override %q is already defined", field))
}
continue
}
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: CHASM search attribute alias %q is a CHASM system search attribute", alias))
}
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: CHASM search attribute alias %q is a reserved search attribute", alias))
}
if _, ok := rc.searchAttributesMapper.systemAliasToField[alias]; ok {
registrable_component.go
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: CHASM search attribute alias %q is already defined as a system search attribute alias", alias))
}
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: search attribute alias %q is already defined", alias))
}
//nolint:forbidigo
panic(fmt.Sprintf("registrable component validation error: search attribute field %q is already defined", field))
}
rc.searchAttributesMapper.fieldToAlias[field] = alias
rc.searchAttributesMapper.saTypeMap[field] = valueType
}
}
func WithContextValues(
keyVals map[any]any,
return func(rc *RegistrableComponent) {
if rc.contextValues == nil {
rc.contextValues = make(map[any]any, len(keyVals))
}
maps.Copy(rc.contextValues, keyVals)
}
}
func (rc *RegistrableComponent) registerToLibrary(
library namer,
if rc.library != nil {
return "", 0, fmt.Errorf("component %s is already registered in library %s", rc.componentType, rc.library.Name())
}
rc.fqn = FullyQualifiedName(rc.library.Name(), rc.componentType)
rc.componentID = GenerateTypeID(rc.fqn)
return rc.fqn, rc.componentID, nil
}
// The generated ID is used to uniquely identify components and tasks within the CHASM framework. The same FQN will
// always produce the same ID.
return farm.Fingerprint32([]byte(fqn))
}
// hasBusinessIDAlias returns true if the component has a businessID alias configured
// via WithBusinessIDAlias option.
if rc.searchAttributesMapper == nil {
return false
}
return ok
}
}
sql.RegisterPlugin(PluginName, &plugin{
queryConverter: &queryConverter{},
connPool: newConnPool(),
})
}
func (p *plugin) GetVisibilityQueryConverter() sqlplugin.VisibilityQueryConverter {
logger log.Logger,
_ metrics.Handler,
conn, err := p.connPool.Allocate(cfg, r, logger, p.createDBConnection)
if err != nil {
return nil, err
}
db.OnClose(func() { p.connPool.Close(cfg) }) // remove reference
return db, nil
}
_ resolver.ServiceResolver,
logger log.Logger,
dsn, err := buildDSN(cfg)
if err != nil {
return nil, fmt.Errorf("error building DSN: %w", err)
}
if err != nil {
return nil, err
}
// respect the user's config values when set, otherwise default to 1 for
// backward compatibility and safety.
if cfg.MaxConns > 0 {
if cfg.MaxConns > 1 && !walEnabled {
logger.Warn(
}
db.SetMaxOpenConns(cfg.MaxConns)
db.SetMaxOpenConns(1)
}
db.SetMaxIdleConns(cfg.MaxIdleConns)
}
db.SetConnMaxLifetime(cfg.MaxConnLifetime)
}
// closes. Set ConnMaxIdleTime to 0 (infinite) to prevent idle connections
// from being reaped, which would destroy the database.
}
// Maps struct names in CamelCase to snake without need for db struct tags.
switch {
// creates temporary DB overlay in order to configure database and schemas
if err := p.setupSQLiteDatabase(cfg, db, logger); err != nil {
_ = db.Close()
return nil, err
}
}
func (p *plugin) setupSQLiteDatabase(cfg *config.SQL, conn *sqlx.DB, logger log.Logger) error {
plugin.go
db := newDB(sqlplugin.DbKindUnknown, cfg.DatabaseName, conn, nil, logger)
defer func() { _ = db.Close() }()
if err != nil {
return err
}
// init tables
}
if cfg.ConnectAttributes == nil {
cfg.ConnectAttributes = make(map[string]string)
}
if err != nil {
return "", err
}
"file:%s?%v",
cfg.DatabaseName,
vals.Encode(),
)
return dsn, nil
}
parameters := url.Values{}
// sort ConnectAttributes to get a deterministic order
keys := expmaps.Keys(cfg.ConnectAttributes)
sort.Strings(keys)
for _, k := range keys {
key := strings.TrimSpace(k)
value := strings.TrimSpace(cfg.ConnectAttributes[k])
if parameters.Get(key) != "" {
return nil, fmt.Errorf("duplicate connection attr: %v:%v, %v:%v",
key,
}
parameters.Set(key, value)
continue
}
}
// set time format
return parameters, nil
}
maxNamespaceLength dynamicconfig.IntPropertyFn,
additionalAllowedMethodsDuringHandover []string,
additional := make(map[string]struct{}, len(additionalAllowedMethodsDuringHandover))
for _, m := range additionalAllowedMethodsDuringHandover {
additional[m] = struct{}{}
}
namespaceRegistry: namespaceRegistry,
tokenSerializer: tasktoken.NewSerializer(),
enableTokenNamespaceEnforcement: enableTokenNamespaceEnforcement,
additionalAllowedMethodsDuringHandover: additional,
maxNamespaceLength: maxNamespaceLength,
}
}
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
err := ni.setNamespaceIfNotPresent(req)
if err != nil {
return nil, err
}
if hasNamespace {
if err := ni.ValidateName(reqWithNamespace.GetNamespace()); err != nil {
return nil, err
}
}
}
// ValidateName validates a namespace name (currently only a max length check).
func (ni *NamespaceValidatorInterceptor) ValidateName(ns string) error {
namespace_validator.go
if len(ns) > ni.maxNamespaceLength() {
return errNamespaceTooLong
}
}
func (ni *NamespaceValidatorInterceptor) setNamespaceIfNotPresent(
req any,
switch request := req.(type) {
case NamespaceNameGetter:
if request.GetNamespace() == "" {
namespaceEntry, err := ni.extractNamespaceFromTaskToken(req)
if err != nil {
ni.setNamespace(namespaceEntry, req)
}
return nil
}
}
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
namespaceEntry, err := ni.extractNamespace(req)
if err != nil {
return nil, err
}
if err := ni.ValidateState(namespaceEntry, info.FullMethod, GetRoutingKeyFromContext(ctx).ID); err != nil {
namespace_validator.go
return nil, err
}
}
// 4. Namespace from request match namespace from task token, if check is enabled with dynamic config.
// 5. Namespace is in correct state.
func (ni *NamespaceValidatorInterceptor) ValidateState(namespaceEntry *namespace.Namespace, fullMethod string, businessID string) error {
namespace_validator.go
if err := ni.checkNamespaceState(namespaceEntry, fullMethod); err != nil {
return err
}
return ni.checkReplicationState(namespaceEntry, fullMethod, businessID)
namespace_validator.go
}
func (ni *NamespaceValidatorInterceptor) extractNamespace(req any) (*namespace.Namespace, error) {
namespace_validator.go
// Token namespace has priority over request namespace. Check it first.
tokenNamespaceEntry, tokenErr := ni.extractNamespaceFromTaskToken(req)
if tokenErr != nil {
return nil, tokenErr
}
requestNamespaceEntry, requestErr := ni.extractNamespaceFromRequest(req)
namespace_validator.go
// If namespace was extracted from token then it will be used.
if requestErr != nil && tokenNamespaceEntry == nil {
return nil, requestErr
}
err := ni.checkNamespaceMatch(requestNamespaceEntry, tokenNamespaceEntry)
namespace_validator.go
if err != nil {
return nil, err
}
// Use namespace from task token (if specified) and ignore namespace from request.
return tokenNamespaceEntry, nil
}
}
func (ni *NamespaceValidatorInterceptor) extractNamespaceFromRequest(req any) (*namespace.Namespace, error) {
namespace_validator.go
reqWithNamespace, hasNamespace := req.(NamespaceNameGetter)
if !hasNamespace {
}
namespaceName := namespace.Name(reqWithNamespace.GetNamespace())
}
func (ni *NamespaceValidatorInterceptor) extractNamespaceFromTaskToken(req any) (*namespace.Namespace, error) {
namespace_validator.go
reqWithTaskToken, hasTaskToken := req.(TaskTokenGetter)
if !hasTaskToken {
}
taskTokenBytes := reqWithTaskToken.GetTaskToken()
if len(taskTokenBytes) == 0 {
}
func (ni *NamespaceValidatorInterceptor) checkNamespaceMatch(requestNamespace *namespace.Namespace, tokenNamespace *namespace.Namespace) error {
namespace_validator.go
if tokenNamespace == nil || requestNamespace == nil || !ni.enableTokenNamespaceEnforcement() {
return nil
}
if requestNamespace.ID() != tokenNamespace.ID() {
}
func (ni *NamespaceValidatorInterceptor) checkNamespaceState(namespaceEntry *namespace.Namespace, fullMethod string) error {
namespace_validator.go
if namespaceEntry == nil {
}
allowedStates, allowedStatesPerAPIDefined := allowedNamespaceStatesPerAPI[fullMethod]
}
func (ni *NamespaceValidatorInterceptor) checkReplicationState(namespaceEntry *namespace.Namespace, fullMethod string, businessID string) error {
namespace_validator.go
if namespaceEntry == nil {
}
if namespaceEntry.ReplicationState(businessID) != enumspb.REPLICATION_STATE_HANDOVER {
return nil
logger log.Logger,
metricsHandler metrics.Handler,
hostIdentity := hostInfoProvider.HostInfo().Identity()
logger = log.With(logger, tag.ComponentShardController, tag.Address(hostIdentity))
return &ownership{
acquireCh: make(chan struct{}, 1),
config: config,
historyServiceResolver: historyServiceResolver,
hostInfoProvider: hostInfoProvider,
logger: logger,
membershipUpdateCh: make(chan *membership.ChangedEvent, 1),
metricsHandler: metricsHandler,
}
}
o.goros.Go(func(ctx context.Context) error {
o.eventLoop(ctx)
return nil
})
o.acquireLoop(ctx, controller)
return nil
})
shardControllerMembershipUpdateListenerName,
o.membershipUpdateCh,
); err != nil {
o.logger.Fatal("Error adding listener", tag.Error(err))
}
}
acquireTicker := time.NewTicker(o.config.AcquireShardInterval())
defer acquireTicker.Stop()
for {
select {
return
case <-acquireTicker.C:
o.scheduleAcquire()
metrics.MembershipChangedCounter.With(o.metricsHandler).Record(1)
o.logger.Info("", tag.ValueRingMembershipChangedEvent,
tag.NumberProcessed(len(changedEvent.HostsAdded)),
tag.NumberDeleted(len(changedEvent.HostsRemoved)),
tag.NumberChanged(len(changedEvent.HostsChanged)),
)
o.scheduleAcquire()
}
}
}
select {
case o.acquireCh <- struct{}{}:
default:
}
}
func (o *ownership) acquireLoop(ctx context.Context, controller *ControllerImpl) {
ownership.go
for {
select {
return
controller.acquireShards(ctx)
}
}
}
if err := o.historyServiceResolver.RemoveListener(
shardControllerMembershipUpdateListenerName,
); err != nil {
o.logger.Error("Error removing membership update listener", tag.Error(err), tag.OperationFailed)
}
o.goros.Wait()
}
// controller. If membership lists another host as the owner, it returns a
// ShardOwnershipLost error with the correct owner.
ownerInfo, err := o.historyServiceResolver.Lookup(convert.Int32ToString(shardID))
if err != nil {
}
if ownerInfo.Identity() != hostInfo.Identity() {
return serviceerrors.NewShardOwnershipLost(ownerInfo.Identity(), hostInfo.GetAddress())
}
}
config *Config,
namespaceRegistry namespace.Registry,
return &componentOnlyLibrary{
config: config,
namespaceRegistry: namespaceRegistry,
}
}
return libraryName
}
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*Activity](
componentName,
chasm.WithSearchAttributes(
TypeSearchAttribute,
StatusSearchAttribute,
chasm.SearchAttributeTaskQueue,
chasm.SearchAttributeExecutionTime,
),
chasm.WithBusinessIDAlias("ActivityId"),
chasm.WithContextValues(map[any]any{
ctxKeyActivityContext: &activityContext{
config: l.config,
namespaceRegistry: l.namespaceRegistry,
},
}),
),
}
}
// NewNilLibrary creates a Library with all nil handlers. Useful for
config *Config,
namespaceRegistry namespace.Registry,
return &library{
componentOnlyLibrary: *newComponentOnlyLibrary(config, namespaceRegistry),
handler: handler,
activityDispatchTaskHandler: activityDispatchTaskHandler,
scheduleToStartTimeoutTaskHandler: scheduleToStartTimeoutTaskHandler,
scheduleToCloseTimeoutTaskHandler: scheduleToCloseTimeoutTaskHandler,
startToCloseTimeoutTaskHandler: startToCloseTimeoutTaskHandler,
heartbeatTimeoutTaskHandler: heartbeatTimeoutTaskHandler,
}
}
server.RegisterService(&activitypb.ActivityService_ServiceDesc, l.handler)
}
return []*chasm.RegistrableTask{
chasm.NewRegistrableSideEffectTask(
"dispatch",
l.activityDispatchTaskHandler,
),
chasm.NewRegistrablePureTask(
"scheduleToStartTimer",
l.scheduleToStartTimeoutTaskHandler,
),
chasm.NewRegistrablePureTask(
"scheduleToCloseTimer",
l.scheduleToCloseTimeoutTaskHandler,
),
chasm.NewRegistrablePureTask(
"startToCloseTimer",
l.startToCloseTimeoutTaskHandler,
),
chasm.NewRegistrablePureTask(
"heartbeatTimer",
l.heartbeatTimeoutTaskHandler,
),
}
}
logger log.Logger,
instanceID string,
if len(instanceID) > 0 {
handler = handler.WithTags(StringTag(instance, instanceID))
}
runtime.ReadMemStats(&memstats)
return &RuntimeMetricsReporter{
handler: handler,
reportInterval: reportInterval,
logger: logger,
lastNumGC: memstats.NumGC,
quit: make(chan struct{}),
buildTime: build.InfoData.GitTime,
buildInfoHandler: handler.WithTags(
StringTag(gitRevisionTag, build.InfoData.GitRevision),
StringTag(buildDateTag, build.InfoData.GitTime.Format(time.RFC3339)),
StringTag(buildPlatformTag, build.InfoData.GoArch),
StringTag(goVersionTag, build.InfoData.GoVersion),
StringTag(buildVersionTag, headers.ServerVersion),
),
}
}
// report Sends runtime metrics to the local metrics collector.
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
NumGoRoutinesGauge.With(r.handler).Record(float64(runtime.NumGoroutine()))
GoMaxProcsGauge.With(r.handler).Record(float64(runtime.GOMAXPROCS(0)))
MemoryAllocatedGauge.With(r.handler).Record(float64(memStats.Alloc))
MemoryHeapGauge.With(r.handler).Record(float64(memStats.HeapAlloc))
MemoryHeapObjectsGauge.With(r.handler).Record(float64(memStats.HeapObjects))
MemoryHeapIdleGauge.With(r.handler).Record(float64(memStats.HeapIdle))
MemoryHeapInuseGauge.With(r.handler).Record(float64(memStats.HeapInuse))
MemoryHeapReleasedGauge.With(r.handler).Record(float64(memStats.HeapReleased))
MemoryStackGauge.With(r.handler).Record(float64(memStats.StackInuse))
MemoryMallocsGauge.With(r.handler).Record(float64(memStats.Mallocs))
MemoryFreesGauge.With(r.handler).Record(float64(memStats.Frees))
NumGCGauge.With(r.handler).Record(float64(memStats.NumGC))
GcPauseNsTotal.With(r.handler).Record(float64(memStats.PauseTotalNs))
// memStats.NumGC is a perpetually incrementing counter (unless it wraps at 2^32)
num := memStats.NumGC
lastNum := atomic.SwapUint32(&r.lastNumGC, num) // reset for the next iteration
if delta := num - lastNum; delta > 0 {
NumGCCounter.With(r.handler).Record(int64(delta))
if delta > 255 {
// too many GCs happened, the timestamps buffer got wrapped around. Report only the last 256
lastNum = num - 256
}
pause := memStats.PauseNs[i%256]
GcPauseMsTimer.With(r.handler).Record(time.Duration(pause))
}
}
// report build info
r.buildInfoHandler.Gauge(buildAgeMetricName).Record(float64(time.Since(r.buildTime)))
}
// Start Starts the reporter thread that periodically emits metrics.
if !atomic.CompareAndSwapInt32(&r.started, 0, 1) {
return
}
go func() {
ticker := time.NewTicker(r.reportInterval)
for {
select {
case <-ticker.C:
r.report()
ticker.Stop()
return
}
}
}()
}
// Stop Stops reporting of runtime metrics. The reporter cannot be started again after it's been stopped.
close(r.quit)
r.logger.Info("RuntimeMetricsReporter stopped")
}
//
// Note: this function may receive breaking changes or be removed in the future.
statements, err := p.LoadAndSplitQueryFromReaders([]io.Reader{bytes.NewBuffer(executionSchema)})
if err != nil {
return fmt.Errorf("error loading execution schema: %w", err)
}
if err = db.Exec(stmt); err != nil {
return fmt.Errorf("error executing statement %q: %w", stmt, err)
}
}
statements, err = p.LoadAndSplitQueryFromReaders([]io.Reader{bytes.NewBuffer(visibilitySchema)})
setup.go
if err != nil {
return fmt.Errorf("error loading visibility schema: %w", err)
}
if err = db.Exec(stmt); err != nil {
return fmt.Errorf("error executing statement %q: %w", stmt, err)
}
}
}
//
// Note: this function may receive breaking changes or be removed in the future.
db, err := sql.NewSQLDB(sqlplugin.DbKindUnknown, cfg, resolver.NewNoopResolver(), log.NewNoopLogger(), metrics.NoopMetricsHandler)
if err != nil {
return fmt.Errorf("unable to create SQLite admin DB: %w", err)
}
if err := createNamespaceIfNotExists(db, ns); err != nil {
return fmt.Errorf("error creating namespace %q: %w", ns.Detail.Info.Name, err)
}
}
}
global bool,
customSearchAttributes map[string]enumspb.IndexedValueType,
dbCustomSearchAttributes := sadefs.GetDBIndexSearchAttributes(nil).CustomSearchAttributes
fieldToAliasMap := map[string]string{}
for saName, saType := range customSearchAttributes {
var targetFieldName string
var cntUsed int
}
Info: &persistencespb.NamespaceInfo{
Id: primitives.NewUUID().String(),
State: enumspb.NAMESPACE_STATE_REGISTERED,
Name: namespace,
},
Config: &persistencespb.NamespaceConfig{
Retention: timestamp.DurationFromHours(24),
HistoryArchivalState: enumspb.ARCHIVAL_STATE_DISABLED,
VisibilityArchivalState: enumspb.ARCHIVAL_STATE_DISABLED,
CustomSearchAttributeAliases: fieldToAliasMap,
},
ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
ActiveClusterName: activeClusterName,
Clusters: []string{activeClusterName},
},
FailoverVersion: common.EmptyVersion,
FailoverNotificationVersion: -1,
}
return &NamespaceConfig{
Detail: &detail,
IsGlobal: global,
}, nil
}
var (
name = namespace.Detail.GetInfo().GetName()
id = primitives.MustParseUUID(namespace.Detail.GetInfo().GetId())
)
// Return early if namespace already exists
rows, err := db.SelectFromNamespace(context.Background(), sqlplugin.NamespaceFilter{
Name: &name,
})
if err == nil && len(rows) > 0 {
return nil
}
if err != nil {
return err
}
ID: id,
Name: name,
Data: blob.GetData(),
DataEncoding: blob.GetEncodingType().String(),
IsGlobal: namespace.IsGlobal,
NotificationVersion: 0,
}); err != nil {
return err
}
}
currentClusterName string,
logger log.Logger,
return &clusterMetadataManagerImpl{
serializer: serializer,
persistence: persistence,
currentClusterName: currentClusterName,
logger: logger,
}
}
func (m *clusterMetadataManagerImpl) GetName() string {
}
m.persistence.Close()
}
func (m *clusterMetadataManagerImpl) GetClusterMembers(
ctx context.Context,
request *GetClusterMembersRequest,
return m.persistence.GetClusterMembers(ctx, request)
}
func (m *clusterMetadataManagerImpl) UpsertClusterMembership(
ctx context.Context,
request *UpsertClusterMembershipRequest,
if request.RecordExpiry.Seconds() < 1 {
return ErrInvalidMembershipExpiry
}
return ErrIncompleteMembershipUpsert
}
return ErrIncompleteMembershipUpsert
}
return ErrIncompleteMembershipUpsert
}
return ErrIncompleteMembershipUpsert
}
}
ctx context.Context,
request *PruneClusterMembershipRequest,
return m.persistence.PruneClusterMembership(ctx, request)
}
func (m *clusterMetadataManagerImpl) ListClusterMetadata(
ctx context.Context,
request *ListClusterMetadataRequest,
resp, err := m.persistence.ListClusterMetadata(ctx, &InternalListClusterMetadataRequest{
PageSize: request.PageSize,
NextPageToken: request.NextPageToken,
})
if err != nil {
return nil, err
}
clusterMetadata := make([]*GetClusterMetadataResponse, 0, len(resp.ClusterMetadata))
cluster_metadata_store.go
for _, cm := range resp.ClusterMetadata {
res, err := m.convertInternalGetClusterMetadataResponse(cm)
if err != nil {
return nil, err
}
}
return &ListClusterMetadataResponse{ClusterMetadata: clusterMetadata, NextPageToken: resp.NextPageToken}, nil
cluster_metadata_store.go
}
func (m *clusterMetadataManagerImpl) GetCurrentClusterMetadata(
ctx context.Context,
resp, err := m.persistence.GetClusterMetadata(ctx, &InternalGetClusterMetadataRequest{ClusterName: m.currentClusterName})
if err != nil {
return nil, err
}
mcm, err := m.serializer.DeserializeClusterMetadata(resp.ClusterMetadata)
cluster_metadata_store.go
if err != nil {
return nil, err
}
return &GetClusterMetadataResponse{ClusterMetadata: mcm, Version: resp.Version}, nil
cluster_metadata_store.go
}
ctx context.Context,
request *GetClusterMetadataRequest,
resp, err := m.persistence.GetClusterMetadata(ctx, &InternalGetClusterMetadataRequest{ClusterName: request.ClusterName})
if err != nil {
return nil, err
}
mcm, err := m.serializer.DeserializeClusterMetadata(resp.ClusterMetadata)
ctx context.Context,
request *SaveClusterMetadataRequest,
mcm, err := m.serializer.SerializeClusterMetadata(request.ClusterMetadata)
if err != nil {
return false, err
}
oldClusterMetadata, err := m.GetClusterMetadata(ctx, &GetClusterMetadataRequest{ClusterName: request.GetClusterName()})
cluster_metadata_store.go
if _, isNotFound := err.(*serviceerror.NotFound); isNotFound {
return m.persistence.SaveClusterMetadata(ctx, &InternalSaveClusterMetadataRequest{
ClusterName: request.ClusterName,
ClusterMetadata: mcm,
Version: request.Version,
})
}
if err != nil {
return false, err
func (m *clusterMetadataManagerImpl) convertInternalGetClusterMetadataResponse(
resp *InternalGetClusterMetadataResponse,
mcm, err := m.serializer.DeserializeClusterMetadata(resp.ClusterMetadata)
if err != nil {
return nil, err
}
ClusterMetadata: mcm,
Version: resp.Version,
}, nil
}
timeSource clock.TimeSource,
options *MonitorOptions,
return &monitorImpl{
readerStats: make(map[int64]readerStats),
sliceStats: make(map[Slice]sliceStats),
categoryType: categoryType,
timeSource: timeSource,
options: options,
pendingAlerts: make(map[AlertType]struct{}),
silencedAlerts: make(map[AlertType]time.Time),
alertCh: make(chan *Alert, alertChSize),
shutdownCh: make(chan struct{}),
}
}
m.Lock()
defer m.Unlock()
return m.totalPendingTaskCount
}
func (m *monitorImpl) GetSlicePendingTaskCount(slice Slice) int {
}
m.Lock()
defer m.Unlock()
stats := m.sliceStats[slice]
m.totalPendingTaskCount = m.totalPendingTaskCount - stats.pendingTaskCount + count
stats.pendingTaskCount = count
m.sliceStats[slice] = stats
criticalTotalTasks := m.options.PendingTasksCriticalCount()
if criticalTotalTasks > 0 && m.totalPendingTaskCount > criticalTotalTasks {
m.sendAlertLocked(&Alert{
AlertType: AlertTypeQueuePendingTaskCount,
}
m.Lock()
defer m.Unlock()
stats := m.readerStats[readerID]
m.totalSliceCount = m.totalSliceCount - stats.sliceCount + count
stats.sliceCount = count
m.readerStats[readerID] = stats
criticalSliceCount := m.options.SliceCountCriticalThreshold()
if criticalSliceCount > 0 && m.totalSliceCount > criticalSliceCount {
m.sendAlertLocked(&Alert{
AlertType: AlertTypeSliceCount,
}
m.Lock()
defer m.Unlock()
stats, ok := m.readerStats[readerID]
if !ok {
return
}
delete(m.readerStats, readerID)
}
}
return m.alertCh
}
m.Lock()
defer m.Unlock()
close(m.shutdownCh)
for {
select {
case <-m.alertCh:
// drain alertCh
close(m.alertCh)
return
}
}
}
return &componentOnlyLibrary{
metricTagConfig: MetricTagConfiguration.Get(dc),
}
}
return libraryName
}
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*Operation](
componentName,
chasm.WithSearchAttributes(
EndpointSearchAttribute,
ServiceSearchAttribute,
OperationSearchAttribute,
RequestIDSearchAttribute,
StatusSearchAttribute,
),
chasm.WithBusinessIDAlias("OperationId"),
chasm.WithContextValues(map[any]any{
OperationContextKey: &OperationContext{
MetricTagConfig: l.metricTagConfig,
},
}),
),
chasm.NewRegistrableComponent[*Cancellation]("cancellation"),
}
}
type Library struct {
cancellationBackoffTaskHandler *cancellationBackoffTaskHandler,
dc *dynamicconfig.Collection,
return &Library{
componentOnlyLibrary: *newComponentOnlyLibrary(dc),
handler: handler,
operationBackoffTaskHandler: operationBackoffTaskHandler,
operationInvocationTaskHandler: operationInvocationTaskHandler,
operationScheduleToCloseTimeoutTaskHandler: operationScheduleToCloseTimeoutTaskHandler,
operationScheduleToStartTimeoutTaskHandler: operationScheduleToStartTimeoutTaskHandler,
operationStartToCloseTimeoutTaskHandler: operationStartToCloseTimeoutTaskHandler,
cancellationInvocationTaskHandler: cancellationInvocationTaskHandler,
cancellationBackoffTaskHandler: cancellationBackoffTaskHandler,
}
}
return []*chasm.RegistrableTask{
chasm.NewRegistrableSideEffectTask(
"invocation",
l.operationInvocationTaskHandler,
chasm.WithTaskGroup(TaskGroupName),
),
chasm.NewRegistrablePureTask("invocationBackoff", l.operationBackoffTaskHandler),
chasm.NewRegistrablePureTask("scheduleToStartTimeout", l.operationScheduleToStartTimeoutTaskHandler),
chasm.NewRegistrablePureTask("startToCloseTimeout", l.operationStartToCloseTimeoutTaskHandler),
chasm.NewRegistrablePureTask("scheduleToCloseTimeout", l.operationScheduleToCloseTimeoutTaskHandler),
chasm.NewRegistrableSideEffectTask(
"cancellation",
l.cancellationInvocationTaskHandler,
chasm.WithTaskGroup(TaskGroupName),
),
chasm.NewRegistrablePureTask("cancellationBackoff", l.cancellationBackoffTaskHandler),
}
}
server.RegisterService(&nexusoperationpb.NexusOperationService_ServiceDesc, l.handler)
}
testHooks testhooks.TestHooks,
dlqWriter DLQWriter,
historyFetcher := eventhandler.NewHistoryPaginatedFetcher(shardContext.GetNamespaceRegistry(), clientBean, eventSerializer, shardContext.GetLogger())
return &taskProcessorManagerImpl{
config: config,
deleteMgr: workflowDeleteManager,
engine: engine,
eventSerializer: eventSerializer,
shard: shardContext,
status: common.DaemonStatusInitialized,
replicationTaskFetcherFactory: replicationTaskFetcherFactory,
workflowCache: workflowCache,
removeHistoryFetcher: historyFetcher,
logger: shardContext.GetLogger(),
metricsHandler: shardContext.GetMetricsHandler(),
testHooks: testHooks,
dlqWriter: dlqWriter,
enableFetcher: !config.EnableReplicationStream(),
taskProcessors: make(map[string][]TaskProcessor),
taskExecutorProvider: taskExecutorProvider,
taskPollerManager: newPollerManager(shardContext.GetShardID(), shardContext.GetClusterMetadata()),
minTxAckedTaskID: persistence.EmptyQueueMessageID,
shutdownChan: make(chan struct{}),
}
}
if !atomic.CompareAndSwapInt32(
&r.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
// Listen to cluster metadata and dynamically update replication processor for remote clusters.
r.listenToClusterMetadataChange()
}
go r.checkReplicationDLQEmptyLoop()
}
if !atomic.CompareAndSwapInt32(
&r.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
if r.enableFetcher {
r.shard.GetClusterMetadata().UnRegisterMetadataChangeCallback(r)
}
for _, taskProcessors := range r.taskProcessors {
for _, processor := range taskProcessors {
processor.Stop()
}
}
}
}
shardID := r.shard.GetShardID()
cleanupTimer := time.NewTimer(backoff.Jitter(
r.config.ReplicationTaskProcessorCleanupInterval(shardID),
r.config.ReplicationTaskProcessorCleanupJitterCoefficient(shardID),
))
defer cleanupTimer.Stop()
for {
select {
case <-cleanupTimer.C:
if err := r.cleanupReplicationTasks(); err != nil {
r.config.ReplicationTaskProcessorCleanupJitterCoefficient(shardID),
))
return
}
}
}
for {
timer := time.NewTimer(backoff.FullJitter(dlqSizeCheckInterval))
select {
case <-timer.C:
if r.config.ReplicationEnableDLQMetrics() {
r.checkReplicationDLQSize()
}
timer.Stop()
return
}
}
}
*x = NamespaceDetail{}
mi := &file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NamespaceDetail) String() string {
func (*NamespaceDetail) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
if x != nil {
return x.Info
}
return nil
}
func (*NamespaceInfo) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
if x != nil {
return x.Id
}
return ""
}
}
if x != nil {
return x.Name
}
return ""
}
func (*NamespaceConfig) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*NamespaceReplicationConfig) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*FailoverStatus) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func init() { file_temporal_server_api_persistence_v1_namespaces_proto_init() }
namespaces.pb.go
func file_temporal_server_api_persistence_v1_namespaces_proto_init() {
if File_temporal_server_api_persistence_v1_namespaces_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_namespaces_proto_rawDesc), len(file_temporal_server_api_persistence_v1_namespaces_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_namespaces_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_namespaces_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_namespaces_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_namespaces_proto = out.File
file_temporal_server_api_persistence_v1_namespaces_proto_goTypes = nil
file_temporal_server_api_persistence_v1_namespaces_proto_depIdxs = nil
}
requestPriorityFn RequestPriorityFn,
prioritiesOrdered []int,
rateLimiters := make(map[int]RequestRateLimiter)
for _, priority := range prioritiesOrdered {
if priority == OperatorPriority {
rateLimiters[priority] = NewRequestRateLimiterAdapter(
NewDynamicRateLimiter(
NewOperatorRateBurst(rateBurstFn, operatorRPSRatio),
defaultRefreshInterval,
),
)
} else {
rateLimiters[priority] = NewRequestRateLimiterAdapter(
NewDynamicRateLimiter(
rateBurstFn,
defaultRefreshInterval,
),
)
}
}
}
requestPriorityFn RequestPriorityFn,
priorityToRateLimiters map[int]RequestRateLimiter,
priorities := make([]int, 0, len(priorityToRateLimiters))
for priority := range priorityToRateLimiters {
priorities = append(priorities, priority)
}
slices.Sort(priorities)
priorityToIndex := make(map[int]int, len(priorityToRateLimiters))
rateLimiters := make([]RequestRateLimiter, 0, len(priorityToRateLimiters))
for index, priority := range priorities {
priorityToIndex[priority] = index
rateLimiters = append(rateLimiters, priorityToRateLimiters[priority])
}
requestPriorityFn: requestPriorityFn,
priorityToRateLimiters: priorityToRateLimiters,
priorityToIndex: priorityToIndex,
rateLimiters: rateLimiters,
}
}
now time.Time,
request Request,
decidingRateLimiter, consumeRateLimiters := p.getRateLimiters(request)
allow := decidingRateLimiter.Allow(now, request)
if !allow {
return false
}
}
}
now time.Time,
request Request,
decidingRateLimiter, consumeRateLimiters := p.getRateLimiters(request)
decidingReservation := decidingRateLimiter.Reserve(now, request)
if !decidingReservation.OK() {
return decidingReservation
}
otherReservations := make([]Reservation, len(consumeRateLimiters))
priority_rate_limiter_impl.go
for index, limiter := range consumeRateLimiters {
}
return NewPriorityReservation(decidingReservation, otherReservations)
priority_rate_limiter_impl.go
}
func (p *PriorityRateLimiterImpl) getRateLimiters(
request Request,
priority := p.requestPriorityFn(request)
if _, ok := p.priorityToRateLimiters[priority]; !ok {
panic("Request to priority & priority to rate limiter does not match")
}
return p.rateLimiters[rateLimiterIndex], p.rateLimiters[rateLimiterIndex+1:]
}
func NewMemoryScheduledQueueFactory(
params memoryScheduledQueueFactoryParams,
logger := log.With(params.Logger, tag.ComponentMemoryScheduledQueue)
metricsHandler := params.MetricsHandler.WithTags(metrics.OperationTag(metrics.OperationMemoryScheduledQueueProcessorScope))
hostScheduler := ctasks.NewFIFOScheduler[ctasks.Task](
&ctasks.FIFOSchedulerOptions{
QueueSize: 0, // Don't buffer tasks in scheduler. If all workers are busy memoryScheduledQueue reschedules tasks into itself.
WorkerCount: params.Config.MemoryTimerProcessorSchedulerWorkerCount,
},
logger,
)
return &memoryScheduledQueueFactory{
scheduler: hostScheduler,
priorityAssigner: queues.NewPriorityAssigner(
params.NamespaceRegistry,
params.ClusterMetadata.GetCurrentClusterName(),
),
namespaceRegistry: params.NamespaceRegistry,
clusterMetadata: params.ClusterMetadata,
workflowCache: params.WorkflowCache,
timeSource: params.TimeSource,
chasmRegistry: params.ChasmRegistry,
metricsHandler: metricsHandler,
tracer: params.TracerProvider.Tracer(telemetry.ComponentQueueMemory),
logger: logger,
executorWrapper: params.ExecutorWrapper,
}
}
f.scheduler.Start()
}
f.scheduler.Stop()
}
func (f *memoryScheduledQueueFactory) CreateQueue(
shardCtx historyi.ShardContext,
// Reuse TimerQueueActiveTaskExecutor only to executeWorkflowTaskTimeoutTask.
// Unused dependencies are nil.
speculativeWorkflowTaskTimeoutExecutor := newTimerQueueActiveTaskExecutor(
shardCtx,
f.workflowCache,
nil,
f.logger,
f.metricsHandler,
shardCtx.GetConfig(),
nil,
nil,
)
if f.executorWrapper != nil {
speculativeWorkflowTaskTimeoutExecutor = f.executorWrapper.Wrap(speculativeWorkflowTaskTimeoutExecutor)
}
f.scheduler,
f.priorityAssigner,
speculativeWorkflowTaskTimeoutExecutor,
f.namespaceRegistry,
f.clusterMetadata,
f.timeSource,
f.chasmRegistry,
f.metricsHandler,
f.tracer,
f.logger,
)
}
ctx context.Context,
row *sqlplugin.NamespaceRow,
return mdb.conn.ExecContext(ctx,
createNamespaceQuery,
partitionID,
row.ID,
row.Name,
row.IsGlobal,
row.Data,
row.DataEncoding,
row.NotificationVersion,
)
}
// UpdateNamespace updates a single row in namespaces table
ctx context.Context,
filter sqlplugin.NamespaceFilter,
switch {
if filter.ID != nil && filter.Name != nil {
return nil, serviceerror.NewInternal("only ID or name filter can be specified for selection")
}
return mdb.selectAllFromNamespace(ctx, filter)
default:
return nil, errMissingArgs
ctx context.Context,
filter sqlplugin.NamespaceFilter,
var err error
var row sqlplugin.NamespaceRow
switch {
case filter.ID != nil:
err = mdb.conn.GetContext(ctx,
*filter.ID,
)
err = mdb.conn.GetContext(ctx,
&row,
getNamespaceByNameQuery,
partitionID,
*filter.Name,
)
}
}
}
ctx context.Context,
filter sqlplugin.NamespaceFilter,
var err error
var rows []sqlplugin.NamespaceRow
switch {
case filter.GreaterThanID != nil:
err = mdb.conn.SelectContext(ctx,
*filter.PageSize,
)
err = mdb.conn.SelectContext(ctx,
&rows,
listNamespacesQuery,
partitionID,
filter.PageSize,
)
}
}
func (mdb *db) LockNamespaceMetadata(
ctx context.Context,
var row sqlplugin.NamespaceMetadataRow
err := mdb.conn.GetContext(ctx,
&row.NotificationVersion,
lockNamespaceMetadataQuery,
)
if err != nil {
return nil, err
}
}
ctx context.Context,
row *sqlplugin.NamespaceMetadataRow,
return mdb.conn.ExecContext(ctx,
updateNamespaceMetadataQuery,
row.NotificationVersion+1,
row.NotificationVersion,
)
}
healthServer *health.Server,
visibilityManager manager.VisibilityManager,
return &Service{
config: serviceConfig,
server: server,
handler: handler,
logger: logger,
membershipMonitor: membershipMonitor,
grpcListener: grpcListener,
runtimeMetricsReporter: runtimeMetricsReporter,
metricsHandler: metricsHandler,
healthServer: healthServer,
visibilityManager: visibilityManager,
}
}
// Start starts the service
s.logger.Info("matching starting")
// must start base service first
metrics.RestartCount.With(s.metricsHandler).Record(1)
s.handler.Start()
matchingservice.RegisterMatchingServiceServer(s.server, s.handler)
healthpb.RegisterHealthServer(s.server, s.healthServer)
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_SERVING)
reflection.Register(s.server)
go func() {
s.logger.Info("Starting to serve on matching listener")
if err := s.server.Serve(s.grpcListener); err != nil {
s.logger.Fatal("Failed to serve on matching listener", tag.Error(err))
}
}()
}
// Stop stops the service
// remove self from membership ring and wait for traffic to drain
var err error
var waitTime time.Duration
if align := s.config.AlignMembershipChange(); align > 0 {
propagation := s.membershipMonitor.ApproximateMaxPropagationTime()
asOf := util.NextAlignedTime(time.Now().Add(propagation), align)
s.logger.Info("ShutdownHandler: Evicting self from membership ring as of", tag.Timestamp(asOf))
waitTime, err = s.membershipMonitor.EvictSelfAt(asOf)
s.logger.Info("ShutdownHandler: Evicting self from membership ring immediately")
err = s.membershipMonitor.EvictSelf()
}
if err != nil {
s.logger.Error("ShutdownHandler: Failed to evict self from membership ring", tag.Error(err))
}
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_NOT_SERVING)
service.go
s.logger.Info("ShutdownHandler: Waiting for others to discover I am unhealthy")
time.Sleep(max(s.config.ShutdownDrainDuration(), waitTime))
// At this point we should not get any new rpcs since we removed ourself from the ring.
// Additionally, the engine will notice the membership change and stop all task queues
// after a delay. However, we can do it immediately by stopping the handler (which stops
// the engine which stops all task queues).
s.handler.Stop()
// All grpc handlers should be cancelled now. Give them a little time to return.
t := time.AfterFunc(2*time.Second, func() {
s.logger.Info("ShutdownHandler: Drain time expired, stopping all traffic")
s.server.Stop()
})
t.Stop()
s.visibilityManager.Close()
s.logger.Info("matching stopped")
}
)
component := &replicationWorkerComponent{
initParams: params,
}
return fxResult{
Component: component,
}
}
registry.RegisterWorkflowWithOptions(CatchupWorkflow, workflow.RegisterOptions{Name: catchupWorkflowName})
registry.RegisterWorkflowWithOptions(ForceReplicationWorkflow, workflow.RegisterOptions{Name: forceReplicationWorkflowName})
registry.RegisterWorkflowWithOptions(ForceReplicationWorkflowV2, workflow.RegisterOptions{Name: forceReplicationWorkflowV2Name})
registry.RegisterWorkflowWithOptions(NamespaceHandoverWorkflow, workflow.RegisterOptions{Name: namespaceHandoverWorkflowName})
registry.RegisterWorkflowWithOptions(NamespaceHandoverWorkflowV2, workflow.RegisterOptions{Name: namespaceHandoverWorkflowV2Name})
registry.RegisterWorkflowWithOptions(ForceTaskQueueUserDataReplicationWorkflow, workflow.RegisterOptions{Name: forceTaskQueueUserDataReplicationWorkflow})
}
func (wc *replicationWorkerComponent) DedicatedWorkflowWorkerOptions() *workercommon.DedicatedWorkerOptions {
fx.go
// Use default worker
return nil
}
registry.RegisterActivity(wc.activities())
}
func (wc *replicationWorkerComponent) DedicatedActivityWorkerOptions() *workercommon.DedicatedWorkerOptions {
fx.go
return &workercommon.DedicatedWorkerOptions{
TaskQueue: primitives.MigrationActivityTQ,
Options: sdkworker.Options{
BackgroundActivityContext: headers.SetCallerType(context.Background(), headers.CallerTypePreemptable),
},
}
}
return func(
_ context.Context,
_ *verifyReplicationTasksRequest,
_ adminservice.AdminServiceClient,
_ adminservice.AdminServiceClient,
_ *namespace.Namespace,
_ *ExecutionInfo,
_ *adminservice.DescribeMutableStateResponse,
) (verifyResult, error) {
return verifyResult{
status: verified,
}
return &activities{
HistoryShardCount: wc.PersistenceConfig.NumHistoryShards,
executionManager: wc.ExecutionManager,
NamespaceRegistry: wc.NamespaceRegistry,
HistoryClient: wc.HistoryClient,
frontendClient: wc.FrontendClient,
clientFactory: wc.ClientFactory,
clientBean: wc.ClientBean,
namespaceReplicationQueue: wc.NamespaceReplicationQueue,
taskManager: wc.TaskManager,
Logger: wc.Logger,
MetricsHandler: wc.MetricsHandler,
forceReplicationMetricsHandler: wc.MetricsHandler.WithTags(metrics.WorkflowTypeTag(forceReplicationWorkflowName)),
generateMigrationTaskViaFrontend: dynamicconfig.WorkerGenerateMigrationTaskViaFrontend.Get(wc.DynamicCollection),
enableHistoryRateLimiter: dynamicconfig.WorkerEnableHistoryRateLimiter.Get(wc.DynamicCollection),
workflowVerifier: wc.WorkflowVerifier,
chasmRegistry: wc.ChasmRegistry,
}
}
targetDelay time.Duration,
shrinkFactor float64,
p := &AdaptivePool{
ts: ts,
minWorkers: minWorkers,
maxWorkers: maxWorkers,
targetDelay: targetDelay,
shrinkFactor: shrinkFactor,
ch: make(chan func()),
stopCh: make(chan struct{}),
}
for range minWorkers {
go p.work()
}
return p
}
// When Stop is called, concurrent calls to Do may or may not call their function, and future
// calls definitely won't.
close(p.stopCh)
}
// Do calls f() on a worker goroutine. If the call can't be started within targetDelay, it adds
// another worker. If Stop is called concurrently, Do may or may not call f. If Stop has been
// called already, Do does nothing.
// try send first
select {
return
}
// we might want to add a worker, send with timeout
if have < int64(p.maxWorkers) {
select {
case <-p.stopCh:
timer.Stop()
return
timer.Stop()
return
}
go p.work()
}
}
// blocking send
case p.ch <- f:
case <-p.stopCh:
}
}
for {
// try receive first
select {
f()
continue
}
if have > int64(p.minWorkers) {
// jitter this so we shrink slower than we grow
timech, timer := p.ts.NewTimer(time.Duration(float64(p.targetDelay) * p.shrinkFactor * rand.Float64()))
select {
timer.Stop()
return
timer.Stop()
f()
continue
}
return
}
}
}
return ZapTag{
field: zap.String(key, value),
}
}
return ZapTag{
field: zap.Strings(key, value),
}
}
// NewStringerTag returns a tag that will lazily generate the string representation
// These are still useful if the String() implementation is complicated, especially if
// you have lots of Debug-level logs that are ignored in production.
return ZapTag{
field: zap.Stringer(key, value),
}
}
// NewStringersTag returns a tag that will lazily generate the string representation
}
return ZapTag{
field: zap.Int64(key, value),
}
}
return ZapTag{
field: zap.Int(key, value),
}
}
return ZapTag{
field: zap.Int32(key, value),
}
}
func NewUInt32(key string, value uint32) ZapTag {
}
return ZapTag{
field: zap.Bool(key, value),
}
}
func NewErrorTag(key string, value error) ZapTag {
}
return ZapTag{
field: zap.Duration(key, value),
}
}
func NewDurationPtrTag(key string, value *durationpb.Duration) ZapTag {
}
return ZapTag{
field: zap.Any(key, value),
}
}
func NewBinaryTag(key string, value []byte) ZapTag {
// Shorter helpers (aliases for the New* functions above)
return NewStringTag(key, value)
}
func Strings(key string, value []string) ZapTag {
}
return NewStringerTag(key, value)
}
func Stringers(key string, value []fmt.Stringer) ZapTag {
}
return NewInt(key, value)
}
func Int32(key string, value int32) ZapTag {
}
return NewDurationTag(key, value)
}
func DurationPtr(key string, value *durationpb.Duration) ZapTag {
}
return NewAnyTag(key, value)
}
func Binary(key string, value []byte) ZapTag {
handler SideEffectTaskHandler[C, T],
opts ...RegistrableTaskOption,
return newRegistrableTask(
taskType,
reflect.TypeFor[T](),
reflect.TypeFor[C](),
func(
ctx Context,
component any,
taskInvocation TaskInvocation,
taskData any,
registry *Registry,
) (bool, error) {
return handler.Validate(
ctx,
handler PureTaskHandler[C, T],
opts ...RegistrableTaskOption,
return newRegistrableTask(
taskType,
reflect.TypeFor[T](),
reflect.TypeFor[C](),
func(
ctx Context,
component any,
taskInvocation TaskInvocation,
taskData any,
registry *Registry,
) (bool, error) {
return handler.Validate(
ctx,
sideEffectTaskDiscardFn sideEffectTaskDiscardFn,
opts ...RegistrableTaskOption,
rt := &RegistrableTask{
taskType: taskType,
goType: goType,
componentGoType: componentGoType,
validateFn: validateFn,
pureTaskExecuteFn: pureTaskExecuteFn,
sideEffectTaskExecuteFn: sideEffectTaskExecuteFn,
sideEffectTaskDiscardFn: sideEffectTaskDiscardFn,
isPureTask: isPureTask,
}
for _, opt := range opts {
}
}
func (rt *RegistrableTask) registerToLibrary(
library namer,
if rt.library != nil {
return "", 0, fmt.Errorf("task %s is already registered in library %s", rt.taskType, rt.library.Name())
}
fqn := rt.fqType()
rt.taskTypeID = GenerateTypeID(fqn)
// If outboundTaskGroup wasn't set on creation default it here,
// since this is the first place we will have the fqn.
if rt.outboundTaskGroup == "" {
}
}
// the library name and the task type. This is used to uniquely identify
// the task in the registry.
if rt.library == nil {
// this should never happen because the task is only accessible from the library.
panic("task is not registered to a library")
}
}
// affects multi-cursor and the circuit breaker.
// If task group isn't provided, the task group will default to the fully qualified name at library registration.
return func(rt *RegistrableTask) {
rt.outboundTaskGroup = taskgroup
}
}
// DefaultStoreType returns the storeType for the default persistence store
if c.DataStores[c.DefaultStore].SQL != nil {
}
return StoreTypeNoSQL
}
// Validate validates the persistence config
stores := []string{c.DefaultStore}
if c.VisibilityStore != "" {
stores = append(stores, c.VisibilityStore)
}
if c.SecondaryVisibilityStore != "" {
stores = append(stores, c.SecondaryVisibilityStore)
}
// - visibilityStore (es), secondaryVisibilityStore (advanced sql)
return fmt.Errorf("%w: visibilityStore must be specified", ErrPersistenceConfig)
}
isAnyCustom := c.DataStores[c.VisibilityStore].CustomDataStoreConfig != nil ||
c.DataStores[c.SecondaryVisibilityStore].CustomDataStoreConfig != nil
}
ds, ok := c.DataStores[st]
if !ok {
return fmt.Errorf("%w: missing config for datastore %q", ErrPersistenceConfig, st)
}
return fmt.Errorf("%w: datastore %q: %s", ErrPersistenceConfig, st, err.Error())
}
}
}
// VisibilityConfigExist returns whether user specified visibilityStore in config
return c.VisibilityStore != ""
}
// SecondaryVisibilityConfigExist returns whether user specified secondaryVisibilityStore in config
return c.SecondaryVisibilityStore != ""
}
func (c *Persistence) IsSQLVisibilityStore() bool {
}
return c.DataStores[c.VisibilityStore]
}
if c.SecondaryVisibilityStore != "" {
return c.DataStores[c.SecondaryVisibilityStore]
}
ds := c.DataStores[c.VisibilityStore]
if ds.Elasticsearch != nil && ds.Elasticsearch.GetSecondaryVisibilityIndex() != "" {
esConfig := *ds.Elasticsearch
esConfig.Indices = map[string]string{
}
}
}
switch {
case ds.SQL != nil:
return ds.SQL.DatabaseName
case ds.Cassandra != nil:
return ds.Cassandra.Keyspace
case ds.CustomDataStoreConfig != nil:
return ds.CustomDataStoreConfig.IndexName
return ""
}
}
// Validate validates the data store config
storeConfigCount := 0
if ds.SQL != nil {
storeConfigCount++
}
if ds.Cassandra != nil {
storeConfigCount++
}
storeConfigCount++
}
storeConfigCount++
}
return errors.New(
"must provide config for one and only one datastore: " +
}
if ds.SQL.TaskScanPartitions == 0 {
ds.SQL.TaskScanPartitions = 1
}
if err := ds.SQL.validate(); err != nil {
return err
}
}
if err := ds.Cassandra.validate(); err != nil {
return err
}
}
if err := ds.Elasticsearch.Validate(); err != nil {
return err
}
}
}
}
if c.PasswordCommand != nil && c.Password != "" {
return errors.New("passwordCommand and password are mutually exclusive")
}
return errors.New("passwordCommand.command must not be empty")
}
}
logger log.Logger,
serializer serialization.Serializer,
return &sqlShardStore{
SqlStore: NewSQLStore(db, logger, serializer),
currentClusterName: currentClusterName,
}, nil
}
func (m *sqlShardStore) GetClusterName() string {
ctx context.Context,
request *persistence.InternalGetOrCreateShardRequest,
row, err := m.DB.SelectFromShards(ctx, sqlplugin.ShardsFilter{
ShardID: request.ShardID,
})
switch err {
case nil:
return &persistence.InternalGetOrCreateShardResponse{
ShardInfo: persistence.NewDataBlob(row.Data, row.DataEncoding),
}, nil
default:
return nil, serviceerror.NewUnavailablef("GetOrCreateShard: failed to get ShardID %v. Error: %v", request.ShardID, err)
}
return nil, serviceerror.NewNotFoundf("GetOrCreateShard: ShardID %v not found. Error: %v", request.ShardID, err)
}
if err != nil {
return nil, serviceerror.NewUnavailablef("GetOrCreateShard: failed to encode shard info for ShardID %v. Error: %v", request.ShardID, err)
}
ShardID: request.ShardID,
RangeID: rangeID,
Data: shardInfo.Data,
DataEncoding: shardInfo.EncodingType.String(),
}
_, err = m.DB.InsertIntoShards(ctx, row)
if err == nil {
return &persistence.InternalGetOrCreateShardResponse{
ShardInfo: shardInfo,
}, nil
} else if m.DB.IsDupEntryError(err) {
// conflict, try again
request.CreateShardInfo = nil // prevent loop
ctx context.Context,
request *persistence.InternalUpdateShardRequest,
return m.txExecute(ctx, "UpdateShard", func(tx sqlplugin.Tx) error {
if err := lockShard(ctx,
tx,
request.ShardID,
request.PreviousRangeID,
m.logger,
); err != nil {
return err
}
ShardID: request.ShardID,
RangeID: request.RangeID,
Data: request.ShardInfo.Data,
DataEncoding: request.ShardInfo.EncodingType.String(),
})
if err != nil {
return err
}
if err != nil {
return fmt.Errorf("rowsAffected returned error for shardID %v: %v", request.ShardID, err)
}
return fmt.Errorf("rowsAffected returned %v shards instead of one", rowsAffected)
}
})
}
ctx context.Context,
request *persistence.AssertShardOwnershipRequest,
// AssertShardOwnership is not implemented for sql shard store
return nil
}
// initiated by the owning shard
oldRangeID int64,
logger log.Logger,
rangeID, err := tx.WriteLockShards(ctx, sqlplugin.ShardsFilter{
ShardID: shardID,
})
switch err {
case nil:
if rangeID != oldRangeID {
return &persistence.ShardOwnershipLostError{
ShardID: shardID,
}
}
case sql.ErrNoRows:
return serviceerror.NewUnavailablef("Failed to lock shard with ID %v that does not exist.", shardID)
func NewLocalStoreTlsProvider(tlsConfig *config.RootTLS, metricsHandler metrics.Handler, logger log.Logger, certProviderFactory CertProviderFactory,
internodeProvider := certProviderFactory(&tlsConfig.Internode, nil, nil, tlsConfig.RefreshInterval, logger)
var workerProvider CertProvider
if isSystemWorker(tlsConfig) { // explicit system worker config
workerProvider = certProviderFactory(nil, &tlsConfig.SystemWorker, nil, tlsConfig.RefreshInterval, logger)
internodeWorkerProvider := certProviderFactory(&tlsConfig.Internode, nil, &tlsConfig.Frontend.Client, tlsConfig.RefreshInterval, logger)
workerProvider = internodeWorkerProvider
}
for key, groupTLS := range tlsConfig.RemoteClusters {
remoteClusterClientCertProvider[key] = certProviderFactory(&groupTLS, nil, nil, tlsConfig.RefreshInterval, logger)
}
internodeCertProvider: internodeProvider,
internodeClientCertProvider: internodeProvider,
frontendCertProvider: certProviderFactory(&tlsConfig.Frontend, nil, nil, tlsConfig.RefreshInterval, logger),
workerCertProvider: workerProvider,
frontendPerHostCertProviderMap: newLocalStorePerHostCertProviderMap(
tlsConfig.Frontend.PerHostOverrides, certProviderFactory, tlsConfig.RefreshInterval, logger),
remoteClusterClientCertProvider: remoteClusterClientCertProvider,
RWMutex: sync.RWMutex{},
settings: tlsConfig,
metricsHandler: metricsHandler,
logger: logger,
cachedRemoteClusterClientConfig: make(map[string]*tls.Config),
}
provider.initialize()
return provider, nil
}
period := s.settings.ExpirationChecks.CheckInterval
if period != 0 {
s.stop = make(chan bool)
s.ticker = time.NewTicker(period)
}
func (s *localStoreTlsProvider) GetFrontendClientConfig() (*tls.Config, error) {
local_store_tls_provider.go
var client *config.ClientTLS
var useTLS bool
if isSystemWorker(s.settings) {
client = &s.settings.SystemWorker.Client
useTLS = true
client = &s.settings.Frontend.Client
useTLS = s.settings.Frontend.IsClientEnabled()
}
return s.getOrCreateConfig(
&s.cachedFrontendClientConfig,
func() (*tls.Config, error) {
return newClientTLSConfig(s.workerCertProvider, client.ServerName,
useTLS, true, !client.DisableHostVerification)
}
func (s *localStoreTlsProvider) GetFrontendServerConfig() (*tls.Config, error) {
local_store_tls_provider.go
return s.getOrCreateConfig(
&s.cachedFrontendServerConfig,
func() (*tls.Config, error) {
return newServerTLSConfig(s.frontendCertProvider, s.frontendPerHostCertProviderMap, &s.settings.Frontend, s.logger)
},
}
func (s *localStoreTlsProvider) GetInternodeServerConfig() (*tls.Config, error) {
local_store_tls_provider.go
return s.getOrCreateConfig(
&s.cachedInternodeServerConfig,
func() (*tls.Config, error) {
return newServerTLSConfig(s.internodeCertProvider, nil, &s.settings.Internode, s.logger)
},
configConstructor tlsConfigConstructor,
isEnabled bool,
if !isEnabled {
}
// Check if exists under a read lock first
}
return tls.SystemWorker.CertData != "" || tls.SystemWorker.CertFile != "" ||
len(tls.SystemWorker.Client.RootCAData) > 0 || len(tls.SystemWorker.Client.RootCAFiles) > 0 ||
tls.SystemWorker.Client.ForceTLS
}
// matchRemoteClusterKey checks exact matches, then finds the match with the most non-wildcard characters
logger log.Logger,
stickyCacheSize dynamicconfig.IntPropertyFn,
return &clientFactory{
hostPort: hostPort,
tlsConfig: tlsConfig,
metricsHandler: NewMetricsHandler(metricsHandler),
logger: logger,
sdklogger: log.NewSdkLogger(logger),
stickyCacheSize: stickyCacheSize,
}
}
options.HostPort = f.hostPort
options.MetricsHandler = f.metricsHandler
options.Logger = f.sdklogger
options.ConnectionOptions = sdkclient.ConnectionOptions{
TLS: f.tlsConfig,
DialOptions: []grpc.DialOption{
grpc.WithUnaryInterceptor(sdkClientNameHeadersInjectorInterceptor()),
},
}
return options
}
// this shouldn't fail if the first client was created successfully
client, err := sdkclient.NewClientFromExisting(f.GetSystemClient(), f.options(options))
if err != nil {
f.logger.Fatal("error creating sdk client", tag.Error(err))
}
}
f.once.Do(func() {
err := backoff.ThrottleRetry(func() error {
sdkClient, err := sdkclient.Dial(f.options(sdkclient.Options{
Namespace: primitives.SystemLocalNamespace,
}))
if err != nil {
f.logger.Warn("error creating sdk client", tag.Error(err))
return err
}
return nil
}, common.CreateSdkClientFactoryRetryPolicy(), func(err error) bool {
// note err is wrapped by sdk
return common.IsContextDeadlineExceededErr(err) || errors.As(err, &unavail)
})
f.logger.Fatal("error creating sdk client", tag.Error(err))
}
f.logger.Info("setting sticky workflow cache size", tag.Int("size", size))
sdkworker.SetStickyWorkflowCacheSize(size)
}
})
}
taskQueue string,
options sdkworker.Options,
return sdkworker.New(client, taskQueue, options)
}
// Overwrite the 'client-name' and 'client-version' headers on gRPC requests sent using the Go SDK
// so they clearly indicate that the request is coming from the Temporal server.
return func(
ctx context.Context,
method string,
req, reply any,
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
// Can't use headers.SetVersions() here because it is _appending_ headers to the context
// rather than _replacing_ them, which means Go SDK's default headers would still be present.
md, mdExist := metadata.FromOutgoingContext(ctx)
if !mdExist {
md = metadata.New(nil)
}
md.Set(headers.ClientVersionHeaderName, headers.ServerVersion)
ctx = metadata.NewOutgoingContext(ctx, md)
return invoker(ctx, method, req, reply, cc, opts...)
}
}
//
// It is configured to use a pre-registered test namespace and will be closed on TestServer.Stop.
if ts.defaultClient == nil {
ts.defaultClient = ts.NewClientWithOptions(ts.defaultClientOptions)
}
return ts.defaultClient
}
// If no namespace option is set it will use a pre-registered test namespace.
// The returned client will be closed on TestServer.Stop.
if opts.Namespace == "" {
opts.Namespace = ts.defaultTestNamespace
}
if opts.Logger == nil {
opts.Logger = &testLogger{ts.t}
}
defer cancel()
c, err := ts.server.NewClientWithOptions(ctx, opts)
if err != nil {
ts.fatal(fmt.Errorf("error creating client: %w", err))
}
return c
}
// Stop closes test clients and shuts down the server.
for _, w := range ts.workers {
w.Stop()
}
c.Close()
}
if err := ts.server.Stop(); err != nil {
// Log instead of throwing error because there's no need to fail the test
// if it already succeeded.
// If not specifying the WithT option, the caller should execute Stop when finished to close
// the server and release resources.
testNamespace := fmt.Sprintf("temporaltest-%d", rand.Intn(1e6))
ts := TestServer{
defaultTestNamespace: testNamespace,
}
// Apply options
for _, opt := range opts {
opt.apply(&ts)
}
}
Namespaces: []string{ts.defaultTestNamespace},
Ephemeral: true,
Logger: log.NewNoopLogger(),
DynamicConfig: dynamicconfig.StaticClient{
dynamicconfig.ForceSearchAttributesCacheRefreshOnRead.Key(): []dynamicconfig.ConstrainedValue{{Value: true}},
},
// Disable "accept incoming network connections?" prompt on macOS
FrontendIP: "127.0.0.1",
}, ts.serverOptions...)
if err != nil {
ts.fatal(fmt.Errorf("error creating server: %w", err))
}
// Start does not block as long as InterruptOn is unset.
if err := s.Start(); err != nil {
ts.fatal(err)
}
// This sleep helps avoid a panic in github.com/temporalio/[email protected]/swim/labels.go:175
return &ts
}
resolver membership.ServiceResolver,
connectionCloseDelay dynamicconfig.DurationPropertyFn,
c := &clientImpl{
timeout: timeout,
longPollTimeout: longPollTimeout,
clients: clients,
resolver: resolver,
connectionCloseDelay: connectionCloseDelay,
metricsHandler: metricsHandler,
logger: logger,
loadBalancer: lb,
spreadRouting: spreadRouting,
partitionCache: newPartitionCache(metricsHandler),
}
// Start goroutine to prune partition count cache. Stopped by Stop().
c.partitionCache.Start()
// Evict cached clients whose host leaves the membership ring. Stopped by Stop().
c.evictionWatcher = goro.NewHandle(context.Background()).Go(c.watchMembership)
return c
}
// Stop deterministically releases the resources started by NewClient: it stops
// the eviction watcher and partition-cache rotation goroutines and closes every
// cached gRPC connection. It is safe to call more than once.
c.evictionWatcher.Cancel()
<-c.evictionWatcher.Done()
c.partitionCache.Stop()
c.clients.EvictAll()
}
// watchMembership evicts cached clients whose host leaves the membership ring.
// It runs until ctx is cancelled (by Stop).
listenerName := fmt.Sprintf("matchingClientCache-%s", uuid.New().String())
ch := make(chan *membership.ChangedEvent, 1)
if err := c.resolver.AddListener(listenerName, ch); err != nil {
c.logger.Error("Failed to subscribe matching cache to membership", tag.Error(err))
return err
}
// Reap departed hosts via a per-address deadline checked by a single ticker;
// a re-add resets it to the latest removal.
ticker := time.NewTicker(evictionCheckInterval)
defer ticker.Stop()
for {
select {
return nil
for _, h := range event.HostsRemoved {
}
delete(evictAt, h.GetAddress())
}
case <-ticker.C:
reapEvictableClients(c.resolver, c.clients, evictAt)
}
spreadChange := c.spreadRouting()
spread := spreadChange.Value(p.GradualChangeKey(), time.Now())
return c.clients.Lookup(p.RoutingKey(spread))
}
func (c *clientImpl) getClientForTaskQueuePartition(
partition tqid.Partition,
addr, err := c.Route(partition)
if err != nil {
}
client, err := c.clients.GetClientForClientKey(addr)
if err != nil {
sdkClientFactory sdk.ClientFactory,
hostInfo membership.HostInfo,
return &workerManager{
hostInfo: hostInfo,
logger: logger,
sdkClientFactory: sdkClientFactory,
workerComponents: workerComponents,
}
}
if !atomic.CompareAndSwapInt32(
&wm.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
Identity: "temporal-system@" + wm.hostInfo.Identity(),
// TODO: add dynamic config for worker options
BackgroundActivityContext: headers.SetCallerType(context.Background(), headers.CallerTypeBackgroundHigh),
}
sdkClient := wm.sdkClientFactory.GetSystemClient()
defaultWorker := wm.sdkClientFactory.NewWorker(sdkClient, primitives.DefaultWorkerTaskQueue, defaultWorkerOptions)
wm.workers = []sdkworker.Worker{defaultWorker}
for _, wc := range wm.workerComponents {
wfWorkerOptions := wc.DedicatedWorkflowWorkerOptions()
if wfWorkerOptions == nil {
// use default worker
wc.RegisterWorkflow(defaultWorker)
} else {
wfWorkerOptions.Options.Identity = "temporal-system@" + wm.hostInfo.Identity()
// this worker component requires a dedicated worker
}
if activityWorkerOptions == nil {
// use default worker
wc.RegisterActivities(defaultWorker)
// TODO: This is to prevent issues during upgrade/downgrade. Remove in 1.24 release.
wc.RegisterActivities(defaultWorker)
// this worker component requires a dedicated worker for activities
activityWorkerOptions.Options.DisableWorkflowWorker = true
activityWorkerOptions.Options.Identity = "temporal-system@" + wm.hostInfo.Identity()
activityWorker := wm.sdkClientFactory.NewWorker(sdkClient, activityWorkerOptions.TaskQueue, activityWorkerOptions.Options)
wc.RegisterActivities(activityWorker)
wm.workers = append(wm.workers, activityWorker)
}
}
if err := w.Start(); err != nil {
}
}
}
if !atomic.CompareAndSwapInt32(
&wm.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
w.Stop()
}
wm.logger.Info("", tag.ComponentWorkerManager, tag.LifeCycleStopped)
}
func (*StateMachineMap) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*StateMachineRef) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*StateMachineTaskInfo) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*StateMachineTimerGroup) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*VersionedTransition) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*StateMachineTombstoneBatch) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_persistence_v1_hsm_proto_init() {
if File_temporal_server_api_persistence_v1_hsm_proto != nil {
return
}
file_temporal_server_api_persistence_v1_hsm_proto_msgTypes[8].OneofWrappers = []any{
(*StateMachineTombstone_ActivityScheduledEventId)(nil),
(*StateMachineTombstone_TimerId)(nil),
(*StateMachineTombstone_ChildExecutionInitiatedEventId)(nil),
(*StateMachineTombstone_RequestCancelInitiatedEventId)(nil),
(*StateMachineTombstone_SignalExternalInitiatedEventId)(nil),
(*StateMachineTombstone_UpdateId)(nil),
(*StateMachineTombstone_StateMachinePath)(nil),
(*StateMachineTombstone_ChasmNodePath)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_hsm_proto_rawDesc), len(file_temporal_server_api_persistence_v1_hsm_proto_rawDesc)),
NumEnums: 0,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_hsm_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_hsm_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_hsm_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_hsm_proto = out.File
file_temporal_server_api_persistence_v1_hsm_proto_goTypes = nil
file_temporal_server_api_persistence_v1_hsm_proto_depIdxs = nil
}
latencyWindowSize time.Duration,
latencyWindowCount int,
latencyDistribution, err := stats.NewWindowedTDigest(stats.WindowConfig{
WindowSize: latencyWindowSize,
WindowCount: latencyWindowCount,
})
if err != nil {
logger.Error("failed to create latency distribution helper, falling back to default config", tag.Error(err))
latencyDistribution, err = stats.NewWindowedTDigest(stats.WindowConfig{
}
status: common.DaemonStatusInitialized,
shutdownCh: make(chan struct{}),
requestCounts: make(map[int32]int64),
metricsHandler: metricsHandler,
emitMetricsTimer: time.NewTicker(emitMetricsInterval),
logger: logger,
aggregationEnabled: aggregationEnabled,
percentilesEnabled: percentilesEnabled,
latencyDistribution: latencyDistribution,
}
if aggregationEnabled {
ret.latencyAverage = aggregate.NewMovingWindowAvgImpl(windowSize, maxBufferSize)
ret.errorRatio = aggregate.NewMovingWindowAvgImpl(windowSize, maxBufferSize)
} else {
ret.latencyAverage = aggregate.NoopMovingWindowAverage
ret.errorRatio = aggregate.NoopMovingWindowAverage
}
}
if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
}
if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
s.emitMetricsTimer.Stop()
}
func (s *healthSignalAggregatorImpl) Record(callerSegment int32, latency time.Duration, err error) {
health_signal_aggregator.go
if s.aggregationEnabled {
s.latencyAverage.Record(latency.Milliseconds())
if s.percentilesEnabled() && s.latencyDistribution != nil {
s.latencyDistribution.RecordToLatestWindow(float64(latency.Milliseconds()))
}
s.errorRatio.Record(1)
}
}
s.incrementShardRequestCount(callerSegment)
}
}
}
func (s *healthSignalAggregatorImpl) incrementShardRequestCount(shardID int32) {
health_signal_aggregator.go
s.requestsLock.Lock()
defer s.requestsLock.Unlock()
s.requestCounts[shardID]++
}
// Traverse through all shards and get the per-namespace persistence RPS for all shards.
// is configured in dynamic config. This will allow us to see if some namespaces had hit
// this limit in any of the shards.
for {
select {
return
case <-s.emitMetricsTimer.C:
s.requestsLock.Lock()
}
if err == nil {
}
if common.IsContextCanceledErr(err) {
return true
testHooks testhooks.TestHooks,
metricsHandler metrics.Handler,
highestRevSignaledToVersionWf := cache.New(dynamicconfig.ReactivationSignalDedupCacheMaxSize.Get(dc)(), nil)
lc.Append(fx.Hook{
OnStop: func(context.Context) error {
return nil
},
})
logger: logger,
historyClient: historyClient,
visibilityManager: visibilityManager,
matchingClient: matchingClient,
workerControllerInstanceClient: workerControllerInstanceClient,
maxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
visibilityMaxPageSize: dynamicconfig.FrontendVisibilityMaxPageSize.Get(dc),
maxTaskQueuesInDeploymentVersion: dynamicconfig.MatchingMaxTaskQueuesInDeploymentVersion.Get(dc),
maxDeployments: dynamicconfig.MatchingMaxDeployments.Get(dc),
testHooks: testHooks,
metricsHandler: metricsHandler,
highestRevSignaledToVersionWf: highestRevSignaledToVersionWf,
}
}
dc *dynamicconfig.Collection,
params activityDeps,
return fxResult{
Component: &workerComponent{
activityDeps: params,
dynamicConfig: dc,
},
}
}
func (s *workerComponent) DedicatedWorkerOptions(ns *namespace.Namespace) *workercommon.PerNSDedicatedWorkerOptions {
fx.go
return &workercommon.PerNSDedicatedWorkerOptions{
Enabled: true,
}
}
func (s *workerComponent) Register(registry sdkworker.Registry, ns *namespace.Namespace, details workercommon.RegistrationDetails) func() {
fx.go
workflowVersionGetter := func() DeploymentWorkflowVersion {
val := DeploymentWorkflowVersion(dynamicconfig.MatchingDeploymentWorkflowVersion.Get(s.dynamicConfig)(ns.Name().String()))
return val
}
versionWorkflow := func(ctx workflow.Context, args *deploymentspb.WorkerDeploymentVersionWorkflowArgs) error {
fx.go
refreshIntervalGetter := func() time.Duration {
return dynamicconfig.VersionDrainageStatusRefreshInterval.Get(s.dynamicConfig)(ns.Name().String())
return VersionWorkflow(ctx, workflowVersionGetter, refreshIntervalGetter, visibilityGracePeriodGetter, args)
}
registry.RegisterWorkflowWithOptions(versionWorkflow, workflow.RegisterOptions{Name: WorkerDeploymentVersionWorkflowType})
fx.go
deploymentWorkflow := func(ctx workflow.Context, args *deploymentspb.WorkerDeploymentWorkflowArgs) error {
maxVersionsGetter := func() int {
return dynamicconfig.MatchingMaxVersionsInDeployment.Get(s.dynamicConfig)(ns.Name().String())
return Workflow(ctx, workflowVersionGetter, maxVersionsGetter, args)
}
registry.RegisterWorkflowWithOptions(deploymentWorkflow, workflow.RegisterOptions{Name: WorkerDeploymentWorkflowType})
fx.go
versionActivities := &VersionActivities{
activityDeps: s.activityDeps,
namespace: ns,
}
registry.RegisterActivity(versionActivities)
activities := &Activities{
activityDeps: s.activityDeps,
namespace: ns,
}
registry.RegisterActivity(activities)
return nil
}
)
return a.staticClusterState
}
const (
visibilityReadEnabled bool,
namespaceDefaults *config.ArchivalNamespaceDefaults,
historyConfig := NewArchivalConfig(
historyState,
dynamicconfig.HistoryArchivalState.WithDefault(historyState).Get(dc),
dynamicconfig.EnableReadFromHistoryArchival.WithDefault(historyReadEnabled).Get(dc),
namespaceDefaults.History.State,
namespaceDefaults.History.URI,
)
visibilityConfig := NewArchivalConfig(
visibilityState,
dynamicconfig.VisibilityArchivalState.WithDefault(visibilityState).Get(dc),
dynamicconfig.EnableReadFromVisibilityArchival.WithDefault(visibilityReadEnabled).Get(dc),
namespaceDefaults.Visibility.State,
namespaceDefaults.Visibility.URI,
)
return &archivalMetadata{
historyConfig: historyConfig,
visibilityConfig: visibilityConfig,
}
}
return metadata.historyConfig
}
return metadata.visibilityConfig
}
// NewArchivalConfig constructs a new valid ArchivalConfig
namespaceDefaultStateStr string,
namespaceDefaultURI string,
staticClusterState, err := getClusterArchivalState(staticClusterStateStr)
if err != nil {
panic(err)
}
namespaceDefaultState, err := getNamespaceArchivalState(namespaceDefaultStateStr)
archival_metadata.go
if err != nil {
panic(err)
}
staticClusterState: staticClusterState,
dynamicClusterState: dynamicClusterState,
enableRead: enableRead,
namespaceDefaultState: namespaceDefaultState,
namespaceDefaultURI: namespaceDefaultURI,
}
}
}
str = strings.TrimSpace(strings.ToLower(str))
switch str {
return ArchivalDisabled, nil
case config.ArchivalPaused:
return ArchivalPaused, nil
}
func getNamespaceArchivalState(str string) (enumspb.ArchivalState, error) {
archival_metadata.go
str = strings.TrimSpace(strings.ToLower(str))
switch str {
case "", config.ArchivalDisabled:
return enumspb.ARCHIVAL_STATE_DISABLED, nil
case config.ArchivalEnabled:
return enumspb.ARCHIVAL_STATE_ENABLED, nil
logger log.Logger,
renewRangeIDFn renewRangeIDFn,
return &taskKeyManager{
generator: newTaskKeyGenerator(
config.RangeSizeBits,
timeSource,
logger,
renewRangeIDFn,
),
tracker: newTaskRequestTracker(taskCategoryRegistry),
timeSource: timeSource,
logger: logger,
config: config,
}
}
func (m *taskKeyManager) setAndTrackTaskKeys(
}
m.tracker.drain()
}
func (m *taskKeyManager) setRangeID(
rangeID int64,
m.generator.setRangeID(rangeID)
// rangeID update means all pending add tasks requests either already succeeded
// are guaranteed to fail, so we can clear pending requests in the tracker
m.tracker.clear()
}
func (m *taskKeyManager) setTaskMinScheduledTime(
taskMinScheduledTime time.Time,
m.generator.setTaskMinScheduledTime(taskMinScheduledTime)
}
func (m *taskKeyManager) getExclusiveReaderHighWatermark(
category tasks.Category,
minTaskKey, ok := m.tracker.minTaskKey(category)
if !ok {
}
// TODO: should this be moved generator.setTaskKeys() ?
// TODO: Truncation here is just to make sure task scheduled time has the same precision as the old logic.
// Remove this truncation once we validate the rest of the code can worker correctly with higher precision.
m.timeSource.Now().Add(m.config.TimerProcessorMaxTimeShift()).Truncate(common.ScheduledTaskMinPrecision),
)
nextTaskKey := m.generator.peekTaskKey(category)
exclusiveReaderHighWatermark := tasks.MinKey(
minTaskKey,
nextTaskKey,
)
if category.Type() == tasks.CategoryTypeScheduled {
// TODO: Truncation here is just to make sure task scheduled time has the same precision as the old logic.
// Remove this truncation once we validate the rest of the code can worker correctly with higher precision.
exclusiveReaderHighWatermark.FireTime = exclusiveReaderHighWatermark.FireTime.
Truncate(common.ScheduledTaskMinPrecision)
}
}
func NewReaderGroup(
initializer ReaderInitializer,
return &ReaderGroup{
initializer: initializer,
status: common.DaemonStatusInitialized,
readerMap: make(map[int64]Reader),
}
}
if !atomic.CompareAndSwapInt32(&g.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
defer g.Unlock()
for _, reader := range g.readerMap {
reader.Start()
}
}
if !atomic.CompareAndSwapInt32(&g.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
defer g.Unlock()
for _, reader := range g.readerMap {
}
}
}
g.Lock()
defer g.Unlock()
return g.getReaderByIDLocked(readerID)
}
if g.readerMap == nil {
return nil, false
}
return reader, ok
}
g.Lock()
defer g.Unlock()
return g.newReaderLocked(readerID, slices...)
}
func (g *ReaderGroup) newReaderLocked(readerID int64, slices ...Slice) Reader {
reader_group.go
reader := g.initializer(readerID, slices)
if _, ok := g.readerMap[readerID]; ok {
panic(fmt.Sprintf("reader with ID %v already exists", readerID))
}
if g.isStarted() {
}
}
}
return atomic.LoadInt32(&g.status) == common.DaemonStatusStarted
}
// ThrottleRetry is a resource aware version of Retry.
// Resource exhausted error will be retried using a different throttle retry policy, instead of the specified one.
func ThrottleRetry(operation Operation, policy RetryPolicy, isRetryable IsRetryable) error {
retry.go
ctxOp := func(context.Context) error { return operation() }
return ThrottleRetryContext(context.Background(), ctxOp, policy, isRetryable)
}
policy RetryPolicy,
isRetryable IsRetryable,
var err error
var next time.Duration
if isRetryable == nil {
}
timeSrc := clock.NewRealTimeSource()
r := NewRetrier(policy, timeSrc)
t := NewRetrier(throttleRetryPolicy, timeSrc)
for ctx.Err() == nil {
}
return err
}
}
next = max(next, t.NextBackOff(err))
}
break
}
select {
case <-timer.C:
timer.Stop()
}
}
// always return the last error we got from operation, even if it is not useful
// this retry utility does not have enough information to do any filtering/mapping
}
return ctx.Err()
}
policy RetryPolicy,
isRetryable IsRetryable,
var zero T
var result T
var err error
var next time.Duration
if isRetryable == nil {
isRetryable = func(error) bool { return true }
}
timeSrc := clock.NewRealTimeSource()
r := NewRetrier(policy, timeSrc)
t := NewRetrier(throttleRetryPolicy, timeSrc)
for ctx.Err() == nil {
if err == nil {
return result, nil
}
return zero, err
}
}
if _, ok := err.(*serviceerror.ResourceExhausted); ok {
queueType persistence.QueueType,
serializer serialization.Serializer,
queue := &sqlQueue{
SqlStore: NewSQLStore(db, logger, serializer),
queueType: queueType,
logger: logger,
}
return queue, nil
}
func (q *sqlQueue) Init(
ctx context.Context,
blob *commonpb.DataBlob,
if err := q.initializeQueueMetadata(ctx, blob); err != nil {
return err
}
}
}
return -q.queueType
}
func (q *sqlQueue) initializeQueueMetadata(
ctx context.Context,
blob *commonpb.DataBlob,
_, err := q.DB.SelectFromQueueMetadata(ctx, sqlplugin.QueueMetadataFilter{
QueueType: q.queueType,
})
switch err {
return nil
result, err := q.DB.InsertIntoQueueMetadata(ctx, &sqlplugin.QueueMetadataRow{
QueueType: q.queueType,
Data: blob.Data,
DataEncoding: blob.EncodingType.String(),
})
if err != nil {
return serviceerror.NewUnavailablef("initializeQueueMetadata operation failed. Error %v", err)
}
if err != nil {
return fmt.Errorf("rowsAffected returned error when initializing queue metadata %v: %v", q.queueType, err)
}
return fmt.Errorf("rowsAffected returned %v queue metadata instead of one", rowsAffected)
}
default:
return err
ctx context.Context,
blob *commonpb.DataBlob,
_, err := q.DB.SelectFromQueueMetadata(ctx, sqlplugin.QueueMetadataFilter{
QueueType: q.getDLQTypeFromQueueType(),
})
switch err {
return nil
result, err := q.DB.InsertIntoQueueMetadata(ctx, &sqlplugin.QueueMetadataRow{
QueueType: q.getDLQTypeFromQueueType(),
Data: blob.Data,
DataEncoding: blob.EncodingType.String(),
})
if err != nil {
return serviceerror.NewUnavailablef("initializeDLQMetadata operation failed. Error %v", err)
}
if err != nil {
return fmt.Errorf("rowsAffected returned error when initializing DLQ metadata %v: %v", q.queueType, err)
}
return fmt.Errorf("rowsAffected returned %v DLQ metadata instead of one", rowsAffected)
}
default:
return err
ctx context.Context,
filter sqlplugin.HistoryImmediateTasksRangeFilter,
var rows []sqlplugin.HistoryImmediateTasksRow
if err := mdb.conn.SelectContext(ctx,
&rows,
getHistoryImmediateTasksQuery,
filter.ShardID,
filter.CategoryID,
filter.InclusiveMinTaskID,
filter.ExclusiveMaxTaskID,
filter.PageSize,
); err != nil {
return nil, err
}
}
ctx context.Context,
filter sqlplugin.TransferTasksRangeFilter,
var rows []sqlplugin.TransferTasksRow
if err := mdb.conn.SelectContext(ctx,
&rows,
getTransferTasksQuery,
filter.ShardID,
filter.InclusiveMinTaskID,
filter.ExclusiveMaxTaskID,
filter.PageSize,
); err != nil {
return nil, err
}
}
ctx context.Context,
filter sqlplugin.TimerTasksRangeFilter,
var rows []sqlplugin.TimerTasksRow
filter.InclusiveMinVisibilityTimestamp = mdb.converter.ToSQLiteDateTime(filter.InclusiveMinVisibilityTimestamp)
filter.ExclusiveMaxVisibilityTimestamp = mdb.converter.ToSQLiteDateTime(filter.ExclusiveMaxVisibilityTimestamp)
if err := mdb.conn.SelectContext(ctx,
&rows,
getTimerTasksQuery,
filter.ShardID,
filter.InclusiveMinVisibilityTimestamp,
filter.InclusiveMinTaskID,
filter.InclusiveMinVisibilityTimestamp,
filter.ExclusiveMaxVisibilityTimestamp,
filter.PageSize,
); err != nil {
return nil, err
}
rows[i].VisibilityTimestamp = mdb.converter.FromSQLiteDateTime(rows[i].VisibilityTimestamp)
}
}
ctx context.Context,
filter sqlplugin.VisibilityTasksRangeFilter,
var rows []sqlplugin.VisibilityTasksRow
if err := mdb.conn.SelectContext(ctx,
&rows,
getVisibilityTasksQuery,
filter.ShardID,
filter.InclusiveMinTaskID,
filter.ExclusiveMaxTaskID,
filter.PageSize,
); err != nil {
return nil, err
}
}
}
func NormalPartitionFromRpcName(rpcName string, namespaceId string, taskType enumspb.TaskQueueType) (*NormalPartition, error) {
task_queue_id.go
baseName, partition, err := parseRpcName(rpcName)
if err != nil {
return nil, err
}
return tq.NormalPartition(partition), nil
}
func MustNormalPartitionFromRpcName(rpcName string, namespaceId string, taskType enumspb.TaskQueueType) *NormalPartition {
task_queue_id.go
p, err := NormalPartitionFromRpcName(rpcName, namespaceId, taskType)
if err != nil {
panic(err)
}
}
return n.name
}
func (n *TaskQueueFamily) NamespaceId() string {
}
return &NormalPartition{
taskQueue: n,
partitionId: partitionId,
}
}
func (n *TaskQueue) StickyPartition(stickyName string) *StickyPartition {
}
return p.taskQueue
}
return p.partitionId == 0
}
func (p *NormalPartition) IsChild() bool {
}
return p.taskQueue.family.namespaceId
}
return p.taskQueue.taskType
}
// ParentPartition returns a NormalPartition for the parent partition, using the given branching degree.
}
if p.IsRoot() {
}
return nonRootPartitionPrefix + p.TaskQueue().Name() + partitionDelimiter + strconv.Itoa(p.partitionId)
}
}
if batchSize == 0 {
return fmt.Sprintf("%s:%s:%d", p.NamespaceId(), p.RpcName(), p.TaskType()), 0
task_queue_id.go
}
// We want to use LookupN to spread partitions across available nodes, but LookupN takes O(n)
// time and space, so we should limit the n that we pass to it. Reduce the partition id by some
}
key := fmt.Sprintf("%s:%s:%d", p.NamespaceId(), p.RpcName(), p.TaskType())
return []byte(key)
}
// parseRpcName takes the rpc name of a task queue partition and returns a ParseTaskQueuePartition.
// Returns an error if the given name is not a valid rpc name.
baseName := rpcName
partition := 0
if strings.HasPrefix(rpcName, nonRootPartitionPrefix) {
suffixOff := strings.LastIndex(rpcName, partitionDelimiter)
if suffixOff <= len(nonRootPartitionPrefix) {
}
return "", 0, serviceerror.NewInvalidArgument("task queue family name cannot have prefix /_sys/ " + baseName)
}
}
namespaceReplicationInducingRateBurstFn quotas.RateBurst,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
mapping := make(map[string]quotas.RequestRateLimiter)
executionRateLimiter := NewExecutionPriorityRateLimiter(executionRateBurstFn, operatorRPSRatio)
visibilityRateLimiter := NewVisibilityPriorityRateLimiter(visibilityRateBurstFn, operatorRPSRatio)
namespaceReplicationInducingRateLimiter := NewNamespaceReplicationInducingAPIPriorityRateLimiter(namespaceReplicationInducingRateBurstFn, operatorRPSRatio)
for api := range APIToPriority {
mapping[api] = executionRateLimiter
}
for api := range VisibilityAPIToPriority {
mapping[api] = visibilityRateLimiter
}
for api := range NamespaceReplicationInducingAPIToPriority {
mapping[api] = namespaceReplicationInducingRateLimiter
}
}
rateBurstFn quotas.RateBurst,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewPriorityRateLimiterHelper(
rateBurstFn,
operatorRPSRatio,
func(req quotas.Request) int {
return quotas.OperatorPriority
}
}
return ExecutionAPIPrioritiesOrdered[len(ExecutionAPIPrioritiesOrdered)-1]
},
rateBurstFn quotas.RateBurst,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewPriorityRateLimiterHelper(
rateBurstFn,
operatorRPSRatio,
func(req quotas.Request) int {
if req.CallerType == headers.CallerTypeOperator {
return quotas.OperatorPriority
rateBurstFn quotas.RateBurst,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewPriorityRateLimiterHelper(
rateBurstFn,
operatorRPSRatio,
func(req quotas.Request) int {
if req.CallerType == headers.CallerTypeOperator {
return quotas.OperatorPriority
globalQuotaBurstRatio dynamicconfig.FloatPropertyFnWithNamespaceFilter,
logger log.Logger,
rateFn := calculator.NewLoggedNamespaceCalculator(
calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: memberCounter,
PerInstanceQuota: func(ns string) int { return 0 },
GlobalQuota: globalQuota,
},
).GetQuota
func(req quotas.Request) quotas.RequestRateLimiter {
return quotas.NewRequestRateLimiterAdapter(
quotas.NewDynamicRateLimiter(
}
if _, ok := operationExcludedAPIs[apiFullName]; ok {
return false
}
_, inNamespaceReplicationInducingAPI := NamespaceReplicationInducingAPIToPriority[apiFullName]
return inAPI || inNamespaceReplicationInducingAPI
}
logger log.Logger,
metricsHandler metrics.Handler,
return &reschedulerImpl{
scheduler: scheduler,
timeSource: timeSource,
logger: logger,
metricsHandler: metricsHandler,
status: common.DaemonStatusInitialized,
shutdownCh: make(chan struct{}),
timerGate: timer.NewLocalGate(timeSource),
taskChannelKeyFn: scheduler.TaskChannelKeyFn(),
pqMap: make(map[TaskChannelKey]collection.Queue[rescheduledExecuable]),
}
}
if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
go r.rescheduleLoop()
r.logger.Info("Task rescheduler started.", tag.LifeCycleStarted)
}
if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
r.timerGate.Close()
if success := common.AwaitWaitGroup(&r.shutdownWG, time.Minute); !success {
r.logger.Warn("Task rescheduler timedout on shutdown.", tag.LifeCycleStopTimedout)
}
}
}
defer r.shutdownWG.Done()
cleanupTimer := time.NewTimer(backoff.Jitter(
reschedulerPQCleanupDuration,
reschedulerPQCleanupJitterCoefficient,
))
defer cleanupTimer.Stop()
for {
select {
r.drain()
return
case <-r.timerGate.FireCh():
r.reschedule()
}
r.Lock()
defer r.Unlock()
for key, pq := range r.pqMap {
for !pq.IsEmpty() {
pq.Remove()
shardStore ShardStore,
serializer serialization.Serializer,
return &shardManagerImpl{
shardStore: shardStore,
serializer: serializer,
}
}
m.shardStore.Close()
}
func (m *shardManagerImpl) GetName() string {
ctx context.Context,
request *GetOrCreateShardRequest,
createShardInfo := func() (int64, *commonpb.DataBlob, error) {
if shardInfo == nil {
}
shardInfo.UpdateTime = timestamp.TimeNowPtrUtc()
data, err := m.serializer.ShardInfoToBlob(shardInfo)
if err != nil {
return 0, nil, err
}
}
internalResp, err := m.shardStore.GetOrCreateShard(ctx, &InternalGetOrCreateShardRequest{
shard_manager.go
ShardID: request.ShardID,
CreateShardInfo: createShardInfo,
LifecycleContext: request.LifecycleContext,
})
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
ShardInfo: shardInfo,
}, nil
}
ctx context.Context,
request *UpdateShardRequest,
shardInfo := request.ShardInfo
shardInfo.UpdateTime = timestamp.TimeNowPtrUtc()
shardInfoBlob, err := m.serializer.ShardInfoToBlob(shardInfo)
if err != nil {
return err
}
ShardID: request.ShardInfo.GetShardId(),
RangeID: request.ShardInfo.GetRangeId(),
Owner: request.ShardInfo.GetOwner(),
ShardInfo: shardInfoBlob,
PreviousRangeID: request.PreviousRangeID,
}
return m.shardStore.UpdateShard(ctx, internalRequest)
}
ctx context.Context,
request *AssertShardOwnershipRequest,
return m.shardStore.AssertShardOwnership(ctx, request)
}
tx *sqlx.Tx,
logger log.Logger,
mdb := &db{
dbKind: dbKind,
dbName: dbName,
onClose: make([]func(), 0),
db: xdb,
tx: tx,
logger: logger,
}
mdb.conn = xdb
if tx != nil {
}
return mdb
}
// BeginTx starts a new transaction and returns a reference to the Tx object
xtx, err := mdb.db.BeginTxx(ctx, nil)
if err != nil {
return nil, err
}
}
// Commit commits a previously started transaction
return mdb.tx.Commit()
}
// Rollback triggers rollback of a previously started transaction
}
mdb.mu.Lock()
mdb.onClose = append(mdb.onClose, hook)
mdb.mu.Unlock()
}
// Close closes the connection to the sqlite db
mdb.mu.RLock()
defer mdb.mu.RUnlock()
for _, hook := range mdb.onClose {
hook()
}
// database connection will be automatically closed by the hook handler when all references are removed
}
// PluginName returns the name of the plugin
return PluginName
}
// DbName returns the name of the database
return mdb.dbName
}
// ExpectedVersion returns expected version.
// VerifyVersion verify schema version is up to date
return nil
// TODO(jlegrone): implement this
// expectedVersion := mdb.ExpectedVersion()
// return schema.VerifyCompatibleVersion(mdb, mdb.dbName, expectedVersion)
}
serviceResolver membership.ServiceResolver,
logger log.Logger,
return quotas.NewRequestRateLimiterAdapter(
quotas.NewDefaultOutgoingRateLimiter(
calculator.NewLoggedCalculator(
calculator.ClusterAwareQuotaCalculator{
MemberCounter: serviceResolver,
PerInstanceQuota: dynamicconfig.AdminBatcherHostRPS.Get(dc),
GlobalQuota: dynamicconfig.AdminBatcherGlobalRPS.Get(dc),
},
log.With(logger, tag.ComponentAdminBatcher, tag.ScopeHost),
).GetQuota,
),
)
}
func NewResult(
dc *dynamicconfig.Collection,
params activityDeps,
return fxResult{
Component: &workerComponent{
activityDeps: params,
dc: dc,
enabledFeature: dynamicconfig.EnableBatcherNamespace.Get(dc),
},
}
}
func (s *workerComponent) DedicatedWorkerOptions(ns *namespace.Namespace) *workercommon.PerNSDedicatedWorkerOptions {
fx.go
namespaceName := ns.Name().String()
enableFeature := s.enabledFeature(namespaceName)
return &workercommon.PerNSDedicatedWorkerOptions{
Enabled: enableFeature,
}
}
func (s *workerComponent) Register(registry sdkworker.Registry, ns *namespace.Namespace, _ workercommon.RegistrationDetails) func() {
fx.go
// Register the batch workflow with both the proto-qualified and unqualified types.
// TODO(spkane31): Remove the proto-qualified type and call the unqualified type from the frontend after the 1.30 release.
registry.RegisterWorkflowWithOptions(BatchWorkflowProtobuf, workflow.RegisterOptions{Name: BatchWFTypeName})
// Newer version of the batch workflow which was rewritten to accept a proto struct as input.
registry.RegisterWorkflowWithOptions(BatchWorkflowProtobuf, workflow.RegisterOptions{Name: BatchWFTypeProtobufName})
registry.RegisterActivity(s.activities(ns.Name(), ns.ID()))
return nil
}
func (s *workerComponent) activities(name namespace.Name, id namespace.ID) *activities {
fx.go
return &activities{
activityDeps: s.activityDeps,
namespace: name,
namespaceID: id,
rps: dynamicconfig.BatcherRPS.Get(s.dc),
concurrency: dynamicconfig.BatcherConcurrency.Get(s.dc),
}
}
)
// This line cannot be combined with others!
// This ensures the stack trace has information of the caller.
def := newMetricDefinition(name, opts...)
globalRegistry.register(def)
return timerDefinition{def}
}
// This line cannot be combined with others!
// This ensures the stack trace has information of the caller.
def := newMetricDefinition(name, append(opts, WithUnit(Bytes))...)
globalRegistry.register(def)
return histogramDefinition{def}
}
// This line cannot be combined with others!
// This ensures the stack trace has information of the caller.
def := newMetricDefinition(name, append(opts, WithUnit(Dimensionless))...)
globalRegistry.register(def)
return histogramDefinition{def}
}
// This line cannot be combined with others!
// This ensures the stack trace has information of the caller.
def := newMetricDefinition(name, opts...)
globalRegistry.register(def)
return counterDefinition{def}
}
// This line cannot be combined with others!
// This ensures the stack trace has information of the caller.
def := newMetricDefinition(name, opts...)
globalRegistry.register(def)
return gaugeDefinition{def}
}
return handler.Histogram(d.name, d.unit)
}
return handler.Counter(d.name)
}
return handler.Gauge(d.name)
}
return handler.Timer(d.name)
}
rateBurstFn RateBurst,
refreshInterval time.Duration,
rateLimiter := &DynamicRateLimiterImpl{
rateBurstFn: rateBurstFn,
refreshInterval: refreshInterval,
refreshTimer: time.NewTimer(refreshInterval),
rateLimiter: NewRateLimiter(rateBurstFn.Rate(), rateBurstFn.Burst()),
}
return rateLimiter
}
// NewDefaultIncomingRateLimiter returns a default rate limiter
func NewDefaultIncomingRateLimiter(
rateFn RateFn,
return NewDynamicRateLimiter(
NewDefaultIncomingRateBurst(rateFn),
defaultRefreshInterval,
)
}
// NewDefaultOutgoingRateLimiter returns a default rate limiter
func NewDefaultOutgoingRateLimiter(
rateFn RateFn,
return NewDynamicRateLimiter(
NewDefaultOutgoingRateBurst(rateFn),
defaultRefreshInterval,
)
}
// NewDefaultRateLimiter returns a default rate limiter with a dynamic burst ratio
rateFn RateFn,
burstRatioFn BurstRatioFn,
return NewDynamicRateLimiter(
NewDefaultRateBurst(rateFn, burstRatioFn),
defaultRefreshInterval,
)
}
// Allow immediately returns with true or false indicating if a rate limit
// token is available or not
d.maybeRefresh()
return d.rateLimiter.Allow()
}
// AllowN immediately returns with true or false indicating if n rate limit
// token is available or not
func (d *DynamicRateLimiterImpl) AllowN(now time.Time, numToken int) bool {
dynamic_rate_limiter_impl.go
d.maybeRefresh()
return d.rateLimiter.AllowN(now, numToken)
}
// Reserve reserves a rate limit token
d.maybeRefresh()
return d.rateLimiter.Reserve()
}
// ReserveN reserves n rate limit token
func (d *DynamicRateLimiterImpl) ReserveN(now time.Time, numToken int) Reservation {
dynamic_rate_limiter_impl.go
d.maybeRefresh()
return d.rateLimiter.ReserveN(now, numToken)
}
// Wait waits up till deadline for a rate limit token
}
select {
case <-d.refreshTimer.C:
d.refreshTimer.Reset(d.refreshInterval)
d.Refresh()
// noop
}
}
dbCustomSearchAttributeFieldNameRE = func() map[enumspb.IndexedValueType]*regexp.Regexp {
constants.go
res := map[enumspb.IndexedValueType]*regexp.Regexp{}
for t := range defaultNumDBCustomSearchAttributes {
res[t] = regexp.MustCompile(fmt.Sprintf(`^%s(0[1-9]|[1-9][0-9])$`, t.String()))
}
return res
}()
)
// System returns a clone of the system search attributes map.
return maps.Clone(system)
}
// Predefined returns a clone of the predefined search attributes map.
return maps.Clone(predefined)
}
// PredefinedWhiteList returns a clone of the predefined whitelist search attributes map.
return maps.Clone(predefinedWhiteList)
}
// Reserved returns a clone of the reserved field names map.
// IsSystem returns true if name is system search attribute
_, ok := system[name]
return ok
}
// IsReserved returns true if name is system reserved and can't be used as custom search attribute name.
if _, ok := system[name]; ok {
return true
}
return true
}
return true
}
}
// IsChasmSystem returns true if name is a system search attribute used by CHASM
_, ok := chasmSystemSearchAttributes[name]
return ok
}
// IsChasmOverridableSystem returns true if name is a system search attribute whose dedicated
// visibility column a CHASM component may override with its own value. See
// chasmOverridableSystemSearchAttributes for the semantics and exclusions.
_, ok := chasmOverridableSystemSearchAttributes[name]
return ok
}
// ChasmOverridableSystem returns a clone of the CHASM-overridable system search attributes set.
// GetSqlDbColName maps system and reserved search attributes to column names for SQL tables.
// If the input is not a system or reserved search attribute, then it returns the input.
if fieldName, ok := sqlDbSystemNameToColName[name]; ok {
}
return name
}
func GetDBIndexSearchAttributes(
override map[enumspb.IndexedValueType]int,
csa := map[string]enumspb.IndexedValueType{}
for saType, defaultNumAttrs := range defaultNumDBCustomSearchAttributes {
numAttrs := defaultNumAttrs
if value, ok := override[saType]; ok {
numAttrs = value
}
csa[fmt.Sprintf("%s%02d", saType.String(), i+1)] = saType
}
}
CustomSearchAttributes: csa,
}
}
func NewHandler(
params HandlerParams,
handler := &Handler{
config: params.Config,
metricsHandler: params.MetricsHandler,
logger: params.Logger,
throttledLogger: params.ThrottledLogger,
engine: NewEngine(
params.TaskManager,
params.FairTaskManager,
params.HistoryClient,
params.MatchingRawClient, // Use non retry client inside matching
params.WorkerDeploymentClient,
params.Config,
params.Logger,
params.ThrottledLogger,
params.MetricsHandler,
params.NamespaceRegistry,
params.HostInfoProvider,
params.MatchingServiceResolver,
params.ClusterMetadata,
params.NamespaceReplicationQueue,
params.VisibilityManager,
params.NexusEndpointManager,
params.TestHooks,
params.SearchAttributeProvider,
params.SearchAttributeMapperProvider,
params.RateLimiter,
params.Serializer,
params.TaskHookFactories,
params.PartitionScalerFactory,
),
namespaceRegistry: params.NamespaceRegistry,
workersRegistry: params.WorkersRegistry,
}
// prevent from serving requests before matching engine is started and ready
handler.startWG.Add(1)
return handler
}
// Start starts the handler
h.engine.Start()
h.startWG.Done()
}
// Stop stops the handler
h.engine.Stop()
}
func (h *Handler) opMetricsHandler(
timeSource clock.TimeSource,
clusterMetadata cluster.Metadata,
dcRedirectionPolicy := RedirectionPolicyGenerator(
clusterMetadata,
enabledForNS,
selectedAPIsOnlyForNS,
namespaceCache,
policy,
)
return &Redirection{
currentClusterName: clusterMetadata.GetCurrentClusterName(),
redirectionPolicy: dcRedirectionPolicy,
namespaceCache: namespaceCache,
logger: logger,
clientBean: clientBean,
metricsHandler: metricsHandler,
timeSource: timeSource,
}
}
// WithRedirectResponses returns a copy of the interceptor that treats the given fullMethod ->
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
defer log.CapturePanic(i.logger, &retError)
if raFn, ok := i.redirectResponsesByFullMethod[info.FullMethod]; ok {
if !i.RedirectionAllowed(ctx) {
return handler(ctx, req)
}
return handler(ctx, req)
}
return handler(ctx, req)
}
if _, ok := localAPIResponses[methodName]; ok {
return i.handleLocalAPIInvocation(ctx, req, handler, methodName)
}
if raFn, ok := globalAPIResponses[methodName]; ok {
namespaceName, err := GetNamespaceName(i.namespaceCache, req)
handler grpc.UnaryHandler,
methodName string,
scope, startTime := i.BeforeCall(dcRedirectionMetricsPrefix + methodName)
defer func() {
i.AfterCall(scope, startTime, i.currentClusterName, "local", retError)
}()
return handler(ctx, req)
}
func (i *Redirection) BeforeCall(
operation string,
return i.metricsHandler.WithTags(metrics.OperationTag(operation), metrics.ServiceRoleTag(metrics.DCRedirectionRoleTagValue)), i.timeSource.Now()
}
func (i *Redirection) AfterCall(
namespaceName string,
retError error,
// Only emit redirection metrics when actual cross-cluster redirection occurred
if targetClusterName != i.currentClusterName {
metricsHandler = metricsHandler.WithTags(metrics.TargetClusterTag(targetClusterName))
metrics.ClientRedirectionLatency.With(metricsHandler).Record(i.timeSource.Now().Sub(startTime))
func (i *Redirection) RedirectionAllowed(
ctx context.Context,
// default to allow dc redirection
values := metadata.ValueFromIncomingContext(ctx, DCRedirectionContextHeaderName)
if len(values) == 0 {
}
allowed, err := strconv.ParseBool(values[0])
if err != nil {
logger log.Logger,
httpTraceProvider commonnexus.HTTPClientTraceProvider,
return &NexusOperationHTTPHandler{
base: nexusrpc.BaseHTTPHandler{
Logger: log.NewSlogLogger(logger),
FailureConverter: nexusrpc.DefaultFailureConverter(),
},
logger: logger,
enpointRegistry: endpointRegistry,
namespaceRegistry: namespaceRegistry,
auth: authInterceptor,
namespaceValidationInterceptor: namespaceValidationInterceptor,
namespaceRateLimitInterceptor: namespaceRateLimitInterceptor,
namespaceConcurrencyLimitInterceptor: namespaceConcurrencyLimitInterceptor,
rateLimitInterceptor: rateLimitInterceptor,
preprocessErrorCounter: metricsHandler.Counter(metrics.NexusRequestPreProcessErrors.Name()).Record,
nexusHandler: nexusrpc.NewHTTPHandler(nexusrpc.HandlerOptions{
Handler: &nexusHandler{
logger: logger,
metricsHandler: metricsHandler,
clusterMetadata: clusterMetadata,
namespaceRegistry: namespaceRegistry,
matchingClient: matchingservice.MatchingServiceClient(matchingClient),
auth: authInterceptor,
telemetryInterceptor: telemetryInterceptor,
requestErrorHandler: requestErrorHandler,
redirectionInterceptor: redirectionInterceptor,
forwardingEnabledForNamespace: serviceConfig.EnableNamespaceNotActiveAutoForwarding,
forwardingClients: clientCache,
payloadSizeLimit: serviceConfig.BlobSizeLimitError,
headersBlacklist: serviceConfig.NexusRequestHeadersBlacklist,
useForwardByEndpoint: serviceConfig.NexusForwardRequestUseEndpoint,
metricTagConfig: serviceConfig.NexusOperationsMetricTagConfig,
httpTraceProvider: httpTraceProvider,
},
GetResultTimeout: serviceConfig.KeepAliveMaxConnectionIdle(),
Logger: log.NewSlogLogger(logger),
Serializer: commonnexus.PayloadSerializer,
}),
}
}
func (h *NexusOperationHTTPHandler) RegisterRoutes(r *mux.Router) {
nexus_operation_http_handler.go
r.PathPrefix("/" + commonnexus.RouteDispatchNexusTaskByNamespaceAndTaskQueue.Representation() + "/").
HandlerFunc(h.dispatchNexusTaskByNamespaceAndTaskQueue)
r.PathPrefix("/" + commonnexus.RouteDispatchNexusTaskByEndpoint.Representation() + "/").
HandlerFunc(h.dispatchNexusTaskByEndpoint)
}
func (h *NexusOperationHTTPHandler) writeFailure(writer http.ResponseWriter, r *http.Request, err error) {
maxPendingKeysFn func() int,
metricsHandler metrics.Handler,
s := &SliceImpl{
paginationFnProvider: paginationFnProvider,
executableFactory: executableFactory,
scope: scope,
iterators: []Iterator{
NewIterator(paginationFnProvider, scope.Range),
},
executableTracker: newExecutableTracker(grouper),
monitor: monitor,
maxPredicateSizeFn: maxPredicateSizeFn,
maxPendingKeysFn: maxPendingKeysFn,
metricsHandler: metricsHandler,
}
s.ensurePredicateSizeLimit()
return s
}
s.stateSanityCheck()
return s.scope
}
func (s *SliceImpl) CanSplitByRange(key tasks.Key) bool {
}
func (s *SliceImpl) SelectTasks(readerID int64, batchSize int) ([]Executable, error) {
slice.go
s.stateSanityCheck()
if len(s.iterators) == 0 {
return []Executable{}, nil
}
s.monitor.SetSlicePendingTaskCount(s, len(s.pendingExecutables))
}()
for len(executables) < batchSize && len(s.iterators) != 0 {
if s.iterators[0].HasNext() {
task, err := s.iterators[0].Next()
if err != nil {
s.add(executable)
executables = append(executables, executable)
s.iterators = s.iterators[1:]
}
}
}
s.stateSanityCheck()
return len(s.iterators) != 0
}
func (s *SliceImpl) TaskStats() TaskStats {
}
if s.destroyed {
panic("Can not invoke method on destroyed queue slice")
}
}
maxPredicateSize := s.maxPredicateSizeFn()
// 0 == unlimited
if maxPredicateSize > 0 && s.scope.Predicate.Size() > maxPredicateSize {
// Due to the limitations in predicate merging logic, the predicate size can easily grow unbounded.
// The simplest mitigation is to stop merging and replace with the univeral predicate.
endpointsRefreshInterval dynamicconfig.DurationPropertyFn,
persistence p.NexusEndpointManager,
return &nexusEndpointClient{
endpointsRefreshInterval: endpointsRefreshInterval,
persistence: persistence,
tableVersionChanged: make(chan struct{}),
}
}
func (m *nexusEndpointClient) CreateNexusEndpoint(
// notifyOwnershipChanged starts or stops a background routine which watches the Nexus endpoints table version for
// changes. This is only expected to be called from matchingEngineImpl.notifyNexusEndpointsOwnershipChange()
var oldHandle *goro.Handle
m.refreshLock.Lock()
if isOwner && m.refreshHandle == nil {
// Just acquired ownership. Start refresh loop on table version to catch any updates from previous owner.
nexus_endpoint_client.go
backgroundCtx := headers.SetCallerInfo(
context.Background(),
headers.SystemBackgroundHighCallerInfo,
)
m.refreshHandle = goro.NewHandle(backgroundCtx)
m.refreshHandle.Go(m.refreshTableVersion)
oldHandle = m.refreshHandle
m.refreshHandle = nil
}
if oldHandle != nil {
<-oldHandle.Done()
}
}
func (m *nexusEndpointClient) refreshTableVersion(ctx context.Context) error {
nexus_endpoint_client.go
for ctx.Err() == nil {
util.InterruptibleSleep(ctx, backoff.Jitter(m.endpointsRefreshInterval(), 0.2))
}
}
func (m *nexusEndpointClient) checkTableVersion(ctx context.Context) {
nexus_endpoint_client.go
// Acquire lock to make sure we are not in the middle of an update.
m.Lock()
defer m.Unlock()
resp, err := m.persistence.ListNexusEndpoints(ctx, &p.ListNexusEndpointsRequest{
LastKnownTableVersion: 0,
PageSize: 0,
})
if err != nil || resp.TableVersion != m.tableVersion {
m.hasLoadedEndpoints.Store(false)
ch := m.tableVersionChanged
specBuilder *SpecBuilder,
params activityDeps,
return fxResult{
Component: &workerComponent{
specBuilder: specBuilder,
activityDeps: params,
enabledForNs: dynamicconfig.WorkerEnableScheduler.Get(dc),
enableCHASMMigration: dynamicconfig.EnableCHASMSchedulerMigration.Get(dc),
chasmMigrationRolloutPercent: dynamicconfig.CHASMSchedulerMigrationRolloutPercent.Get(dc),
migrateWithRunningWorkflows: dynamicconfig.EnableCHASMSchedulerMigrationWithRunningWorkflows.Get(dc),
globalNSStartWorkflowRPS: dynamicconfig.SchedulerNamespaceStartWorkflowRPS.Subscribe(dc),
maxBlobSize: dynamicconfig.BlobSizeLimitError.Get(dc),
localActivitySleepLimit: dynamicconfig.SchedulerLocalActivitySleepLimit.Get(dc),
},
}
}
func (s *workerComponent) DedicatedWorkerOptions(ns *namespace.Namespace) *workercommon.PerNSDedicatedWorkerOptions {
fx.go
return &workercommon.PerNSDedicatedWorkerOptions{
Enabled: s.enabledForNs(ns.Name().String()),
}
}
func (s *workerComponent) Register(registry sdkworker.Registry, ns *namespace.Namespace, details workercommon.RegistrationDetails) func() {
fx.go
nsName := ns.Name().String()
wfFunc := func(ctx workflow.Context, args *schedulespb.StartScheduleArgs) error {
key := fmt.Appendf(nil, "%s\x00%s", nsName, args.State.ScheduleId)
enableMigration := func() bool {
return schedulerWorkflowWithSpecBuilder(ctx, args, s.specBuilder, enableMigration, migrateWithRunningWorkflows)
}
registry.RegisterWorkflowWithOptions(wfFunc, workflow.RegisterOptions{Name: WorkflowType})
fx.go
activities, cleanup := s.newActivities(ns.Name(), ns.ID(), details)
registry.RegisterActivity(activities)
return cleanup
}
func (s *workerComponent) newActivities(name namespace.Name, id namespace.ID, details workercommon.RegistrationDetails) (*activities, func()) {
fx.go
const burstRatio = 1.0
lim := quotas.NewRateLimiter(1, 1)
cb := func(rps float64) {
localRPS := rps * float64(details.Multiplicity) / float64(details.TotalWorkers)
burst := max(1, int(math.Ceil(localRPS*burstRatio)))
lim.SetRateBurst(localRPS, burst)
}
initialRPS, cancel := s.globalNSStartWorkflowRPS(name.String(), cb)
cb(initialRPS)
return &activities{
activityDeps: s.activityDeps,
namespace: name,
namespaceID: id,
startWorkflowRateLimiter: lim,
maxBlobSize: func() int { return s.maxBlobSize(name.String()) },
localActivitySleepLimit: func() time.Duration { return s.localActivitySleepLimit(name.String()) },
}, cancel
}
func init() { file_temporal_server_api_historyservice_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_api_historyservice_v1_request_response_proto_init() {
if File_temporal_server_api_historyservice_v1_request_response_proto != nil {
return
}
file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[107].OneofWrappers = []any{
(*StreamWorkflowReplicationMessagesRequest_SyncReplicationState)(nil),
}
file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[108].OneofWrappers = []any{
(*StreamWorkflowReplicationMessagesResponse_Messages)(nil),
}
file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[134].OneofWrappers = []any{
(*CompleteNexusOperationChasmRequest_Success)(nil),
(*CompleteNexusOperationChasmRequest_Failure)(nil),
}
file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[136].OneofWrappers = []any{
(*CompleteNexusOperationRequest_Success)(nil),
(*CompleteNexusOperationRequest_Failure)(nil),
}
file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[162].OneofWrappers = []any{
(*ExecuteMultiOperationRequest_Operation_StartWorkflow)(nil),
(*ExecuteMultiOperationRequest_Operation_UpdateWorkflow)(nil),
}
file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes[163].OneofWrappers = []any{
(*ExecuteMultiOperationResponse_Response_StartWorkflow)(nil),
(*ExecuteMultiOperationResponse_Response_UpdateWorkflow)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc), len(file_temporal_server_api_historyservice_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 171,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_temporal_server_api_historyservice_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_api_historyservice_v1_request_response_proto_msgTypes,
ExtensionInfos: file_temporal_server_api_historyservice_v1_request_response_proto_extTypes,
}.Build()
File_temporal_server_api_historyservice_v1_request_response_proto = out.File
file_temporal_server_api_historyservice_v1_request_response_proto_goTypes = nil
file_temporal_server_api_historyservice_v1_request_response_proto_depIdxs = nil
}
)
var value T
return &FutureImpl[T]{
status: pending,
readyCh: make(chan struct{}),
value: value,
err: nil,
}
}
func (f *FutureImpl[T]) Get(
ctx context.Context,
if f.Ready() {
}
return f.value, f.err
case <-ctx.Done():
var value T
value T,
err error,
// cannot directly set status to `ready`, to prevent data race in case multiple `Get` occurs
// instead set status to `setting` to prevent concurrent completion of this future
if !atomic.CompareAndSwapInt32(
&f.status,
pending,
setting,
) {
panic("future has already been completed")
}
f.err = err
atomic.CompareAndSwapInt32(&f.status, setting, ready)
close(f.readyCh)
}
value T,
err error,
if !atomic.CompareAndSwapInt32(
&f.status,
pending,
setting,
) {
}
f.err = err
atomic.CompareAndSwapInt32(&f.status, setting, ready)
close(f.readyCh)
return true
}
return atomic.LoadInt32(&f.status) == ready
}
transactionSizeLimit dynamicconfig.IntPropertyFn,
enableBestEffortDeleteTasksOnWorkflowUpdate dynamicconfig.BoolPropertyFn,
return &executionManagerImpl{
serializer: serializer,
eventBlobCache: eventBlobCache,
persistence: persistence,
logger: logger,
pagingTokenSerializer: newJSONHistoryTokenSerializer(),
transactionSizeLimit: transactionSizeLimit,
enableBestEffortDeleteTasksOnWorkflowUpdate: enableBestEffortDeleteTasksOnWorkflowUpdate,
}
}
return m.persistence.GetName()
}
func (m *executionManagerImpl) GetHistoryBranchUtil() HistoryBranchUtil {
ctx context.Context,
request *GetHistoryTasksRequest,
if err := validateTaskRange(
request.TaskCategory.Type(),
request.InclusiveMinTaskKey,
request.ExclusiveMaxTaskKey,
); err != nil {
return nil, err
}
if err != nil {
return nil, err
}
for _, internalTask := range resp.Tasks {
task, err := m.serializer.DeserializeTask(request.TaskCategory, internalTask.Blob)
if err != nil {
}
Tasks: historyTasks,
NextPageToken: resp.NextPageToken,
}, nil
}
}
m.persistence.Close()
}
func (m *executionManagerImpl) trimHistoryNode(
minTaskKey tasks.Key,
maxTaskKey tasks.Key,
minTaskIDSpecified := minTaskKey.TaskID != 0
minFireTimeSpecified := !minTaskKey.FireTime.IsZero() && !minTaskKey.FireTime.Equal(tasks.DefaultFireTime)
maxTaskIDSpecified := maxTaskKey.TaskID != 0
maxFireTimeSpecified := !maxTaskKey.FireTime.IsZero() && !maxTaskKey.FireTime.Equal(tasks.DefaultFireTime)
switch taskCategoryType {
case tasks.CategoryTypeImmediate:
if !maxTaskIDSpecified {
return serviceerror.NewInvalidArgument("invalid task range, max taskID must be specified for immediate task category")
}
return serviceerror.NewInvalidArgument("invalid task range, fireTime must be empty for immediate task category")
}
if !maxFireTimeSpecified {
return serviceerror.NewInvalidArgument("invalid task range, max fire time must be specified for scheduled task category")
}
return serviceerror.NewInvalidArgument("invalid task range, taskID must be empty for scheduled task category")
}
)
return &serializerImpl{encodingType: encodingTypeFromEnv()}
}
func (t *serializerImpl) EncodingType() enumspb.EncodingType {
}
func (t *serializerImpl) SerializeClusterMetadata(cm *persistencespb.ClusterMetadata) (*commonpb.DataBlob, error) {
serializer.go
if cm == nil {
cm = &persistencespb.ClusterMetadata{}
}
}
func (t *serializerImpl) DeserializeClusterMetadata(data *commonpb.DataBlob) (*persistencespb.ClusterMetadata, error) {
serializer.go
if data == nil {
return nil, nil
}
return nil, nil
}
err := Decode(data, cm)
if err != nil {
return nil, err
}
}
func (t *serializerImpl) serialize(p proto.Message) (*commonpb.DataBlob, error) {
serializer.go
if p == nil {
return nil, nil
}
if err != nil {
return nil, NewSerializationError(t.encodingType, err)
}
}
func (e *DeserializationError) IsTerminalTaskError() bool { return true }
func (t *serializerImpl) ShardInfoToBlob(info *persistencespb.ShardInfo) (*commonpb.DataBlob, error) {
serializer.go
return encodeBlob(info, t.encodingType)
}
func (t *serializerImpl) ShardInfoFromBlob(data *commonpb.DataBlob) (*persistencespb.ShardInfo, error) {
serializer.go
shardInfo := &persistencespb.ShardInfo{}
err := Decode(data, shardInfo)
if err != nil {
return nil, err
}
}
}
if queueState.ReaderStates == nil {
queueState.ReaderStates = make(map[int64]*persistencespb.QueueReaderState)
}
}
func (t *serializerImpl) NamespaceDetailToBlob(info *persistencespb.NamespaceDetail) (*commonpb.DataBlob, error) {
serializer.go
return encodeBlob(info, t.encodingType)
}
func (t *serializerImpl) NamespaceDetailFromBlob(data *commonpb.DataBlob) (*persistencespb.NamespaceDetail, error) {
serializer.go
result := &persistencespb.NamespaceDetail{}
return result, Decode(data, result)
}
func (t *serializerImpl) HistoryTreeInfoToBlob(info *persistencespb.HistoryTreeInfo) (*commonpb.DataBlob, error) {
}
func (t *serializerImpl) QueueMetadataToBlob(metadata *persistencespb.QueueMetadata) (*commonpb.DataBlob, error) {
serializer.go
// TODO change ENCODING_TYPE_JSON to ENCODING_TYPE_PROTO3
return encodeBlob(metadata, enumspb.ENCODING_TYPE_JSON)
}
func (t *serializerImpl) QueueMetadataFromBlob(data *commonpb.DataBlob) (*persistencespb.QueueMetadata, error) {
rateLimiterGenFn RequestRateLimiterFn,
rateLimiterKeyFn RequestRateLimiterKeyFn[K],
return &MapRequestRateLimiterImpl[K]{
rateLimiterGenFn: rateLimiterGenFn,
rateLimiterKeyFn: rateLimiterKeyFn,
rateLimiters: make(map[K]*rateLimiterEntry),
ttlNano: int64(rateLimiterTTL),
cleanupTicker: time.NewTicker(rateLimiterCleanupInterval),
}
}
return req.Caller
}
func NewNamespaceRequestRateLimiter(
rateLimiterGenFn RequestRateLimiterFn,
return NewMapRequestRateLimiter(rateLimiterGenFn, namespaceRequestRateLimiterKeyFn)
}
// Allow attempts to allow a request to go through. The method returns
now time.Time,
request Request,
rateLimiter := r.getOrInitRateLimiter(now, request)
return rateLimiter.Allow(now, request)
}
// Reserve returns a Reservation that indicates how long the caller
now time.Time,
req Request,
r.maybeCleanup(now)
key := r.rateLimiterKeyFn(req)
nowNano := now.UnixNano()
r.RLock()
entry, ok := r.rateLimiters[key]
r.RUnlock()
if ok {
return entry.rateLimiter
}
r.Lock()
defer r.Unlock()
if entry, ok := r.rateLimiters[key]; ok {
entry.lastAccess.Store(nowNano)
return entry.rateLimiter
}
entry.lastAccess.Store(nowNano)
r.rateLimiters[key] = entry
return newRateLimiter
}
// receive drains at most one ticker tick, so only one sweeper starts even if many
// callers reach here at once.
func (r *MapRequestRateLimiterImpl[K]) maybeCleanup(now time.Time) {
map_request_rate_limiter_impl.go
select {
case <-r.cleanupTicker.C:
go r.cleanup(now)
}
}
rateFn RateFn,
burstFn BurstFn,
return &RateBurstImpl{
rateFn: rateFn,
burstFn: burstFn,
}
}
func NewDefaultIncomingRateBurst(
rateFn RateFn,
return NewDefaultRateBurst(rateFn, func() float64 {
})
}
func NewDefaultOutgoingRateBurst(
rateFn RateFn,
return NewDefaultRateBurst(rateFn, func() float64 {
return defaultOutgoingRateBurstRatio
})
}
rateFn RateFn,
rateToBurstRatio BurstRatioFn,
burstFn := func() int {
if rate < 0 {
rate = 0
}
if ratio < 0 {
ratio = 0
}
if burst == 0 && rate > 0 && ratio > 0 {
}
}
}
return d.rateFn()
}
return d.burstFn()
}
func NewMutableRateBurst(
baseRateBurstFn RateBurst,
operatorRateRatio func() float64,
return &OperatorRateBurstImpl{
operatorRateRatio: operatorRateRatio,
baseRateBurstFn: baseRateBurstFn,
}
}
return c.operatorRateRatio() * c.baseRateBurstFn.Rate()
}
return c.baseRateBurstFn.Burst()
}
}
func init() { file_temporal_server_api_matchingservice_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_api_matchingservice_v1_request_response_proto_init() {
if File_temporal_server_api_matchingservice_v1_request_response_proto != nil {
return
}
file_temporal_server_api_matchingservice_v1_request_response_proto_msgTypes[27].OneofWrappers = []any{
(*UpdateWorkerBuildIdCompatibilityRequest_ApplyPublicRequest_)(nil),
(*UpdateWorkerBuildIdCompatibilityRequest_RemoveBuildIds_)(nil),
(*UpdateWorkerBuildIdCompatibilityRequest_PersistUnknownBuildId)(nil),
}
file_temporal_server_api_matchingservice_v1_request_response_proto_msgTypes[29].OneofWrappers = []any{
(*GetWorkerVersioningRulesRequest_Request)(nil),
}
file_temporal_server_api_matchingservice_v1_request_response_proto_msgTypes[31].OneofWrappers = []any{
(*UpdateWorkerVersioningRulesRequest_Request)(nil),
}
file_temporal_server_api_matchingservice_v1_request_response_proto_msgTypes[37].OneofWrappers = []any{
(*SyncDeploymentUserDataRequest_UpdateVersionData)(nil),
(*SyncDeploymentUserDataRequest_ForgetVersion)(nil),
}
file_temporal_server_api_matchingservice_v1_request_response_proto_msgTypes[56].OneofWrappers = []any{
(*DispatchNexusTaskResponse_HandlerError)(nil),
(*DispatchNexusTaskResponse_Response)(nil),
(*DispatchNexusTaskResponse_RequestTimeout)(nil),
(*DispatchNexusTaskResponse_Failure)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_matchingservice_v1_request_response_proto_rawDesc), len(file_temporal_server_api_matchingservice_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 97,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_matchingservice_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_api_matchingservice_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_api_matchingservice_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_api_matchingservice_v1_request_response_proto = out.File
file_temporal_server_api_matchingservice_v1_request_response_proto_goTypes = nil
file_temporal_server_api_matchingservice_v1_request_response_proto_depIdxs = nil
}
// dual emit the metric with the all tag. If a blank namespace is provided then
// this converts that to an unknown namespace.
if len(value) == 0 {
}
}
// NamespaceUnknownTag returns a new namespace:unknown tag-value
return namespaceUnknownTag
}
// NamespaceStateTag returns a new namespace state tag.
}
if value == "" {
value = unknownValue
}
}
if value == "" {
value = unknownValue
}
}
}
return Tag{Key: ErrorTypeTagName, Value: strings.TrimPrefix(util.ErrorType(err), errorPrefix)}
}
func OutcomeTag(outcome string) Tag {
}
return Tag{Key: serviceName, Value: string(value)}
}
func ActionType(value string) Tag {
}
return Tag{Key: OperationTagName, Value: value}
}
return Tag{Key: key, Value: value}
}
return Tag{Key: CacheTypeTagName, Value: value}
}
return Tag{Key: PriorityTagName, Value: strconv.Itoa(int(value))}
}
// ReasonString is just a string but the special type is defined here to remind callers of ReasonTag to limit the
// NewRoute returns a new [Route] instance with the given components.
return Route[T]{components: components}
}
// RouteBuilder is a builder for the [Route] interface.
// NewBuilder creates a new [RouteBuilder] instance, which can be used to define a new [Route] via a fluent API.
return &RouteBuilder[T]{}
}
// With adds a series of [Component] instances to the [Route].
r.components = append(r.components, c...)
return r
}
// Constant adds a [Constant] component to the [Route].
return r.With(Constant[T](values...))
}
// StringVariable adds a [StringVariable] component to the [Route].
func (r *RouteBuilder[T]) StringVariable(name string, getter func(*T) *string) *RouteBuilder[T] {
route.go
return r.With(StringVariable[T](name, getter))
}
// Build returns a read-only [Route].
return NewRoute[T](r.components...)
}
// Representation returns the [github.com/gorilla/mux] compatible string representation of the route for usage in a
// they're present in the components. We do this because it's easier to add a slash depending on the context than to
// remove it.
return r.serialize(func(c Component[T]) string {
return c.Representation()
})
}
// Constant returns a [Component] that represents a series of constant HTTP path components in a Route.
// They will be joined via strings when used to construct a path or path representation.
return values
}
type constant[T any] []string
return strings.Join(s, "/")
}
func (s constant[T]) Serialize(T) string {
// StringVariable returns a [Component] that represents a string variable in a Route.
return stringVariable[T]{name, getter}
}
type stringVariable[T any] struct {
}
return "{" + s.name + "}"
}
func (s stringVariable[T]) Serialize(t T) string {
// unmanagedFieldsOf yields all non-CHASM managed fields of a struct.
return func(yield func(fi fieldInfo) bool) {
if valueT.Kind() == reflect.Pointer {
}
fieldT := field.Type
if fieldT == UnimplementedComponentT {
}
// Skip the data field, which is always CHASM-managed.
}
prefix := genericTypePrefix(fieldT)
switch prefix {
case chasmFieldTypePrefix,
chasmMapTypePrefix,
chasmMSPointerType,
continue // Skip CHASM fields.
if !yield(fieldInfo{typ: fieldT, name: fieldN}) {
return
}
}
tn := t.String()
if tn == chasmMSPointerType {
}
if bracketPos == -1 {
}
}
if tagName := f.Tag.Get(fieldNameTag); tagName != "" {
return tagName
}
}
// This is used at registration time to validate that archetypes using Visibility
// have configured a businessID alias.
if componentT.Kind() == reflect.Pointer {
}
return false
}
fieldT := field.Type
if fieldT == visibilityFieldT {
}
}
}
logger log.Logger,
metricsHandler metrics.Handler,
nextTaskTimer := time.NewTimer(0)
if !nextTaskTimer.Stop() {
<-nextTaskTimer.C
}
taskQueue: collection.NewPriorityQueue[Executable](executableVisibilityTimeCompareLess),
nextTaskTimer: nextTaskTimer,
newTaskCh: make(chan Executable),
timeSource: timeSource,
logger: logger,
metricsHandler: metricsHandler,
status: common.DaemonStatusInitialized,
shutdownCh: make(chan struct{}),
scheduler: scheduler,
}
}
}
if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
defer q.logger.Info("", tag.LifeCycleStarted)
q.shutdownWG.Add(1)
go q.processQueueLoop()
}
if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
defer q.logger.Info("", tag.LifeCycleStopped)
close(q.shutdownCh)
if success := common.AwaitWaitGroup(&q.shutdownWG, time.Minute); !success {
q.logger.Warn("", tag.LifeCycleStopTimedout)
}
//nolint:revive // cognitive complexity
defer q.shutdownWG.Done()
for {
select {
case <-q.shutdownCh:
return
}
return
case newTask := <-q.newTaskCh:
var nextTaskTime time.Time
)
return &bucket{
namespaces: make(map[namespace.ID]*nsEntries),
order: list.New(),
}
}
// upsertHeartbeats inserts or refreshes a WorkerHeartbeat under the given namespace.
// NewRegistry creates a workers heartbeat registry with the given parameters.
m := newRegistryImpl(params)
lc.Append(fx.StartStopHook(m.Start, m.Stop))
return m
}
m := ®istryImpl{
buckets: make([]*bucket, params.NumBuckets()),
maxItemsFn: params.MaxItems,
ttlFn: params.TTL,
minEvictAgeFn: params.MinEvictAge,
evictionIntervalFn: params.EvictionInterval,
seed: maphash.MakeSeed(),
quit: make(chan struct{}),
metricsHandler: params.MetricsHandler,
metricsEmitter: &workerMetricsEmitter{
handler: params.MetricsHandler,
config: params.MetricsConfig,
},
}
for i := range m.buckets {
m.buckets[i] = newBucket()
}
return m
}
// evictLoop periodically triggers TTL and capacity-based eviction.
for {
select {
case <-time.After(m.evictionIntervalFn()):
m.evictByTTL()
m.recordUtilizationMetric()
m.recordWorkerCountMetric()
return
}
}
// Start begins the background eviction process.
go m.evictLoop()
}
// Stop halts background eviction.
close(m.quit)
}
func (m *registryImpl) RecordWorkerHeartbeats(nsID namespace.ID, nsName namespace.Name, principal *commonpb.Principal, workerHeartbeat []*workerpb.WorkerHeartbeat) {
logger log.Logger,
connectionCloseDelay dynamicconfig.DurationPropertyFn,
conns := &sync.Map{}
c := &connectionPoolImpl[C]{
conns: conns,
historyServiceResolver: historyServiceResolver,
rpcFactory: rpcFactory,
clientCtor: clientCtor,
logger: logger,
connectionCloseDelay: connectionCloseDelay,
}
// Close cached conns whose host leaves the membership ring.
c.watcher = goro.NewHandle(context.Background()).Go(c.watchMembership)
return c
}
// Close stops the watcher and closes all pooled connections.
if !c.closed.CompareAndSwap(false, true) {
return
}
<-c.watcher.Done()
// Set closed before reaping so a concurrent create can't re-cache a conn.
c.conns.Range(func(key, value any) bool {
c.conns.Delete(key)
if err := value.(clientConnection[C]).grpcConn.Close(); err != nil {
}
listenerName := fmt.Sprintf("%p", c.conns)
ch := make(chan *membership.ChangedEvent, 1)
if err := c.historyServiceResolver.AddListener(listenerName, ch); err != nil {
c.logger.Error("Failed to subscribe history connection pool to membership", tag.Error(err))
return err
}
// Reap departed hosts via a per-address deadline checked by a single ticker;
// a re-add resets it to the latest removal.
ticker := time.NewTicker(evictionCheckInterval)
defer ticker.Stop()
for {
select {
return nil
for _, h := range event.HostsRemoved {
}
delete(evictAt, rpcAddress(h.GetAddress()))
}
case <-ticker.C:
c.reapClosableConns(evictAt)
// maximum combined weight for concurrent access, capable of handling multiple priority levels.
// Most of the logic is taken directly from golang's semaphore.Weighted.
waitLists := make([]*list.List, NumPriorities)
for i := range waitLists {
waitLists[i] = list.New()
}
return &PrioritySemaphoreImpl{
size: n,
waitLists: waitLists,
}
}
// are available or ctx is done. On success, returns nil. On failure, returns
// ctx.Err() and leaves the semaphore unchanged.
func (s *PrioritySemaphoreImpl) Acquire(ctx context.Context, priority Priority, n int) error {
priority_semaphore_impl.go
if priority >= NumPriorities {
// nolint:forbidigo
panic(fmt.Sprintf("semaphore: invalid priority %v, priority must be less than %v", priority, NumPriorities))
}
s.mu.Lock()
select {
case <-done:
// ctx becoming done has "happened before" acquiring the semaphore,
s.mu.Unlock()
return ctx.Err()
}
// Check if acquisition can proceed without waiting
// Since we hold s.mu and haven't synchronized since checking done, if
priority_semaphore_impl.go
// ctx becomes done before we return here, it becoming done must have
// "happened concurrently" with this call - it cannot "happen before"
// we return in this branch. So, we're ok to always acquire here.
s.cur += n
s.mu.Unlock()
return nil
}
if n > s.size {
}
s.mu.Lock()
defer s.mu.Unlock()
s.cur -= n
if s.cur < 0 {
s.mu.Unlock()
panic("semaphore: released more than held")
}
}
for _, l := range s.waitLists {
for {
next := l.Front()
if next == nil {
break // No more waiters blocked.
}
// noWaiters returns if there is no waiter that has priority higher or equal to lowestPriority.
func (s *PrioritySemaphoreImpl) noWaiters(lowestPriority Priority) bool {
priority_semaphore_impl.go
for _, l := range s.waitLists[:lowestPriority+1] {
if l.Len() > 0 {
return false
}
}
}
resolver ReplicationResolver,
mutations ...Mutation,
if resolver == nil {
return nil, serviceerror.NewInvalidArgument("replicationResolver must be provided")
}
info: detail.Info,
config: detail.Config,
configVersion: detail.ConfigVersion,
customSearchAttributesMapper: CustomSearchAttributesMapper{
fieldToAlias: detail.Config.CustomSearchAttributeAliases,
aliasToField: util.InverseMap(detail.Config.CustomSearchAttributeAliases),
},
replicationResolver: resolver,
}
for _, m := range mutations {
}
}
// ID observes this namespace's permanent unique identifier in string form.
if ns.info == nil {
return ID("")
}
}
// Name observes this namespace's configured name.
if ns.info == nil {
return Name("")
}
}
if ns.info == nil {
return enumspb.NAMESPACE_STATE_UNSPECIFIED
}
}
// Being a global namespace doesn't necessarily mean that there are multiple registered clusters for it, only that it
// has a failover version. To determine whether operations should be replicated for a namespace, see ReplicationPolicy.
return ns.replicationResolver.IsGlobalNamespace()
}
// FailoverNotificationVersion return the global notification version of when failover happened
// Note: Do not use this to determine if a workflow is active in the cluster.
// Use ActiveClusterName(businessID) instead.
return ns.replicationResolver.ActiveInCluster(clusterName)
}
// ReplicationPolicy return the derived workflow replication policy
}
return string(id)
}
func (id ID) IsEmpty() bool {
}
return string(n)
}
return n == EmptyName
}
func (m *CustomSearchAttributesMapper) GetAlias(fieldName string, namespace string) (string, error) {
// - error if input is malformed
// - UUID object if input can be parsed and is valid
if s == "" {
}
if err != nil {
return nil, err
}
}
// Scan implements sql.Scanner interface to allow this type to be
// parsed transparently by database drivers
if src == nil {
return nil
}
if err := guuid.Scan(src); err != nil {
return err
}
return nil
}
// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. This method returns a byte slice representation of uuid
return []byte(u), nil
}
hex.Encode(dst, u[:4])
dst[8] = '-'
hex.Encode(dst[9:13], u[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], u[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], u[8:10])
dst[23] = '-'
hex.Encode(dst[24:], u[10:])
}
clusterMetadata cluster.Metadata,
clientBean client.Bean,
return &taskFetcherFactoryImpl{
clusterMetadata: clusterMetadata,
clientBean: clientBean,
config: config,
fetchers: make(map[string]taskFetcher),
status: common.DaemonStatusInitialized,
logger: logger,
}
}
// Start starts the fetchers
if !atomic.CompareAndSwapInt32(
&f.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
f.logger.Info("Replication task fetchers started.")
}
// Stop stops the fetchers
if !atomic.CompareAndSwapInt32(
&f.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
f.fetchersLock.Lock()
defer f.fetchersLock.Unlock()
for _, fetcher := range f.fetchers {
fetcher.Stop()
}
}
}
f.clusterMetadata.RegisterMetadataChangeCallback(
f,
func(oldClusterMetadata map[string]*cluster.ClusterInformation, newClusterMetadata map[string]*cluster.ClusterInformation) {
f.fetchersLock.Lock()
defer f.fetchersLock.Unlock()
currentCluster := f.clusterMetadata.GetCurrentClusterName()
// Fetcher is lazy init. The callback only need to handle remove case.
for clusterName, newClusterInfo := range newClusterMetadata {
if clusterName == currentCluster {
continue
}
if fetcher, ok := f.fetchers[clusterName]; ok {
// NewDefaultVersionChecker constructs a new VersionChecker using default versions from const.
return NewVersionChecker(SupportedClients, ServerVersion)
}
// NewVersionChecker constructs a new VersionChecker
func NewVersionChecker(supportedClients map[string]string, serverVersion string) *versionChecker {
version_checker.go
return &versionChecker{
serverVersion: semver.MustParse(serverVersion),
supportedClients: supportedClients,
supportedClientsRange: mustParseRanges(supportedClients),
}
}
// GetClientNameAndVersion extracts SDK name and version from context headers
headers := GetValues(ctx, ClientNameHeaderName, ClientVersionHeaderName)
clientName := headers[0]
clientVersion := headers[1]
return clientName, clientVersion
}
// SetVersions sets headers for internal communications.
// ClientSupported returns an error if client is unsupported, nil otherwise.
headers := GetValues(ctx, ClientNameHeaderName, ClientVersionHeaderName, SupportedServerVersionsHeaderName)
clientName := headers[0]
clientVersion := headers[1]
supportedServerVersions := headers[2]
// Validate client version only if it is provided and server knows about this client.
if clientName != "" && clientVersion != "" {
clientVersionParsed, parseErr := semver.Parse(clientVersion)
if parseErr != nil {
return serviceerror.NewInvalidArgumentf("Unable to parse client version: %v", parseErr)
}
return serviceerror.NewClientVersionNotSupported(clientVersion, clientName, vc.supportedClients[clientName])
}
// Validate supported server version if it is provided.
supportedServerVersionsParsed, parseErr := semver.ParseRange(supportedServerVersions)
version_checker.go
if parseErr != nil {
return serviceerror.NewInvalidArgumentf("Unable to parse supported server versions: %v", parseErr)
}
return serviceerror.NewServerVersionNotSupported(vc.serverVersion.String(), supportedServerVersions)
}
}
}
}
out := make(map[string]semver.Range, len(ranges))
for c, r := range ranges {
out[c] = semver.MustParseRange(r)
}
return out
}
// NewServerMetricsContextInjectorInterceptor returns grpc server interceptor that adds metrics context to golang
// context.
return func(
ctx context.Context,
req any,
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (any, error) {
ctxWithMetricsBaggage := AddMetricsContext(ctx)
return handler(ctxWithMetricsBaggage, req)
}
}
// NewClientMetricsTrailerPropagatorInterceptor returns grpc client interceptor that injects metrics received in trailer
// into metrics context.
func NewClientMetricsTrailerPropagatorInterceptor(logger log.Logger) grpc.UnaryClientInterceptor {
grpc.go
return func(
ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
var trailer metadata.MD
optsWithTrailer := append(opts, grpc.Trailer(&trailer))
// NewServerMetricsTrailerPropagatorInterceptor returns grpc server interceptor that injects metrics from context into
// gRPC trailer.
func NewServerMetricsTrailerPropagatorInterceptor(logger log.Logger) grpc.UnaryServerInterceptor {
grpc.go
return func(
ctx context.Context,
req any,
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (any, error) {
// we want to return original handler response, so don't override err
resp, err := handler(ctx, req)
// getMetricsContext extracts metrics context from golang context.
metricsCtx := ctx.Value(metricsCtxKey)
if metricsCtx == nil {
return nil
}
}
metricsCtx := &metricsContext{}
return context.WithValue(ctx, metricsCtxKey, metricsCtx)
}
// ContextCounterAdd adds value to counter within metrics context.
// ContextCounterGet returns value and true if successfully retrieved value
metricsCtx := getMetricsContext(ctx)
if metricsCtx == nil {
return 0, false
}
defer metricsCtx.Unlock()
if metricsCtx.CountersInt == nil {
}
result, ok := metricsCtx.CountersInt[name]
// NewRateLimiter returns a new rate limiter that can handle dynamic
// configuration updates
limiter := rate.NewLimiter(rate.Limit(newRPS), newBurst)
ts := clock.NewRealTimeSource()
rl := &RateLimiterImpl{
rps: newRPS,
burst: newBurst,
timeSource: ts,
ClockedRateLimiter: NewClockedRateLimiter(limiter, ts),
}
return rl
}
// SetRPS sets the rate of the rate limiter
}
return rl.ClockedRateLimiter.Reserve()
}
return rl.ClockedRateLimiter.ReserveN(now, n)
}
// SetRateBurst sets the rps & burst of the rate limiter
rl.refreshInternalRateLimiterImpl(&rps, &burst)
}
// Rate returns the rps for this rate limiter
newRate *float64,
newBurst *int,
rl.Lock()
defer rl.Unlock()
refresh := false
if newRate != nil && rl.rps != *newRate {
refresh = true
}
refresh = true
}
rl.SetLimitAt(now, rate.Limit(rl.rps))
rl.SetBurstAt(now, rl.burst)
}
}
// NewRegistry creates a new [Registry].
return &Registry{
machines: make(map[string]StateMachineDefinition),
tasks: make(map[string]TaskSerializer),
immediateExecutors: make(map[string]any),
timerExecutors: make(map[string]any),
remoteExecutors: make(map[string]remoteMethodDefinition),
events: make(map[enumspb.EventType]EventDefinition),
}
}
// RegisterMachine registers a [StateMachineDefinition] by its type.
// Returns an [ErrDuplicateRegistration] if the state machine type has already been registered.
t := sm.Type()
if existing, ok := r.machines[t]; ok {
return fmt.Errorf("%w: state machine already registered for %v - %v", ErrDuplicateRegistration, sm.Type(), existing.Type())
}
return nil
}
// RegisterTaskSerializer registers a [TaskSerializer] for a given type.
// Returns an [ErrDuplicateRegistration] if a serializer for this task type has already been registered.
if exising, ok := r.tasks[t]; ok {
return fmt.Errorf("%w: task already registered for %v: %v", ErrDuplicateRegistration, t, exising)
}
return nil
}
// RegisterImmediateExecutor registers an [ImmediateExecutor] for the given task type.
// Returns an [ErrDuplicateRegistration] if an executor for the type has already been registered.
func RegisterImmediateExecutor[T Task](r *Registry, executor ImmediateExecutor[T]) error {
registry.go
var task T
taskType := task.Type()
// The executors are registered in pairs, so only need to check in one map.
if existing, ok := r.immediateExecutors[taskType]; ok {
return fmt.Errorf(
"%w: executor already registered for task type %v: %v",
// RegisterTimerExecutor registers a [TimerExecutor] for the given task type.
// Returns an [ErrDuplicateRegistration] if an executor for the type has already been registered.
func RegisterTimerExecutor[T Task](r *Registry, executor TimerExecutor[T]) error {
registry.go
var task T
taskType := task.Type()
// The executors are registered in pairs, so only need to check in one map.
if existing, ok := r.timerExecutors[taskType]; ok {
return fmt.Errorf(
"%w: executor already registered for task type %v: %v",
// RegisterEventDefinition registers an [EventDefinition] for the given event type.
// Returns an [ErrDuplicateRegistration] if a definition for the type has already been registered.
t := def.Type()
prev, ok := r.events[t]
if ok {
return fmt.Errorf("%w: event definition for event type %v: %v", ErrDuplicateRegistration, t, prev)
}
return nil
}
)
return &contextFactoryImpl{
ContextFactoryParams: ¶ms,
}
}
func (c *contextFactoryImpl) CreateContext(
shardID int32,
closeCallback CloseCallback,
shard, err := newContext(
shardID,
c.EngineFactory,
c.Config,
c.PersistenceConfig,
closeCallback,
c.Logger,
c.ThrottledLogger,
c.PersistenceExecutionManager,
c.PersistenceShardManager,
c.ClientBean,
c.HistoryClient,
c.MetricsHandler,
c.EventLogger,
c.PayloadSerializer,
c.TimeSource,
c.NamespaceRegistry,
c.SaProvider,
c.SaMapperProvider,
c.ClusterMetadata,
c.ArchivalMetadata,
c.HostInfoProvider,
c.TaskCategoryRegistry,
c.EventsCache,
c.StateMachineRegistry,
c.ChasmRegistry,
c.ChasmWorkflowRegistry,
c.EndpointRegistry,
c.HandoverTrackerFactory,
)
if err != nil {
return nil, err
}
return shard, nil
}
var Library = &library{}
return libraryName
}
return []*nexus.Service{NewTestServiceNexusService()}
}
return []*chasm.NexusServiceProcessor{NewTestServiceNexusServiceProcessor()}
}
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*PayloadStore](
componentName,
chasm.WithBusinessIDAlias("PayloadStoreId"),
chasm.WithSearchAttributes(
PayloadTotalCountSearchAttribute,
PayloadTotalSizeSearchAttribute,
ExecutionStatusSearchAttribute,
chasm.SearchAttributeTaskQueue,
),
chasm.WithContextValues(map[any]any{
componentCtxKey: componentCtxVal,
}),
),
}
}
return []*chasm.RegistrableTask{
chasm.NewRegistrablePureTask(
"payloadTTLPureTask",
&PayloadTTLPureTaskHandler{},
),
chasm.NewRegistrableSideEffectTask(
"payloadTTLSideEffectTask",
&PayloadTTLSideEffectTaskHandler{},
),
}
}
// New creates a new cache with the given options
return NewWithMetrics(maxSize, opts, metrics.NoopMetricsHandler)
}
// NewWithMetrics creates a new cache that will emit capacity and ttl metrics.
// handler should be tagged with metrics.CacheTypeTag.
func NewWithMetrics(maxSize int, opts *Options, handler metrics.Handler) StoppableCache {
lru.go
if opts == nil {
}
if backgroundEvict == nil {
return dynamicconfig.CacheBackgroundEvictSettings{
Enabled: false,
}
}
}
if timeSource == nil {
}
metrics.CacheTtl.With(handler).Record(opts.TTL)
c := &lru{
byAccess: list.New(),
byKey: make(map[any]*list.Element),
ttl: opts.TTL,
maxSize: maxSize,
currSize: 0,
pin: opts.Pin,
onPut: opts.OnPut,
onEvict: opts.OnEvict,
timeSource: timeSource,
metricsHandler: handler,
backgroundEvict: backgroundEvict,
}
if c.backgroundEvict().Enabled {
c.loops.Go(c.bgEvictLoop)
}
}
}
c.loops.Cancel()
}
func (c *lru) bgEvictLoop(ctx context.Context) error {
// NewLocalGate create a new timer gate instance
lg := &LocalGateImpl{
timer: time.NewTimer(0),
nextWakeupTime: time.Time{},
fireCh: make(chan struct{}, 1),
closeCh: make(chan struct{}),
timeSource: timeSource,
}
// the timer should be stopped when initialized
if !lg.timer.Stop() {
// drain the existing signal if exist
<-lg.timer.C
}
defer close(lg.fireCh)
defer lg.timer.Stop()
loop:
for {
select {
select {
// re-transmit on gateC
default:
}
// closed; cleanup and quit
break loop
}
}
}()
}
// FireCh return the channel which will be fired when time is up
return lg.fireCh
}
// FireAfter check will the timer get fired after a certain time
// Update the timer gate, return true if update is a success.
// Success means timer is idle or timer is set with a sooner time to fire
// NOTE: negative duration will make the timer fire immediately
now := lg.timeSource.Now()
if lg.timer.Stop() && lg.nextWakeupTime.Before(nextTime) {
// this means the timer, before stopped, is active && next wake-up time do not have to be updated
lg.timer.Reset(lg.nextWakeupTime.Sub(now))
// this means the timer, before stopped, is active && next wake-up time has to be updated
// or this means the timer, before stopped, is already fired / never active
lg.timer.Reset(nextTime.Sub(now))
// Notifies caller that next notification is reset to fire at passed in 'next' visibility time
return true
}
// Close shutdown the timer
close(lg.closeCh)
}
// InverseMap creates the inverse map, ie., for a key-value map, it builds the value-key map.
if m == nil {
return nil
}
invm := make(map[V]K, len(m))
for k, v := range m {
// MapSlice given slice xs []T and f(T) S produces slice []S by applying f to every element of xs
if xs == nil {
return nil
}
for i, s := range xs {
}
}
// FilterSlice iterates over elements of a slice, returning a new slice of all elements predicate returns true for.
var out []T
for _, elem := range in {
}
}
}
// RepeatSlice given slice and a number (n) produces a new slice containing original slice n times
// if n is non-positive will produce nil
if xs == nil || n <= 0 {
return nil
}
for i := range n {
copy(ys[i*len(xs):], xs)
}
return ys
}
// InterruptibleSleep is like time.Sleep but can be interrupted by a context.
// Returns context error if interrupted, otherwise nil.
timer := time.NewTimer(timeout)
defer timer.Stop()
select {
case <-timer.C:
return nil
return ctx.Err()
}
}
)
return &workerComponent{
historyClient: params.HistoryClient,
currentClusterName: string(params.CurrentClusterName),
taskClientDialer: params.TaskClientDialer,
}
}
//revive:disable:import-shadowing this doesn't actually shadow imports because it's a method, not a function
}
registry.RegisterWorkflowWithOptions(c.workflow, workflow.RegisterOptions{
Name: WorkflowName,
})
}
func (c *workerComponent) DedicatedWorkflowWorkerOptions() *workercommon.DedicatedWorkerOptions {
workflow.go
// use default worker
return nil
}
registry.RegisterActivityWithOptions(c.deleteTasks, activity.RegisterOptions{
Name: deleteTasksActivityName,
})
registry.RegisterActivityWithOptions(c.readTasks, activity.RegisterOptions{
Name: readTasksActivityName,
})
registry.RegisterActivityWithOptions(c.reEnqueueTasks, activity.RegisterOptions{
Name: reEnqueueTasksActivityName,
})
}
func (c *workerComponent) DedicatedActivityWorkerOptions() *workercommon.DedicatedWorkerOptions {
workflow.go
return &workercommon.DedicatedWorkerOptions{
TaskQueue: primitives.DLQActivityTQ,
Options: sdkworker.Options{
BackgroundActivityContext: headers.SetCallerType(
context.Background(),
headers.CallerTypePreemptable,
),
},
}
}
// Dial implements [TaskClientDialer] by calling the [TaskClientDialerFn] with the cluster name.
func (*ChasmTaskInfo) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_chasm_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_persistence_v1_chasm_proto_init() {
if File_temporal_server_api_persistence_v1_chasm_proto != nil {
return
}
file_temporal_server_api_persistence_v1_hsm_proto_init()
file_temporal_server_api_persistence_v1_chasm_proto_msgTypes[1].OneofWrappers = []any{
(*ChasmNodeMetadata_ComponentAttributes)(nil),
(*ChasmNodeMetadata_DataAttributes)(nil),
(*ChasmNodeMetadata_CollectionAttributes)(nil),
(*ChasmNodeMetadata_PointerAttributes)(nil),
}
file_temporal_server_api_persistence_v1_chasm_proto_msgTypes[10].OneofWrappers = []any{
(*ChasmNexusCompletion_Success)(nil),
(*ChasmNexusCompletion_Failure)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_chasm_proto_rawDesc), len(file_temporal_server_api_persistence_v1_chasm_proto_rawDesc)),
NumEnums: 0,
NumMessages: 15,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_chasm_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_chasm_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_chasm_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_chasm_proto = out.File
file_temporal_server_api_persistence_v1_chasm_proto_goTypes = nil
file_temporal_server_api_persistence_v1_chasm_proto_depIdxs = nil
}
ctx context.Context,
row *sqlplugin.ShardsRow,
return mdb.conn.ExecContext(ctx,
createShardQry,
row.ShardID,
row.RangeID,
row.Data,
row.DataEncoding,
)
}
// UpdateShards updates one or more rows into shards table
ctx context.Context,
row *sqlplugin.ShardsRow,
return mdb.conn.ExecContext(ctx,
updateShardQry,
row.RangeID,
row.Data,
row.DataEncoding,
row.ShardID,
)
}
// SelectFromShards reads one or more rows from shards table
ctx context.Context,
filter sqlplugin.ShardsFilter,
var row sqlplugin.ShardsRow
err := mdb.conn.GetContext(ctx,
&row,
getShardQry,
filter.ShardID,
)
if err != nil {
}
return &row, err
}
ctx context.Context,
filter sqlplugin.ShardsFilter,
var rangeID int64
err := mdb.conn.GetContext(ctx,
&rangeID,
lockShardQry,
filter.ShardID,
)
return rangeID, err
}
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED
}
func (d ScheduledEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED
}
func (d CancelRequestedEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED
}
func (d CancelRequestCompletedEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED
}
func (d CancelRequestFailedEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_STARTED
}
func (d StartedEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_COMPLETED
}
func (d CompletedEventDefinition) CherryPick(root *hsm.Node, event *historypb.HistoryEvent, excludeTypes map[enumspb.ResetReapplyExcludeType]struct{}) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_FAILED
}
func (d FailedEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCELED
}
func (d CanceledEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT
}
func (d TimedOutEventDefinition) Apply(root *hsm.Node, event *historypb.HistoryEvent) error {
}
if err := reg.RegisterEventDefinition(ScheduledEventDefinition{}); err != nil {
return err
}
if err := reg.RegisterEventDefinition(CancelRequestedEventDefinition{}); err != nil {
events.go
return err
}
if err := reg.RegisterEventDefinition(CancelRequestCompletedEventDefinition{}); err != nil {
events.go
return err
}
if err := reg.RegisterEventDefinition(CancelRequestFailedEventDefinition{}); err != nil {
events.go
return err
}
return err
}
return err
}
return err
}
return err
}
}
func NewOperatorHandlerImpl(
args NewOperatorHandlerImplArgs,
handler := &OperatorHandlerImpl{
logger: args.Logger,
status: common.DaemonStatusInitialized,
config: args.config,
sdkClientFactory: args.sdkClientFactory,
metricsHandler: args.MetricsHandler,
visibilityMgr: args.VisibilityMgr,
saManager: args.SaManager,
healthServer: args.healthServer,
historyClient: args.historyClient,
clusterMetadataManager: args.clusterMetadataManager,
clusterMetadata: args.clusterMetadata,
clientFactory: args.clientFactory,
namespaceRegistry: args.namespaceRegistry,
nexusEndpointClient: args.nexusEndpointClient,
}
return handler
}
// Start starts the handler
if atomic.CompareAndSwapInt32(
&h.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
h.healthServer.SetServingStatus(OperatorServiceName, healthpb.HealthCheckResponse_SERVING)
}
}
// Stop stops the handler
if atomic.CompareAndSwapInt32(
&h.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
h.healthServer.SetServingStatus(OperatorServiceName, healthpb.HealthCheckResponse_NOT_SERVING)
}
}
}
func init() { file_temporal_server_api_adminservice_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_api_adminservice_v1_request_response_proto_init() {
if File_temporal_server_api_adminservice_v1_request_response_proto != nil {
return
}
file_temporal_server_api_adminservice_v1_request_response_proto_msgTypes[59].OneofWrappers = []any{
(*StreamWorkflowReplicationMessagesRequest_SyncReplicationState)(nil),
}
file_temporal_server_api_adminservice_v1_request_response_proto_msgTypes[60].OneofWrappers = []any{
(*StreamWorkflowReplicationMessagesResponse_Messages)(nil),
}
file_temporal_server_api_adminservice_v1_request_response_proto_msgTypes[61].OneofWrappers = []any{
(*GetNamespaceRequest_Namespace)(nil),
(*GetNamespaceRequest_Id)(nil),
}
file_temporal_server_api_adminservice_v1_request_response_proto_msgTypes[90].OneofWrappers = []any{
(*StartAdminBatchOperationRequest_RefreshTasksOperation)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_adminservice_v1_request_response_proto_rawDesc), len(file_temporal_server_api_adminservice_v1_request_response_proto_rawDesc)),
NumEnums: 1,
NumMessages: 105,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_adminservice_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_api_adminservice_v1_request_response_proto_depIdxs,
EnumInfos: file_temporal_server_api_adminservice_v1_request_response_proto_enumTypes,
MessageInfos: file_temporal_server_api_adminservice_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_api_adminservice_v1_request_response_proto = out.File
file_temporal_server_api_adminservice_v1_request_response_proto_goTypes = nil
file_temporal_server_api_adminservice_v1_request_response_proto_depIdxs = nil
}
}
func file_temporal_server_api_replication_v1_message_proto_init() {
if File_temporal_server_api_replication_v1_message_proto != nil {
return
}
file_temporal_server_api_replication_v1_message_proto_msgTypes[0].OneofWrappers = []any{
message.pb.go
(*ReplicationTask_NamespaceTaskAttributes)(nil),
(*ReplicationTask_SyncShardStatusTaskAttributes)(nil),
(*ReplicationTask_SyncActivityTaskAttributes)(nil),
(*ReplicationTask_HistoryTaskAttributes)(nil),
(*ReplicationTask_SyncWorkflowStateTaskAttributes)(nil),
(*ReplicationTask_TaskQueueUserDataAttributes)(nil),
(*ReplicationTask_SyncHsmAttributes)(nil),
(*ReplicationTask_BackfillHistoryTaskAttributes)(nil),
(*ReplicationTask_VerifyVersionedTransitionTaskAttributes)(nil),
(*ReplicationTask_SyncVersionedTransitionTaskAttributes)(nil),
}
file_temporal_server_api_replication_v1_message_proto_msgTypes[21].OneofWrappers = []any{
(*VersionedTransitionArtifact_SyncWorkflowStateMutationAttributes)(nil),
(*VersionedTransitionArtifact_SyncWorkflowStateSnapshotAttributes)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_replication_v1_message_proto_rawDesc), len(file_temporal_server_api_replication_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 23,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_replication_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_replication_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_replication_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_replication_v1_message_proto = out.File
file_temporal_server_api_replication_v1_message_proto_goTypes = nil
file_temporal_server_api_replication_v1_message_proto_depIdxs = nil
}
config *persistencespb.NamespaceConfig,
targetCluster string,
detail := &persistencespb.NamespaceDetail{
Info: ensureInfo(info),
Config: ensureConfig(config),
ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
ActiveClusterName: targetCluster,
Clusters: []string{targetCluster},
},
FailoverVersion: common.EmptyVersion,
}
factory := NewDefaultReplicationResolverFactory()
resolver := factory(detail)
ns, _ := FromPersistentState(detail, resolver, WithGlobalFlag(false))
return ns
}
// NewNamespaceForTest returns an entry with test data
repConfig *persistencespb.NamespaceReplicationConfig,
failoverVersion int64,
detail := &persistencespb.NamespaceDetail{
Info: ensureInfo(info),
Config: ensureConfig(config),
ReplicationConfig: ensureRepConfig(repConfig),
FailoverVersion: failoverVersion,
}
factory := NewDefaultReplicationResolverFactory()
resolver := factory(detail)
ns, _ := FromPersistentState(detail, resolver, WithGlobalFlag(true))
return ns
}
func ensureInfo(proto *persistencespb.NamespaceInfo) *persistencespb.NamespaceInfo {
testconstructors.go
if proto == nil {
return &persistencespb.NamespaceInfo{}
}
}
func ensureConfig(proto *persistencespb.NamespaceConfig) *persistencespb.NamespaceConfig {
testconstructors.go
if proto == nil {
return &persistencespb.NamespaceConfig{}
}
}
func ensureRepConfig(proto *persistencespb.NamespaceReplicationConfig) *persistencespb.NamespaceReplicationConfig {
testconstructors.go
if proto == nil {
return &persistencespb.NamespaceReplicationConfig{}
}
}
)
func NewClockedRateLimiter(rateLimiter *rate.Limiter, timeSource clock.TimeSource) ClockedRateLimiter {
clocked_rate_limiter.go
return ClockedRateLimiter{
rateLimiter: rateLimiter,
timeSource: timeSource,
recycleCh: make(chan struct{}),
}
}
return l.AllowN(l.timeSource.Now(), 1)
}
return l.rateLimiter.AllowN(now, token)
}
// ClockedReservation wraps a rate.Reservation with a clockwork.Clock. It is used to ensure that the reservation
}
return r.reservation.OK()
}
return r.DelayFrom(r.timeSource.Now())
}
return r.reservation.DelayFrom(t)
}
func (r ClockedReservation) Cancel() {
}
return l.ReserveN(l.timeSource.Now(), 1)
}
func (l ClockedRateLimiter) ReserveN(now time.Time, token int) ClockedReservation {
clocked_rate_limiter.go
reservation := l.rateLimiter.ReserveN(now, token)
return ClockedReservation{reservation, l.timeSource}
}
func (l ClockedRateLimiter) Wait(ctx context.Context) error {
}
func (l ClockedRateLimiter) SetLimitAt(t time.Time, newLimit rate.Limit) {
clocked_rate_limiter.go
l.rateLimiter.SetLimitAt(t, newLimit)
}
// Clamp burst to >=1 when rate is positive; burst=0 with rate=0 is allowed for pause.
if newBurst < 1 && l.rateLimiter.Limit() > 0 {
newBurst = 1
}
}
func (*UpdateInfo) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_update_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_persistence_v1_update_proto_init() {
if File_temporal_server_api_persistence_v1_update_proto != nil {
return
}
file_temporal_server_api_persistence_v1_hsm_proto_init()
file_temporal_server_api_persistence_v1_update_proto_msgTypes[0].OneofWrappers = []any{
(*UpdateAdmissionInfo_HistoryPointer_)(nil),
}
file_temporal_server_api_persistence_v1_update_proto_msgTypes[3].OneofWrappers = []any{
(*UpdateInfo_Acceptance)(nil),
(*UpdateInfo_Completion)(nil),
(*UpdateInfo_Admission)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_update_proto_rawDesc), len(file_temporal_server_api_persistence_v1_update_proto_rawDesc)),
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_update_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_update_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_update_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_update_proto = out.File
file_temporal_server_api_persistence_v1_update_proto_goTypes = nil
file_temporal_server_api_persistence_v1_update_proto_depIdxs = nil
}
}
return backoff.NewExponentialRetryPolicy(cfg.InitialInterval).
WithMaximumInterval(cfg.MaxInterval).
WithExpirationInterval(backoff.NoInterval)
}
var defaultRetryPolicyConfig = RetryPolicyConfig{
}
func configProvider(dc *dynamicconfig.Collection, cfg *config.Persistence) *Config {
config.go
return &Config{
Enabled: Enabled.Get(dc),
EnableChasm: dynamicconfig.EnableChasm.Get(dc),
EnableChasmNexusWorkflowOperations: EnableChasmWorkflowOperations.Get(dc),
NumHistoryShards: cfg.NumHistoryShards,
LongPollBuffer: LongPollBuffer.Get(dc),
LongPollTimeout: LongPollTimeout.Get(dc),
RequestTimeout: RequestTimeout.Get(dc),
MinRequestTimeout: MinRequestTimeout.Get(dc),
MaxConcurrentOperationsPerWorkflow: MaxConcurrentOperationsPerWorkflow.Get(dc),
MaxServiceNameLength: MaxServiceNameLength.Get(dc),
MaxOperationNameLength: MaxOperationNameLength.Get(dc),
MaxOperationTokenLength: MaxOperationTokenLength.Get(dc),
MaxOperationHeaderSize: MaxOperationHeaderSize.Get(dc),
DisallowedOperationHeaders: DisallowedOperationHeaders.Get(dc),
MaxOperationScheduleToCloseTimeout: MaxOperationScheduleToCloseTimeout.Get(dc),
PayloadSizeLimit: dynamicconfig.BlobSizeLimitError.Get(dc),
PayloadSizeLimitWarn: dynamicconfig.BlobSizeLimitWarn.Get(dc),
MaxUserMetadataSummarySize: dynamicconfig.MaxUserMetadataSummarySize.Get(dc),
MaxUserMetadataDetailsSize: dynamicconfig.MaxUserMetadataDetailsSize.Get(dc),
CallbackURLTemplate: CallbackURLTemplate.Get(dc),
UseSystemCallbackURL: UseSystemCallbackURL.Get(dc),
UseNewFailureWireFormat: UseNewFailureWireFormat.Get(dc),
VisibilityMaxPageSize: dynamicconfig.FrontendVisibilityMaxPageSize.Get(dc),
MaxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
MaxReasonLength: MaxReasonLength.Get(dc),
RetryPolicy: RetryPolicy.Get(dc),
}
}
func NewPagingIterator[V any](
paginationFn PaginationFn[V],
iter := &PagingIteratorImpl[V]{
paginationFn: paginationFn,
pageToken: nil,
pageErr: nil,
pageItems: nil,
nextPageItemIndex: 0,
}
iter.getNextPage() // this will initialize the paging iterator
return iter
}
// NewPagingIteratorWithToken create a new paging iterator with initial token
// HasNext return whether has next item or err
// pagination encounters error
if iter.pageErr != nil {
return true
}
// still have local cached item to return
}
iter.getNextPage()
return iter.HasNext()
}
}
// Next return next item or err
if !iter.HasNext() {
panic("HistoryEventIterator Next() called without checking HasNext()")
}
err := iter.pageErr
iter.pageErr = nil
// we have cached events
index := iter.nextPageItemIndex
iter.nextPageItemIndex++
return iter.pageItems[index], nil
}
panic("HistoryEventIterator Next() should return either a history event or a err")
}
items, token, err := iter.paginationFn(iter.pageToken)
if err == nil {
iter.pageToken = token
iter.pageErr = nil
iter.pageItems = nil
iter.pageToken = nil
iter.pageErr = err
}
}
forwardingClients *cluster.FrontendHTTPClientCache,
httpTraceProvider commonnexus.HTTPClientTraceProvider,
return &nexusCompletionHandler{
ClusterMetadata: clusterMetadata,
NamespaceRegistry: namespaceRegistry,
Logger: logger,
MetricsHandler: metricsHandler,
Config: serviceConfig,
CallbackTokenGenerator: callbackTokenGenerator,
HistoryClient: historyClient,
TelemetryInterceptor: telemetryInterceptor,
RequestErrorHandler: requestErrorHandler,
NamespaceValidationInterceptor: namespaceValidationInterceptor,
NamespaceRateLimitInterceptor: namespaceRateLimitInterceptor,
NamespaceConcurrencyLimitInterceptor: namespaceConcurrencyLimitInterceptor,
RateLimitInterceptor: rateLimitInterceptor,
AuthInterceptor: authInterceptor,
RedirectionInterceptor: redirectionInterceptor,
ForwardingClients: forwardingClients,
HTTPTraceProvider: httpTraceProvider,
clientVersionChecker: headers.NewDefaultVersionChecker(),
preProcessErrorsCounter: metricsHandler.Counter(metrics.NexusCompletionRequestPreProcessErrors.Name()),
}
}
func newNexusCompletionHTTPHandler(handler *nexusCompletionHandler, logger log.Logger) *nexusCompletionHTTPHandler {
nexus_completion_http_handler.go
return &nexusCompletionHTTPHandler{
httpHandler: nexusrpc.NewCompletionHTTPHandler(nexusrpc.CompletionHandlerOptions{
Handler: handler,
Logger: log.NewSlogLogger(logger),
Serializer: commonnexus.PayloadSerializer,
}),
}
}
// CompleteOperation implements nexus.CompletionHandler.
}
func (h *nexusCompletionHTTPHandler) RegisterRoutes(r *mux.Router) {
nexus_completion_http_handler.go
r.Path("/" + commonnexus.RouteCompletionCallback.Representation()).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, rpc.MaxNexusAPIRequestBodyBytes)
h.httpHandler.ServeHTTP(w, r)
})
r.Path(commonnexus.PathCompletionCallbackNoIdentifier).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
nexus_completion_http_handler.go
r.Body = http.MaxBytesReader(w, r.Body, rpc.MaxNexusAPIRequestBodyBytes)
h.httpHandler.ServeHTTP(w, r)
)
func newTaskRequestTracker(registry tasks.TaskCategoryRegistry) *taskRequestTracker {
task_request_tracker.go
outstandingTaskKeys := make(map[tasks.Category]map[tasks.Key]struct{})
for _, category := range registry.GetCategories() {
outstandingTaskKeys[category] = make(map[tasks.Key]struct{})
}
return &taskRequestTracker{
pendingTaskKeys: outstandingTaskKeys,
}
}
func (t *taskRequestTracker) minTaskKey(
category tasks.Category,
t.Lock()
defer t.Unlock()
pendingTasksForCategory := t.pendingTaskKeys[category]
if len(pendingTasksForCategory) == 0 {
}
minKey := tasks.MaximumKey
// otherwise inflight request can fails as those requests are conditioned on
// the current rangeID
t.Lock()
if t.inflightRequestCount == 0 {
t.Unlock()
return
}
waitCh := make(chan struct{})
}
t.Lock()
defer t.Unlock()
for category := range t.pendingTaskKeys {
t.pendingTaskKeys[category] = make(map[tasks.Key]struct{})
}
t.inflightRequestCount = 0
t.closeWaitChannelsLocked()
}
for _, waitCh := range t.waitChannels {
close(waitCh)
}
}
}
func newOperationInvocationTaskHandler(opts operationInvocationTaskHandlerOptions) *operationInvocationTaskHandler {
operation_tasks.go
return &operationInvocationTaskHandler{
nexusTaskHandlerBase: opts.toBase(),
callbackTokenGenerator: opts.CallbackTokenGenerator,
}
}
func (h *operationInvocationTaskHandler) Validate(
}
func newOperationBackoffTaskHandler(opts operationTaskHandlerOptions) *operationBackoffTaskHandler {
operation_tasks.go
return &operationBackoffTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
logger: opts.Logger,
}
}
func (h *operationBackoffTaskHandler) Validate(
}
func newOperationScheduleToStartTimeoutTaskHandler(opts operationTaskHandlerOptions) *operationScheduleToStartTimeoutTaskHandler {
operation_tasks.go
return &operationScheduleToStartTimeoutTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
logger: opts.Logger,
}
}
func (h *operationScheduleToStartTimeoutTaskHandler) Validate(
}
func newOperationStartToCloseTimeoutTaskHandler(opts operationTaskHandlerOptions) *operationStartToCloseTimeoutTaskHandler {
operation_tasks.go
return &operationStartToCloseTimeoutTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
logger: opts.Logger,
}
}
func (h *operationStartToCloseTimeoutTaskHandler) Validate(
}
func newOperationScheduleToCloseTimeoutTaskHandler(opts operationTaskHandlerOptions) *operationScheduleToCloseTimeoutTaskHandler {
operation_tasks.go
return &operationScheduleToCloseTimeoutTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
logger: opts.Logger,
}
}
func (h *operationScheduleToCloseTimeoutTaskHandler) Validate(
logger log.Logger,
certProviderFactory CertProviderFactory,
if err := validateRootTLS(&encryptionSettings); err != nil {
return nil, err
}
certProviderFactory = NewLocalStoreCertProvider
}
return NewLocalStoreTlsProvider(&encryptionSettings, metricsHandler.WithTags(metrics.OperationTag(metrics.ServerTlsScope)), logger, certProviderFactory)
}
if err := validateGroupTLS(&cfg.Internode); err != nil {
return err
}
return err
}
}
if err := validateServerTLS(&cfg.Server); err != nil {
return err
}
return err
}
if strings.TrimSpace(host) == "" {
}
}
}
if cfg.CertFile != "" && cfg.CertData != "" {
return fmt.Errorf("cannot specify CertFile and CertData at the same time")
}
return fmt.Errorf("cannot specify KeyFile and KeyData at the same time")
}
}
if cfg.CertFile != "" && cfg.CertData != "" {
return fmt.Errorf("cannot specify CertFile and CertData at the same time")
}
return fmt.Errorf("cannot specify KeyFile and KeyData at the same time")
}
return fmt.Errorf("invalid ServerTLS.ClientCAData: %w", err)
}
return fmt.Errorf("invalid ServerTLS.ClientCAFiles: %w", err)
}
return fmt.Errorf("cannot specify ClientCAFiles and ClientCAData at the same time")
}
}
if err := validateCAs(cfg.RootCAData); err != nil {
return fmt.Errorf("invalid ClientTLS.RootCAData: %w", err)
}
return fmt.Errorf("invalid ClientTLS.RootCAFiles: %w", err)
}
return fmt.Errorf("cannot specify RootCAFiles and RootCAData at the same time")
}
}
for _, ca := range cas {
if strings.TrimSpace(ca) == "" {
return fmt.Errorf("CA cannot be empty string")
}
}
}
func NewCallerInfoInterceptor(
namespaceRegistry namespace.Registry,
return &CallerInfoInterceptor{
namespaceRegistry: namespaceRegistry,
}
}
func (i *CallerInfoInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
ctx = PopulateCallerInfo(
ctx,
func() string { return string(MustGetNamespaceName(i.namespaceRegistry, req)) },
func() string { return api.MethodName(info.FullMethod) },
)
}
nsNameGetter func() string,
methodGetter func() string,
callerInfo := headers.GetCallerInfo(ctx)
infoUpdated := false
nsName := nsNameGetter()
if callerInfo.CallerName != nsName {
callerInfo.CallerName = nsName
infoUpdated = true
}
if !isValidCallerType {
callerInfo.CallerType = headers.CallerTypeAPI
infoUpdated = true
}
callerInfo.CallerType == headers.CallerTypeOperator {
methodName := methodGetter()
if callerInfo.CallOrigin != methodName {
infoUpdated = true
}
}
ctx = headers.SetCallerInfo(ctx, callerInfo)
}
}
rateFn quotas.RateFn,
maxReaders int64,
rateLimiters := make(map[int]quotas.RequestRateLimiter, maxReaders)
readerCallerToPriority := make(map[string]int, maxReaders)
for readerId := DefaultReaderId; readerId != DefaultReaderId+maxReaders; readerId++ {
// use readerId as priority
rateLimiters[int(readerId)] = quotas.NewRequestRateLimiterAdapter(quotas.NewDefaultOutgoingRateLimiter(rateFn))
// reader will use readerId (in string type) as caller when using the rate limiter
readerCallerToPriority[newReaderRequest(readerId).Caller] = int(readerId)
}
lowestPriority := int(DefaultReaderId + maxReaders - 1)
return quotas.NewPriorityRateLimiter(
func(req quotas.Request) int {
return priority
}
return lowestPriority
},
hostReaderRateLimiter quotas.RequestRateLimiter,
maxReaders int64,
return quotas.NewMultiRequestRateLimiter(
NewReaderPriorityRateLimiter(
func() float64 { return float64(shardMaxPollRPS()) },
maxReaders,
),
func newReaderRequest(
readerID int64,
// The priority is only based on readerID (caller),
// api, caller type, caller segment, and call initiation (origin)
// are the same for all the readers, and not related to
// priority so leaving those fields empty.
return quotas.NewRequest(
"",
readerRequestToken,
strconv.FormatInt(readerID, 10),
"",
0,
"",
)
}
tracer trace.Tracer,
logger log.SnTaggedLogger,
timeoutQueue := newMemoryScheduledQueue(
scheduler,
timeSource,
logger,
metricsHandler,
)
return &SpeculativeWorkflowTaskTimeoutQueue{
timeoutQueue: timeoutQueue,
executor: executor,
priorityAssigner: priorityAssigner,
namespaceRegistry: namespaceRegistry,
clusterMetadata: clusterMetadata,
timeSource: timeSource,
chasmRegistry: chasmRegistry,
metricsHandler: metricsHandler,
tracer: tracer,
logger: logger,
}
}
func (q SpeculativeWorkflowTaskTimeoutQueue) Start() {
speculative_workflow_task_timeout_queue.go
q.timeoutQueue.Start()
}
func (q SpeculativeWorkflowTaskTimeoutQueue) Stop() {
speculative_workflow_task_timeout_queue.go
q.timeoutQueue.Stop()
}
func (q SpeculativeWorkflowTaskTimeoutQueue) Category() tasks.Category {
speculative_workflow_task_timeout_queue.go
return tasks.CategoryMemoryTimer
}
func (q SpeculativeWorkflowTaskTimeoutQueue) NotifyNewTasks(ts []tasks.Task) {
speculative_workflow_task_timeout_queue.go
for _, task := range ts {
if wttt, ok := task.(*tasks.WorkflowTaskTimeoutTask); ok {
executable := newSpeculativeWorkflowTaskTimeoutExecutable(NewExecutable(
0,
saMapperProvider searchattribute.MapperProvider,
saValidator *searchattribute.Validator,
return &library{
registry: registry,
config: config,
saMapperProvider: saMapperProvider,
saValidator: saValidator,
workflowServiceNexusHandler: &workflowServiceNexusHandler{
config: config,
namespaceRegistry: namespaceRegistry,
},
}
}
// NewLibrary creates a new CHASM library for the workflow package.
}
return chasm.WorkflowLibraryName
}
type workflowContext struct {
}
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*Workflow](chasm.WorkflowComponentName, chasm.WithContextValues(map[any]any{
ctxKeyWorkflowContext: &workflowContext{registry: l.registry},
})),
chasm.NewRegistrableComponent[*WorkflowUpdate]("update"),
}
}
// SetEventRegistryOnContext injects the event registry into a CHASM context. This is primarily
}
if l.workflowServiceNexusHandler == nil {
return nil
}
mustNewWorkflowServiceNexusHandler(l.workflowServiceNexusHandler),
}
}
if l.workflowServiceNexusHandler == nil {
return nil
}
NewWorkflowServiceNexusServiceProcessor(l.config, l.saMapperProvider, l.saValidator),
}
}
// newHostInfo creates a new *hostInfo instance
return &hostInfo{
addr: addr,
labels: labels,
labelsChecksum: checksumLabels(labels),
}
}
// GetAddress returns the ip:port address
return hi.addr
}
// Identity implements ringpop's Membership interface
// For now, we just use the address as the identity.
return hi.addr
}
// Label implements ringpop's Membership interface
// summary returns a shorthand summary string suitable for logging.
var s strings.Builder
s.WriteString(hi.GetAddress())
for k, v := range hi.labels {
switch k {
// skip these, they can be determined from context
s.WriteString(fmt.Sprintf("[%s=%s]", k, v))
}
}
}
// checksumLabels returns a checksum of a labels map
var c uint64
for k, v := range labels {
vfp := farm.Fingerprint64([]byte(v))
// use xor to combine different labels so that it comes out the same with any iteration
// order, without needing to sort.
c ^= kfp + bits.RotateLeft64(vfp, 3)
}
}
calculator Calculator,
logger log.Logger,
return &LoggedCalculator{
quotaLogger: newQuotaLogger(logger),
calculator: calculator,
}
}
quota := c.calculator.GetQuota()
c.quotaLogger.updateQuota(quota)
return quota
}
func NewLoggedNamespaceCalculator(
calculator NamespaceCalculator,
logger log.Logger,
return &LoggedNamespaceCalculator{
calculator: calculator,
logger: logger,
quotaLoggers: make(map[string]*quotaLogger[float64]),
}
}
func (c *LoggedNamespaceCalculator) GetQuota(namespace string) float64 {
func newQuotaLogger(
logger log.Logger,
return "aLogger[float64]{
logger: logger,
}
}
currentQuota := l.currentValue.Swap(newQuota)
if currentQuota != nil && newQuota == currentQuota.(T) {
}
tag.Any("current-quota", currentQuota),
tag.Any("new-quota", newQuota),
)
}
func NewMultiRequestRateLimiter(
requestRateLimiters ...RequestRateLimiter,
if len(requestRateLimiters) == 0 {
panic("expect at least one rate limiter")
}
requestRateLimiters: requestRateLimiters,
}
}
func (rl *MultiRequestRateLimiterImpl) Allow(now time.Time, request Request) bool {
multi_request_rate_limiter_impl.go
length := len(rl.requestRateLimiters)
reservations := make([]Reservation, 0, length)
for _, requestRateLimiter := range rl.requestRateLimiters {
reservation := requestRateLimiter.Reserve(now, request)
if !reservation.OK() || reservation.DelayFrom(now) > 0 {
if reservation.OK() {
reservation.CancelAt(now)
return false
}
}
}
func (rl *MultiRequestRateLimiterImpl) Reserve(now time.Time, request Request) Reservation {
multi_request_rate_limiter_impl.go
length := len(rl.requestRateLimiters)
reservations := make([]Reservation, 0, length)
for _, requestRateLimiter := range rl.requestRateLimiters {
reservation := requestRateLimiter.Reserve(now, request)
if !reservation.OK() {
// cancel all existing reservation
for _, reservation := range reservations {
return NewMultiReservation(false, nil)
}
}
}
func (rl *MultiRequestRateLimiterImpl) Wait(ctx context.Context, request Request) error {
multi_request_rate_limiter_impl.go
select {
case <-ctx.Done():
return ctx.Err()
}
reservation := rl.Reserve(now, request)
if !reservation.OK() {
return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", request.Token)
}
if delay == 0 {
return nil
}
waitLimit := InfDuration
if deadline, ok := ctx.Deadline(); ok {
metricsHandler metrics.Handler,
opts ...grpc.DialOption,
var grpcSecureOpt grpc.DialOption
if tlsConfig == nil {
grpcSecureOpt = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
}
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
// Default MaxDelay is 120 seconds which is too high.
Backoff: backoff.DefaultConfig,
MinConnectTimeout: minConnectTimeout,
}
cp.Backoff.MaxDelay = MaxBackoffDelay
dtrace := newDialTracer(hostName, metricsHandler, logger)
contextDialer := func(ctx context.Context, s string) (net.Conn, error) {
// Keep the existing gRPC behavior by using OS defaults for TCP keepalive settings.
// We are on Go 1.23+ and can use KeepAliveConfig directly instead of the old KeepAlive/Control hacks.
}
grpcSecureOpt,
grpc.WithContextDialer(contextDialer),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxInternodeRecvPayloadSize)),
grpc.WithChainUnaryInterceptor(
headersInterceptor,
metrics.NewClientMetricsTrailerPropagatorInterceptor(logger),
errorInterceptor,
),
grpc.WithChainStreamInterceptor(
interceptor.StreamErrorInterceptor,
),
grpc.WithDefaultServiceConfig(DefaultServiceConfig),
grpc.WithDisableServiceConfig(),
grpc.WithConnectParams(cp),
}
dialOptions = append(dialOptions, opts...)
return grpc.NewClient(hostName, dialOptions...)
}
// in this regard; on that platform, `SO_REUSEADDR` has a different meaning and
// should not be set (setting it may have unpredictable consequences).
port, err := getFreePort("127.0.0.1")
if err != nil {
// try ipv6
port, err = getFreePort("[::1]")
}
}
}
l, err := net.Listen("tcp", host+":0")
if err != nil {
return 0, fmt.Errorf("failed to assign a free port: %v", err)
}
port := l.Addr().(*net.TCPAddr).Port
// On Linux and some BSD variants, ephemeral ports are randomized, and may
// consequently repeat within a short time frame after the listening end
// has been closed. To avoid this, we make a connection to the port, then
// close that connection from the server's side (this is very important),
// which puts the connection in TIME_WAIT state for some time (by default,
// 60s on Linux). While it remains in that state, the OS will not reallocate
// that port number for bind(:0) syscalls, yet we are not prevented from
// explicitly binding to it (thanks to SO_REUSEADDR).
//
// On macOS and Windows, the above technique is not necessary, as the OS
// allocates ephemeral ports sequentially, meaning a port number will only
// be reused after the entire range has been exhausted. Quite the opposite,
// given that these OSes use a significantly smaller range for ephemeral
// ports, making an extra connection just to reserve a port might actually
// be harmful (by hastening ephemeral port exhaustion).
if runtime.GOOS != "darwin" && runtime.GOOS != "windows" {
r, err := net.DialTCP("tcp", nil, l.Addr().(*net.TCPAddr))
if err != nil {
return 0, fmt.Errorf("failed to assign a free port: %v", err)
}
if err != nil {
return 0, fmt.Errorf("failed to assign a free port: %v", err)
}
// Closing the socket from the server side
defer r.Close()
}
}
func (f *historyEngineFactory) CreateEngine(
shard historyi.ShardContext,
return NewEngineWithShardContext(
shard,
f.ClientBean,
f.MatchingClient,
f.SdkClientFactory,
f.EventNotifier,
f.Config,
f.VersionMembershipCache,
f.WorkerDeploymentClient,
f.RoutingInfoCache,
f.RawMatchingClient,
f.WorkflowCache,
f.ReplicationProgressCache,
f.Serializer,
f.QueueFactories,
f.ReplicationTaskFetcherFactory,
f.ReplicationTaskExecutorProvider,
api.NewWorkflowConsistencyChecker(shard, f.WorkflowCache),
f.TracerProvider,
f.PersistenceVisibilityMgr,
f.EventBlobCache,
f.TaskCategoryRegistry,
f.ReplicationDLQWriter,
f.CommandHandlerRegistry,
f.ChasmWorkflowRegistry,
f.OutboundQueueCBPool,
f.PersistenceRateLimiter,
f.TestHooks,
f.ChasmEngine,
)
}
// New returns a new instance as daemon
return &Processor{
sdkClientFactory: params.SdkClientFactory,
metricsHandler: params.MetricsHandler.WithTags(metrics.OperationTag(metrics.ParentClosePolicyProcessorScope)),
cfg: params.Config,
logger: log.With(params.Logger, tag.ComponentBatcher),
clientBean: params.ClientBean,
currentCluster: params.CurrentCluster,
hostInfo: params.HostInfo,
}
}
// Start starts the scanner
svcClient := s.sdkClientFactory.GetSystemClient()
processorWorker := s.sdkClientFactory.NewWorker(svcClient, processorTaskQueueName, getWorkerOptions(s))
processorWorker.RegisterWorkflowWithOptions(ProcessorWorkflow, workflow.RegisterOptions{Name: processorWFTypeName})
processorWorker.RegisterActivityWithOptions(ProcessorActivity, activity.RegisterOptions{Name: processorActivityName})
return processorWorker.Start()
}
ctx := context.WithValue(context.Background(), processorContextKey, p)
ctx = headers.SetCallerInfo(ctx, headers.SystemBackgroundHighCallerInfo)
return worker.Options{
MaxConcurrentActivityExecutionSize: p.cfg.MaxConcurrentActivityExecutionSize(),
MaxConcurrentWorkflowTaskExecutionSize: p.cfg.MaxConcurrentWorkflowTaskExecutionSize(),
MaxConcurrentActivityTaskPollers: p.cfg.MaxConcurrentActivityTaskPollers(),
MaxConcurrentWorkflowTaskPollers: p.cfg.MaxConcurrentWorkflowTaskPollers(),
BackgroundActivityContext: ctx,
Identity: "temporal-system@" + p.hostInfo.Identity(),
}
}
InvocationTaskHandler *invocationTaskHandler,
BackoffTaskHandler *backoffTaskHandler,
return &Library{
InvocationTaskHandler: InvocationTaskHandler,
BackoffTaskHandler: BackoffTaskHandler,
}
}
return chasm.CallbackLibraryName
}
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*Callback](
chasm.CallbackComponentName,
chasm.WithDetached(),
),
}
}
return []*chasm.RegistrableTask{
chasm.NewRegistrableSideEffectTask(
"invoke",
l.InvocationTaskHandler,
),
chasm.NewRegistrablePureTask(
"backoff",
l.BackoffTaskHandler,
),
}
}
}
// deepCopyForMapstructure does a simple deep copy of T. Fancy cases (anything other than plain old data)
// is not handled and will panic.
// nolint:revive // this will be triggered from a static initializer before it can be triggered from production code
return deepCopyValue(reflect.ValueOf(t)).Interface().(T)
}
switch v.Kind() {
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
nv := reflect.New(v.Type()).Elem()
nv.Set(v)
return nv
case reflect.Array:
nv := reflect.New(v.Type()).Elem()
}
return deepCopyValue(v.Elem()).Addr()
if v.IsNil() {
return v
}
nv := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
for i := range v.Len() {
}
return nv
// Special case for time.Time: it has unexported fields so we can't copy it field by
// field, but we can copy zero values (which is all we need for default values).
if v.Type() == reflect.TypeFor[time.Time]() {
if v.Interface().(time.Time).IsZero() {
return reflect.ValueOf(time.Time{})
}
// nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code
panic(fmt.Sprintf("Can't deep copy non-zero time.Time: %v", v.Interface()))
}
for i := range v.Type().NumField() {
nv.Field(i).Set(deepCopyValue(v.Field(i)))
}
return nv
case reflect.Interface, reflect.Func, reflect.Chan:
// only nil values of any other reference types allowed!
if v.IsNil() {
return v
}
fallthrough
default:
metricsHandler metrics.Handler,
logger log.Logger,
rateBurst := quotas.NewDefaultRateBurst(rateFn, quotas.BurstRatioFn(burstRatio))
limiter := &HealthRequestRateLimiterImpl{
enabled: atomic.Bool{},
rateLimiter: quotas.NewRateLimiter(rateBurst.Rate(), rateBurst.Burst()),
healthSignals: healthSignals,
rateBurst: rateBurst,
params: params,
refreshTimer: time.NewTicker(DefaultRefreshInterval),
metricsHandler: metricsHandler,
logger: logger,
}
curRateMultiplier := new(float64)
*curRateMultiplier = DefaultInitialRateMultiplier
limiter.curRateMultiplier.Store(curRateMultiplier)
limiter.refreshDynamicParams()
return limiter
}
func (rl *HealthRequestRateLimiterImpl) Allow(now time.Time, request quotas.Request) bool {
}
func (rl *HealthRequestRateLimiterImpl) Reserve(now time.Time, request quotas.Request) quotas.Reservation {
health_request_rate_limiter.go
rl.maybeRefresh()
if !rl.enabled.Load() {
return quotas.NoopReservation
}
return rl.rateLimiter.ReserveN(now, request.Token)
}
}
select {
case <-rl.refreshTimer.C:
rl.refreshDynamicParams()
}
func (rl *HealthRequestRateLimiterImpl) refreshDynamicParams() {
health_request_rate_limiter.go
options := rl.params()
rl.enabled.Store(options.Enabled)
rl.curOptions.Store(&options)
}
func (rl *HealthRequestRateLimiterImpl) updateRefreshTimer() {
// encodingTypeFromEnv returns an EncodingType based on the environment variable `TEMPORAL_TEST_DATA_ENCODING`.
// It defaults to "ENCODING_TYPE_PROTO3" codec if the environment variable is not set.
codecType := os.Getenv(SerializerDataEncodingEnvVar)
switch strings.ToLower(codecType) {
return enumspb.ENCODING_TYPE_PROTO3
case "json":
return enumspb.ENCODING_TYPE_JSON
}
blob, err := codec.NewJSONPBEncoder().Encode(m)
if err != nil {
return nil, err
}
Data: blob,
EncodingType: enumspb.ENCODING_TYPE_JSON,
}, nil
data, err := proto.MarshalOptions{Deterministic: opts.deterministic}.Marshal(m)
if err != nil {
return nil, NewSerializationError(enumspb.ENCODING_TYPE_PROTO3, err)
}
EncodingType: enumspb.ENCODING_TYPE_PROTO3,
Data: data,
}, nil
default:
return nil, NewUnknownEncodingTypeError(encoding.String(), enumspb.ENCODING_TYPE_JSON, enumspb.ENCODING_TYPE_PROTO3)
}
if data == nil {
return NewDeserializationError(enumspb.ENCODING_TYPE_UNSPECIFIED, errors.New("cannot decode nil"))
}
case enumspb.ENCODING_TYPE_JSON:
return codec.NewJSONPBEncoder().Decode(data.Data, result)
err := proto.Unmarshal(data.Data, result)
if err != nil {
return NewDeserializationError(enumspb.ENCODING_TYPE_PROTO3, err)
}
default:
return NewUnknownEncodingTypeError(data.EncodingType.String(), enumspb.ENCODING_TYPE_JSON, enumspb.ENCODING_TYPE_PROTO3)
logger log.Logger,
renewRangeIDFn renewRangeIDFn,
return &taskKeyGenerator{
nextTaskID: taskIDUninitialized,
exclusiveMaxTaskID: taskIDUninitialized,
rangeSizeBits: rangeSizeBits,
timeSource: timeSource,
logger: logger,
renewRangeIDFn: renewRangeIDFn,
}
}
func (a *taskKeyGenerator) setTaskKeys(
func (a *taskKeyGenerator) peekTaskKey(
category tasks.Category,
switch category.Type() {
return tasks.NewImmediateKey(a.nextTaskID)
return tasks.NewKey(
a.taskMinScheduledTime,
a.nextTaskID,
)
default:
panic(fmt.Sprintf("Unknown category type: %v", category.Type()))
}
a.nextTaskID = rangeID << a.rangeSizeBits
a.exclusiveMaxTaskID = (rangeID + 1) << a.rangeSizeBits
a.logger.Info("Task key range updated",
tag.Number(a.nextTaskID),
tag.NextNumber(a.exclusiveMaxTaskID),
)
}
func (a *taskKeyGenerator) setTaskMinScheduledTime(
taskMinScheduledTime time.Time,
a.taskMinScheduledTime = util.MaxTime(a.taskMinScheduledTime, taskMinScheduledTime)
}
func (a *taskKeyGenerator) generateTaskID() (int64, error) {
// GetValues returns header values for passed header names.
// It always returns slice of the same size as number of passed header names.
headerValues := make([]string, len(headerNames))
for i, headerName := range headerNames {
if values := metadata.ValueFromIncomingContext(ctx, headerName); len(values) > 0 {
}
}
}
}
return GRPCHeaderGetter{ctx: ctx}
}
// Get a single value from the underlying gRPC metadata.
// Returns an empty string if the metadata key is unset.
if values := metadata.ValueFromIncomingContext(h.ctx, key); len(values) > 0 {
return values[0]
}
}
// StripPrincipal removes principal headers from incoming metadata to prevent
// external callers from spoofing principal identity.
mdIncoming, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ctx
}
mdIncoming.Delete(PrincipalNameHeaderName)
return metadata.NewIncomingContext(ctx, mdIncoming)
}
// setIncomingMD sets the key-value pairs in the incoming metadata.
// Empty values are ignored.
mdIncoming, ok := metadata.FromIncomingContext(ctx)
if !ok {
}
if v != "" {
mdIncoming.Set(k, v)
}
}
}
)
b := make([]string, len(a))
for i, v := range a {
b[i] = f(v)
}
return b
}
return fmt.Sprintf(deleteMapQryTemplate, tableName)
}
func makeSetKeyInMapQry(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string {
execution_maps.go
return fmt.Sprintf(setKeyInMapQryTemplate,
tableName,
strings.Join(nonPrimaryKeyColumns, ","),
strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string {
return ":" + x
}), ","),
strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string {
return x + "=VALUES(" + x + ")"
}), ","),
mapKeyName)
}
return fmt.Sprintf(deleteKeyInMapQryTemplate,
tableName,
mapKeyName)
}
func makeGetMapQryTemplate(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string {
execution_maps.go
return fmt.Sprintf(getMapQryTemplate,
tableName,
mapKeyName,
strings.Join(nonPrimaryKeyColumns, ","))
}
var (
)
b := make([]string, len(a))
for i, v := range a {
b[i] = f(v)
}
return b
}
return fmt.Sprintf(deleteMapQueryTemplate, tableName)
}
func makeSetKeyInMapQry(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string {
execution_maps.go
return fmt.Sprintf(setKeyInMapQueryTemplate,
tableName,
strings.Join(nonPrimaryKeyColumns, ","),
strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string {
return ":" + x
}), ","),
mapKeyName,
return "excluded." + x
}), ","))
}
return fmt.Sprintf(deleteKeyInMapQueryTemplate,
tableName,
mapKeyName)
}
func makeGetMapQryTemplate(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string {
execution_maps.go
return fmt.Sprintf(getMapQueryTemplate,
tableName,
mapKeyName,
strings.Join(nonPrimaryKeyColumns, ","))
}
var (
)
b := make([]string, len(a))
for i, v := range a {
b[i] = f(v)
}
return b
}
return fmt.Sprintf(deleteMapQryTemplate, tableName)
}
func makeSetKeyInMapQry(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string {
execution_maps.go
return fmt.Sprintf(setKeyInMapQryTemplate,
tableName,
strings.Join(nonPrimaryKeyColumns, ","),
strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string {
return ":" + x
}), ","),
strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string {
return x + "=" + x
}), ","),
mapKeyName)
}
return fmt.Sprintf(deleteKeyInMapQryTemplate,
tableName,
mapKeyName)
}
func makeGetMapQryTemplate(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string {
execution_maps.go
return fmt.Sprintf(getMapQryTemplate,
tableName,
mapKeyName,
strings.Join(nonPrimaryKeyColumns, ","))
}
var (
registry *hsm.Registry,
options TaskExecutorOptions,
exec := taskExecutor{options}
if err := hsm.RegisterImmediateExecutor(
registry,
exec.executeInvocationTask,
); err != nil {
return err
}
registry,
exec.executeBackoffTask,
); err != nil {
return err
}
registry,
exec.executeScheduleToCloseTimeoutTask,
); err != nil {
return err
}
registry,
exec.executeScheduleToStartTimeoutTask,
); err != nil {
return err
}
registry,
exec.executeStartToCloseTimeoutTask,
); err != nil {
return err
}
registry,
exec.executeCancelationTask,
); err != nil {
return err
}
registry,
exec.executeCancelationBackoffTask,
)
}
)
return Key{
FireTime: DefaultFireTime,
TaskID: taskID,
}
}
return Key{
FireTime: fireTime,
TaskID: taskID,
}
}
func ValidateKey(key Key) error {
}
if left.FireTime.Before(right.FireTime) {
}
}
}
}
if k.TaskID == math.MaxInt64 {
if k.FireTime.UnixNano() == math.MaxInt64 {
panic("Key encountered positive overflow")
return NewKey(k.FireTime.Add(time.Nanosecond), 0)
}
}
}
func file_temporal_server_api_taskqueue_v1_message_proto_init() {
if File_temporal_server_api_taskqueue_v1_message_proto != nil {
return
}
file_temporal_server_api_taskqueue_v1_message_proto_msgTypes[0].OneofWrappers = []any{
message.pb.go
(*TaskVersionDirective_UseAssignmentRules)(nil),
(*TaskVersionDirective_AssignedBuildId)(nil),
}
file_temporal_server_api_taskqueue_v1_message_proto_msgTypes[5].OneofWrappers = []any{
(*TaskQueuePartition_NormalPartitionId)(nil),
(*TaskQueuePartition_StickyName)(nil),
(*TaskQueuePartition_WorkerCommands)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_taskqueue_v1_message_proto_rawDesc), len(file_temporal_server_api_taskqueue_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 16,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_taskqueue_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_taskqueue_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_taskqueue_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_taskqueue_v1_message_proto = out.File
file_temporal_server_api_taskqueue_v1_message_proto_goTypes = nil
file_temporal_server_api_taskqueue_v1_message_proto_depIdxs = nil
}
var _ log.Logger = (*SdkLogger)(nil)
if sl, ok := logger.(SkipLogger); ok {
logger = sl.Skip(extraSkipForSdkLogger)
}
logger: logger,
}
}
var tags []tag.Tag
for i := 0; i < len(keyvals); i++ {
tags = append(tags, t)
continue
}
if !keyIsString {
key = fmt.Sprintf("%v", keyvals[i])
}
if i+1 == len(keyvals) {
val = noValue
i++
}
}
}
}
l.logger.Info(msg, l.tags(keyvals)...)
}
l.logger.Warn(msg, l.tags(keyvals)...)
}
func (l *SdkLogger) Error(msg string, keyvals ...any) {
}
return NewSdkLogger(
With(l.logger, l.tags(keyvals)...))
}
}
return &connPool{
pool: make(map[string]entry),
}
}
// Allocate allocates the shared database in the pool or returns already exists instance with the same DSN. If instance
logger log.Logger,
create func(*config.SQL, resolver.ServiceResolver, log.Logger) (*sqlx.DB, error),
cp.mu.Lock()
defer cp.mu.Unlock()
dsn, err := buildDSN(cfg)
if err != nil {
return nil, err
}
return entry.db, nil
}
if err != nil {
return nil, err
}
return db, nil
}
// Close virtual connection to database. Only closes for real once no references left.
cp.mu.Lock()
defer cp.mu.Unlock()
dsn, err := buildDSN(cfg)
if err != nil {
return
}
if !ok {
// no such database
return
}
// todo: at the moment pool will persist a single connection to the DB for the whole duration of application
// temporal will start and stop DB connections multiple times, which will cause the loss of the cache
globalQuota func(ns string) int,
tokens map[string]int,
return &ConcurrentRequestLimitInterceptor{
namespaceRegistry: namespaceRegistry,
logger: logger,
quotaCalculator: calculator.NewLoggedNamespaceCalculator(
calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: memberCounter,
PerInstanceQuota: perInstanceQuota,
GlobalQuota: globalQuota,
},
log.With(logger, tag.ComponentLongPollHandler, tag.ScopeNamespace),
),
tokens: tokens,
activeTokensCount: make(map[string]*int32),
}
}
func (ni *ConcurrentRequestLimitInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
nsName := MustGetNamespaceName(ni.namespaceRegistry, req)
mh := GetMetricsHandlerFromContext(ctx, ni.logger)
cleanup, err := ni.Allow(nsName, info.FullMethod, mh, req)
defer cleanup()
if err != nil {
return nil, err
}
}
mh metrics.Handler,
req any,
// token will default to 0
token := ni.tokens[methodName]
if token == 0 {
}
// for GetWorkflowExecutionHistoryRequest, we only care about long poll requests
rateLimiter quotas.RequestRateLimiter,
tokens map[string]int,
return &RateLimitInterceptor{
rateLimiter: rateLimiter,
tokens: tokens,
}
}
func (i *RateLimitInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
methodName := info.FullMethod
// for DescribeTaskQueueRequest, we want to use visibility rate limit only if reachability is queried
describeTQReq, ok := req.(*workflowservice.DescribeTaskQueueRequest)
if ok && describeTQReq.GetReportTaskReachability() {
methodName += "WithReachability"
}
return nil, err
}
}
methodName string,
headerGetter headers.HeaderGetter,
token, ok := i.tokens[methodName]
if !ok {
}
// we don't want to apply rate limiter if a method is configured with 0 tokens.
return nil
}
methodName,
token,
headerGetter.Get(headers.CallerNameHeaderName),
headerGetter.Get(headers.CallerTypeHeaderName),
0, // this interceptor layer does not throttle based on caller segment
"", // this interceptor layer does not throttle based on call initiation
)) {
return RateLimitServerBusy
}
}
metricsHandler metrics.Handler,
timeSource clock.TimeSource,
return &ExecutionAwareScheduler[T]{
baseScheduler: baseScheduler,
executionQueueScheduler: newExecutionQueueScheduler(
options.MaxQueues,
options.QueueTTL,
options.QueueConcurrency,
queueKeyFn,
logger,
metricsHandler,
timeSource,
),
queueKeyFn: queueKeyFn,
options: options,
logger: logger,
}
}
s.baseScheduler.Start()
// Always start the executionQueueScheduler regardless of current config.
// The Enabled check gates task routing, so an idle scheduler has minimal
// overhead. This ensures if the config changes from disabled to enabled,
// tasks will be processed correctly.
s.executionQueueScheduler.Start()
}
s.baseScheduler.Stop()
s.executionQueueScheduler.Stop()
}
func (s *ExecutionAwareScheduler[T]) Submit(task T) {
var _ hsm.Task = ScheduleToCloseTimeoutTask{}
return TaskTypeScheduleToCloseTimeout
}
func (t ScheduleToCloseTimeoutTask) Deadline() time.Time {
var _ hsm.Task = InvocationTask{}
return TaskTypeInvocation
}
func (InvocationTask) Deadline() time.Time {
var _ hsm.Task = BackoffTask{}
return TaskTypeBackoff
}
func (t BackoffTask) Deadline() time.Time {
var _ hsm.Task = CancelationTask{}
return TaskTypeCancelation
}
func (CancelationTask) Deadline() time.Time {
var _ hsm.Task = CancelationBackoffTask{}
return TaskTypeCancelationBackoff
}
func (t CancelationBackoffTask) Deadline() time.Time {
var _ hsm.Task = ScheduleToStartTimeoutTask{}
return TaskTypeScheduleToStartTimeout
}
func (t ScheduleToStartTimeoutTask) Deadline() time.Time {
var _ hsm.Task = StartToCloseTimeoutTask{}
return TaskTypeStartToCloseTimeout
}
func (t StartToCloseTimeoutTask) Deadline() time.Time {
}
if err := reg.RegisterTaskSerializer(TaskTypeScheduleToCloseTimeout, TimeoutTaskSerializer{}); err != nil {
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeInvocation, InvocationTaskSerializer{}); err != nil {
tasks.go
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeBackoff, BackoffTaskSerializer{}); err != nil {
tasks.go
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeCancelation, CancelationTaskSerializer{}); err != nil {
tasks.go
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeCancelationBackoff, CancelationBackoffTaskSerializer{}); err != nil { // nolint:revive
tasks.go
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeScheduleToStartTimeout, ScheduleToStartTimeoutTaskSerializer{}); err != nil {
tasks.go
return err
}
return reg.RegisterTaskSerializer(TaskTypeStartToCloseTimeout, StartToCloseTimeoutTaskSerializer{})
tasks.go
}
func (*VersionHistoryItem) ProtoMessage() {}
mi := &file_temporal_server_api_history_v1_message_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
func (*VersionHistories) ProtoMessage() {}
mi := &file_temporal_server_api_history_v1_message_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_history_v1_message_proto_init() {
if File_temporal_server_api_history_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_history_v1_message_proto_rawDesc), len(file_temporal_server_api_history_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_history_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_history_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_history_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_history_v1_message_proto = out.File
file_temporal_server_api_history_v1_message_proto_goTypes = nil
file_temporal_server_api_history_v1_message_proto_depIdxs = nil
}
func (*QueueMetadata) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_queue_metadata_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
func init() { file_temporal_server_api_persistence_v1_queue_metadata_proto_init() }
queue_metadata.pb.go
func file_temporal_server_api_persistence_v1_queue_metadata_proto_init() {
if File_temporal_server_api_persistence_v1_queue_metadata_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_queue_metadata_proto_rawDesc), len(file_temporal_server_api_persistence_v1_queue_metadata_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_queue_metadata_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_queue_metadata_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_queue_metadata_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_queue_metadata_proto = out.File
file_temporal_server_api_persistence_v1_queue_metadata_proto_goTypes = nil
file_temporal_server_api_persistence_v1_queue_metadata_proto_depIdxs = nil
}
registry *chasm.Registry,
library *Library,
return registry.Register(library)
}
func endpointRegistryProvider(
logger log.Logger,
metricsHandler metrics.Handler,
registryConfig := commonnexus.NewEndpointRegistryConfig(dc)
return commonnexus.NewEndpointRegistry(
registryConfig,
matchingClient,
endpointManager,
logger,
metricsHandler,
)
}
func endpointRegistryLifetimeHooks(lc fx.Lifecycle, registry commonnexus.EndpointRegistry) {
fx.go
lc.Append(fx.StartStopHook(registry.StartLifecycle, registry.StopLifecycle))
}
// NexusTransportProvider allows customization of the HTTP transport used for Nexus requests.
type NexusTransportProvider func(namespaceID, serviceName string) http.RoundTripper
return func(namespaceID, serviceName string) http.RoundTripper {
return http.DefaultTransport
}
clusterMetadata cluster.Metadata,
rpcFactory common.RPCFactory,
cl, err := rpcFactory.CreateLocalFrontendHTTPClient()
if err != nil {
return nil, fmt.Errorf("cannot create local frontend HTTP client: %w", err)
}
if clusterInfo, ok := clusterMetadata.GetAllClusterInfo()[clusterMetadata.GetCurrentClusterName()]; ok {
clusterID = clusterInfo.ClusterID
}
m := collection.NewFallibleOnceMap(func(key clientProviderCacheKey) (*http.Client, error) {
transport := httpTransportProvider(key.namespaceID, key.endpointID)
return &http.Client{
})
return func(ctx context.Context, namespaceID string, entry *persistencespb.NexusEndpointEntry, service string) (*nexusrpc.HTTPClient, error) {
fx.go
var url string
var httpClient *http.Client
}
func nexusOperationProcessorAdapter[I any](processor NexusOperationProcessor[I]) func(ctx NexusOperationProcessorContext, input *commonpb.Payload) (*NexusOperationProcessorResult, error) {
nexus_operation_processor.go
return func(ctx NexusOperationProcessorContext, input *commonpb.Payload) (*NexusOperationProcessorResult, error) {
var i I
if err := sdkconverter.PreferProtoDataConverter.FromPayloads(&commonpb.Payloads{Payloads: []*commonpb.Payload{input}}, &i); err != nil {
// NewRegisterableNexusOperationProcessor wraps a typed NexusOperationProcessor and returns a registerable adapter.
func NewRegisterableNexusOperationProcessor[I any](op NexusOperationProcessor[I]) RegisterableNexusOperationProcessor {
nexus_operation_processor.go
return RegisterableNexusOperationProcessor{
processInput: nexusOperationProcessorAdapter(op),
}
}
// NexusServiceProcessor handles input processing for operations within a specific Nexus service.
// NewNexusServiceProcessor constructs a processor for a single Nexus service that can register and invoke operation
// processors by name.
func NewNexusServiceProcessor(name string) *NexusServiceProcessor {
nexus_operation_processor.go
return &NexusServiceProcessor{
name: name,
operations: make(map[string]RegisterableNexusOperationProcessor),
}
}
// RegisterOperation registers a named operation with this service processor.
// Returns an error if an operation with the same name is already registered.
func (p *NexusServiceProcessor) RegisterOperation(name string, op RegisterableNexusOperationProcessor) error {
nexus_operation_processor.go
if _, exists := p.operations[name]; exists {
return fmt.Errorf("operation %q already registered", name)
}
return nil
}
// MustRegisterOperation registers a named operation and panics if registration fails.
func (p *NexusServiceProcessor) MustRegisterOperation(name string, op RegisterableNexusOperationProcessor) {
nexus_operation_processor.go
if err := p.RegisterOperation(name, op); err != nil {
// nolint:forbidigo // Panic is acceptable here for Must-style method.
panic(err)
// NewNexusEndpointProcessor creates a new NexusEndpointProcessor.
return &NexusEndpointProcessor{
serviceProcessors: make(map[string]*NexusServiceProcessor),
}
}
// RegisterServiceProcessor adds a service-level processor to the endpoint keyed by its name.
// Returns an error if a processor with the same name is already registered.
func (p *NexusEndpointProcessor) RegisterServiceProcessor(processor *NexusServiceProcessor) error {
nexus_operation_processor.go
if _, exists := p.serviceProcessors[processor.name]; exists {
return fmt.Errorf("service processor %q already registered", processor.name)
}
return nil
}
}
func NewSQLStore(db sqlplugin.DB, logger log.Logger, serializer serialization.Serializer) SqlStore {
common.go
return SqlStore{
DB: db,
logger: logger,
serializer: serializer,
}
}
return m.DB.PluginName()
}
return m.DB.DbName()
}
if m.DB != nil {
err := m.DB.Close()
if err != nil {
m.logger.Error("Error closing SQL database", tag.Error(err))
}
}
func (m *SqlStore) txExecute(ctx context.Context, operation string, f func(tx sqlplugin.Tx) error) error {
common.go
tx, err := m.DB.BeginTx(ctx)
if err != nil {
return serviceerror.NewUnavailablef("%s failed. Failed to start transaction. Error: %v", operation, err)
}
if err != nil {
rollBackErr := tx.Rollback()
if rollBackErr != nil {
operation string,
err error,
if err == sql.ErrNoRows {
return serviceerror.NewNotFoundf("%v failed. Error: %v ", operation, err)
}
return serviceerror.NewUnavailablef("%v operation failed. Error: %v", operation, err)
metricsHandler metrics.Handler,
workflowIDToShardID func(namespace.ID, string) int32,
hashFn := func(key any) uint32 {
notification, ok := key.(Notification)
if !ok {
return uint32(workflowIDToShardID(namespace.ID(notification.ID.NamespaceID), notification.ID.WorkflowID))
}
timeSource: timeSource,
metricsHandler: metricsHandler.WithTags(metrics.OperationTag(metrics.HistoryEventNotificationScope)),
status: common.DaemonStatusInitialized,
closeChan: make(chan bool),
eventsChan: make(chan *Notification, eventsChanSize),
workflowIDToShardID: workflowIDToShardID,
eventsPubsubs: collection.NewShardedConcurrentTxMap(1024, hashFn),
}
}
}
for {
// send out metrics about the current number of messages in flight
metrics.HistoryEventNotificationInFlightMessageGauge.With(notifier.metricsHandler).Record(float64(len(notifier.eventsChan)))
select {
case event := <-notifier.eventsChan:
// send out metrics about message processing delay
notifier.dispatchHistoryEventNotification(event)
// shutdown
return
}
}
}
if !atomic.CompareAndSwapInt32(¬ifier.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
}
if !atomic.CompareAndSwapInt32(¬ifier.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
}
func (*QueueState) ProtoMessage() {}
mi := &file_temporal_server_api_persistence_v1_queues_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_persistence_v1_queues_proto_init() {
if File_temporal_server_api_persistence_v1_queues_proto != nil {
return
}
file_temporal_server_api_persistence_v1_predicates_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_queues_proto_rawDesc), len(file_temporal_server_api_persistence_v1_queues_proto_rawDesc)),
NumEnums: 0,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_queues_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_queues_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_queues_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_queues_proto = out.File
file_temporal_server_api_persistence_v1_queues_proto_goTypes = nil
file_temporal_server_api_persistence_v1_queues_proto_depIdxs = nil
}
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED
}
func (d ScheduledEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED
}
func (d CancelRequestedEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED
}
func (d CancelRequestCompletedEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED
}
func (d CancelRequestFailedEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_STARTED
}
func (d StartedEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_COMPLETED
}
func (d CompletedEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_FAILED
}
func (d FailedEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_CANCELED
}
func (d CanceledEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
}
return enumspb.EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT
}
func (d TimedOutEventDefinition) Apply(ctx chasm.MutableContext, wf *Workflow, event *historypb.HistoryEvent) error {
// the goroutine starts, which makes it possible for the goroutine to call
// Done() on itself (maybe indirectly) without a race condition.
ctx, cancel := context.WithCancel(ctx)
return &Handle{
context: ctx,
cancel: cancel,
done: make(chan struct{}),
}
}
// Go launches the supplied function in its own goroutine. Go should be called
// exactly once on each *Handle.
go func() {
// use defer here so that the channel is closed even if the func calls
// runtime.Goexit()
defer close(h.done)
if err := f(h.context); err != nil {
}
}()
}
// the Done() channel closing is the time taken by the goroutine to shut itself
// down.
return h.done
}
// Cancel requests that this goroutine stop by cancelling the associated context
// object. This function is threadsafe and idempotent. Note that this function
// _requests_ termination, it does not forcefully kill the goroutine.
h.cancel()
}
// Error observes the error returned by the func passed to Go (if any). There is
// never any error (i.e. this function returns nil) while the goroutine is
// running.
v := h.err.Load()
if v == nil {
return nil
}
}
namespaceRegistry namespace.Registry,
logger log.Logger,
return &MaskInternalErrorDetailsInterceptor{
maskInternalError: maskErrorSetting,
namespaceRegistry: namespaceRegistry,
workflowTags: logtags.NewWorkflowTags(tasktoken.NewSerializer(), logger),
logger: logger,
}
}
func (mi *MaskInternalErrorDetailsInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
resp, err := handler(ctx, req)
if err != nil && mi.shouldMaskErrors(req) {
}
}
func (mi *MaskInternalErrorDetailsInterceptor) shouldMaskErrors(req any) bool {
mask_internal_error.go
ns := MustGetNamespaceName(mi.namespaceRegistry, req)
if ns.IsEmpty() {
return false
}
}
func (mi *MaskInternalErrorDetailsInterceptor) maskUnknownOrInternalErrors(
req any, fullMethodName string, err error,
statusCode := serviceerror.ToStatus(err).Code()
if statusCode != codes.Unknown && statusCode != codes.Internal {
return err
}
// we need to log the original error with hash.
requestErrorHandler ErrorHandler,
additionalAllowedMethodsDuringHandover []string,
additional := make(map[string]struct{}, len(additionalAllowedMethodsDuringHandover))
for _, m := range additionalAllowedMethodsDuringHandover {
additional[m] = struct{}{}
}
enabledForNS: dynamicconfig.EnableNamespaceHandoverWait.Get(dc),
nsCacheRefreshInterval: dynamicconfig.NamespaceCacheRefreshInterval.Get(dc),
namespaceRegistry: namespaceRegistry,
metricsHandler: metricsHandler,
logger: logger,
timeSource: timeSource,
requestErrorHandler: requestErrorHandler,
additionalAllowedMethodsDuringHandover: additional,
}
}
// handlesMethod reports whether the handover gate applies to fullMethod: always for WorkflowService,
// plus any embedder-configured service prefixes.
func (i *NamespaceHandoverInterceptor) handlesMethod(fullMethod string) bool {
namespace_handover.go
if strings.HasPrefix(fullMethod, api.WorkflowServicePrefix) {
return true
}
for _, prefix := range i.additionalServicePrefixes {
if strings.HasPrefix(fullMethod, prefix) {
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
defer log.CapturePanic(i.logger, &retError)
if !i.handlesMethod(info.FullMethod) {
return handler(ctx, req)
}
// review which method is allowed
namespaceName := MustGetNamespaceName(i.namespaceRegistry, req)
if namespaceName != namespace.EmptyName && i.enabledForNS(namespaceName.String()) {
var waitTime *time.Duration
defer func() {
clientBean client.Bean,
taskExecutorProvider TaskExecutorProvider,
return newDLQHandler(
shard,
deleteManager,
workflowCache,
clientBean,
make(map[string]TaskExecutor),
taskExecutorProvider,
)
}
func newDLQHandler(
taskExecutors map[string]TaskExecutor,
taskExecutorProvider TaskExecutorProvider,
if taskExecutors == nil {
panic("Failed to initialize replication DLQ handler due to nil task executors")
}
shard: shard,
deleteManager: deleteManager,
workflowCache: workflowCache,
remoteHistoryFetcher: eventhandler.NewHistoryPaginatedFetcher(
shard.GetNamespaceRegistry(),
clientBean,
shard.GetPayloadSerializer(),
shard.GetLogger(),
),
taskExecutors: taskExecutors,
taskExecutorProvider: taskExecutorProvider,
logger: shard.GetLogger(),
}
}
fx.Provide(
ControllerProvider,
ContextFactoryProvider,
NewDefaultHandoverTrackerFactory,
fx.Annotate(
fx.ResultTags(`group:"deadlockDetectorRoots"`),
),
impl *ControllerImpl,
cfg *configs.Config,
return NewOwnershipBasedQuotaScaler(
impl,
int(cfg.NumberOfShards),
nil,
)
}),
fx.Provide(func(
impl *OwnershipBasedQuotaScalerImpl,
return impl
}),
fx.Provide(func() LazyLoadedOwnershipBasedQuotaScaler {
return LazyLoadedOwnershipBasedQuotaScaler{
Value: &atomic.Value{},
}
}),
fx.Invoke(initLazyLoadedOwnershipBasedQuotaScaler),
fx.Invoke(func(
lc fx.Lifecycle,
impl *OwnershipBasedQuotaScalerImpl,
lc.Append(fx.Hook{
OnStop: func(_ context.Context) error {
return nil
},
})
}),
ownershipBasedQuotaScaler OwnershipBasedQuotaScaler,
lazyLoadedOwnershipBasedQuotaScaler LazyLoadedOwnershipBasedQuotaScaler,
lazyLoadedOwnershipBasedQuotaScaler.Store(ownershipBasedQuotaScaler)
logger.Info("Initialized lazy loaded OwnershipBasedQuotaScaler", tag.Service(serviceName))
}
versionCache worker_versioning.VersionMembershipAndReactivationStatusCache,
testHooks testhooks.TestHooks,
return &transferQueueActiveTaskExecutor{
transferQueueTaskExecutorBase: newTransferQueueTaskExecutorBase(
shard,
workflowCache,
logger,
metricProvider,
historyRawClient,
matchingRawClient,
visibilityManager,
chasmEngine,
),
workflowResetter: ndc.NewWorkflowResetter(
shard,
workflowCache,
logger,
),
parentClosePolicyClient: parentclosepolicy.NewClient(
shard.GetMetricsHandler(),
shard.GetLogger(),
sdkClientFactory,
config.NumParentClosePolicySystemWorkflows(),
),
versionCache: versionCache,
testHooks: testHooks,
}
}
func (t *transferQueueActiveTaskExecutor) Execute(
// NewClusterMetadataLoader creates a new [ClusterMetadataLoader] that loads cluster metadata from the database.
func NewClusterMetadataLoader(manager persistence.ClusterMetadataManager, logger log.Logger) *ClusterMetadataLoader {
cluster_metadata_loader.go
return &ClusterMetadataLoader{
manager: manager,
logger: logger,
}
}
// LoadAndMergeWithStaticConfig loads cluster metadata from the database and merges it with the static config.
func (c *ClusterMetadataLoader) LoadAndMergeWithStaticConfig(ctx context.Context, svc *config.Config) error {
cluster_metadata_loader.go
iter := cluster.GetAllClustersIter(ctx, c.manager)
for iter.HasNext() {
item, err := iter.Next()
if err != nil {
return err
}
c.mergeMetadataFromDBWithStaticConfig(svc, item.ClusterName, newMetadata)
}
}
func (c *ClusterMetadataLoader) mergeMetadataFromDBWithStaticConfig(svc *config.Config, clusterName string, newMetadata *cluster.ClusterInformation) {
cluster_metadata_loader.go
c.backfillShardCount(svc, newMetadata)
if currentMetadata, ok := svc.ClusterMetadata.ClusterInformation[clusterName]; ok {
c.reconcileMetadata(svc, clusterName, currentMetadata, newMetadata)
}
svc.ClusterMetadata.ClusterInformation[clusterName] = *newMetadata
}
currentMetadata cluster.ClusterInformation,
newMetadata *cluster.ClusterInformation,
if clusterName != svc.ClusterMetadata.CurrentClusterName {
c.logger.Warn(
"ClusterInformation in static config is deprecated. Please use TCTL tool to configure remote cluster connections",
return
}
c.logger.Info(fmt.Sprintf("Use rpc address %v for cluster %v.", newMetadata.RPCAddress, clusterName))
}
// backfillShardCount is to add backward compatibility to the svc based cluster connection. It sets the shard count for
// newMetadata to the number of shards in the current cluster, if the shard count is not set in the database.
func (c *ClusterMetadataLoader) backfillShardCount(svc *config.Config, newMetadata *cluster.ClusterInformation) {
cluster_metadata_loader.go
if newMetadata.ShardCount == 0 {
newMetadata.ShardCount = svc.Persistence.NumHistoryShards
}
)
// WithConfig sets a custom configuration
return applyFunc(func(s *serverOptions) {
s.config = cfg
})
}
// ForServices indicates which supplied services (e.g. frontend, history, matching, worker) within the server to start
return applyFunc(func(s *serverOptions) {
s.serviceNames = make(map[primitives.ServiceName]struct{})
for _, name := range names {
s.serviceNames[primitives.ServiceName(name)] = struct{}{}
}
})
}
// WithLogger sets a custom logger
return applyFunc(func(s *serverOptions) {
s.logger = logger
})
}
// WithAuthorizer sets a low level authorizer to allow/deny all API calls
return applyFunc(func(s *serverOptions) {
s.authorizer = authorizer
})
}
// WithClaimMapper configures a role mapper for authorization
func WithClaimMapper(claimMapper func(cfg *config.Config) authorization.ClaimMapper) ServerOption {
server_option.go
return applyFunc(func(s *serverOptions) {
s.claimMapper = claimMapper(s.config)
})
}
// WithDynamicConfigClient sets custom client for reading dynamic configuration.
return applyFunc(func(s *serverOptions) {
s.dynamicConfigClient = c
})
}
}
return file_temporal_server_api_enums_v1_common_proto_enumTypes[1].Descriptor()
}
func (ChecksumFlavor) Type() protoreflect.EnumType {
}
return file_temporal_server_api_enums_v1_common_proto_enumTypes[2].Descriptor()
}
func (CallbackState) Type() protoreflect.EnumType {
}
func file_temporal_server_api_enums_v1_common_proto_init() {
if File_temporal_server_api_enums_v1_common_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_common_proto_rawDesc), len(file_temporal_server_api_enums_v1_common_proto_rawDesc)),
NumEnums: 3,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_common_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_common_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_common_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_common_proto = out.File
file_temporal_server_api_enums_v1_common_proto_goTypes = nil
file_temporal_server_api_enums_v1_common_proto_depIdxs = nil
}
}
return file_temporal_server_api_enums_v1_task_proto_enumTypes[1].Descriptor()
}
func (TaskType) Type() protoreflect.EnumType {
}
return file_temporal_server_api_enums_v1_task_proto_enumTypes[2].Descriptor()
}
func (TaskPriority) Type() protoreflect.EnumType {
}
func file_temporal_server_api_enums_v1_task_proto_init() {
if File_temporal_server_api_enums_v1_task_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_task_proto_rawDesc), len(file_temporal_server_api_enums_v1_task_proto_rawDesc)),
NumEnums: 3,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_task_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_task_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_task_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_task_proto = out.File
file_temporal_server_api_enums_v1_task_proto_goTypes = nil
file_temporal_server_api_enums_v1_task_proto_depIdxs = nil
}
}
return file_temporal_server_api_enums_v1_workflow_proto_enumTypes[0].Descriptor()
}
func (WorkflowExecutionState) Type() protoreflect.EnumType {
}
return file_temporal_server_api_enums_v1_workflow_proto_enumTypes[1].Descriptor()
}
func (WorkflowBackoffType) Type() protoreflect.EnumType {
}
func file_temporal_server_api_enums_v1_workflow_proto_init() {
if File_temporal_server_api_enums_v1_workflow_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_workflow_proto_rawDesc), len(file_temporal_server_api_enums_v1_workflow_proto_rawDesc)),
NumEnums: 3,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_workflow_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_workflow_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_workflow_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_workflow_proto = out.File
file_temporal_server_api_enums_v1_workflow_proto_goTypes = nil
file_temporal_server_api_enums_v1_workflow_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_init() }
message.pb.go
func file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_init() {
if File_temporal_server_chasm_lib_scheduler_proto_v1_message_proto != nil {
return
}
file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_msgTypes[4].OneofWrappers = []any{
(*BackfillerState_BackfillRequest)(nil),
(*BackfillerState_TriggerRequest)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_rawDesc), len(file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_scheduler_proto_v1_message_proto = out.File
file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_goTypes = nil
file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_depIdxs = nil
}
func newPartitionCache(
metricsHandler metrics.Handler,
return &partitionCache{
metricsHandler: metricsHandler,
}
}
for i := range c.shards {
c.shards[i].rotate()
}
c.rotate = goro.NewHandle(context.Background()).Go(func(ctx context.Context) error {
t := time.NewTicker(partitionCacheRotateInterval / partitionCacheNumShards)
defer t.Stop()
for i := 0; ; i = (i + 1) % partitionCacheNumShards {
select {
case <-t.C:
c.shards[i].rotate()
c.emitMetrics()
return ctx.Err()
}
}
}
c.rotate.Cancel()
<-c.rotate.Done()
}
func (c *partitionCache) emitMetrics() {
}
s.lock.Lock()
defer s.lock.Unlock()
s.prev = s.active
s.active = make(map[string]PartitionCounts)
}
func (s *partitionCacheShard) size() int {
// NewSDKVersionInterceptor creates a new SDKVersionInterceptor with default max set size
return &SDKVersionInterceptor{
sdkInfoSet: make(map[versioninfo.SDKInfo]struct{}),
versionChecker: headers.NewDefaultVersionChecker(),
maxSetSize: defaultMaxSetSize,
}
}
// Intercept a grpc request
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
sdkName, sdkVersion := headers.GetClientNameAndVersion(ctx)
if sdkName != "" && sdkVersion != "" {
vi.RecordSDKInfo(sdkName, sdkVersion)
if err := vi.versionChecker.ClientSupported(ctx); err != nil {
return nil, err
}
}
}
// RecordSDKInfo records name and version tuple in memory
info := versioninfo.SDKInfo{Name: name, Version: version}
vi.RLock()
overCap := len(vi.sdkInfoSet) >= vi.maxSetSize
_, found := vi.sdkInfoSet[info]
vi.RUnlock()
if !overCap && !found {
vi.Lock()
vi.sdkInfoSet[info] = struct{}{}
vi.Unlock()
}
}
persistenceNamespaceRateFn quotas.NamespaceRateFn,
persistenceHostRateFn quotas.RateFn,
namespaceRateFnWithFallback := func(namespace string) float64 {
if rate := namespaceRateFn(namespace); rate > 0 {
return rate
}
if rate := hostRateFn(); rate > 0 {
return rate
}
}
// NOTE: task scheduler will use the string format for task priority as the caller type.
// see channelQuotaRequestFn in scheduler.go
}
priorityToRateLimiters := make(map[int]quotas.RequestRateLimiter, len(tasks.PriorityName))
scheduler_quotas.go
for priority := range tasks.PriorityName {
priorityToRateLimiters[int(priority)] = newTaskRequestRateLimiter(
namespaceRateFnWithFallback,
hostRateFnWithFallback,
)
}
priorityLimiter := quotas.NewPriorityRateLimiter(requestPriorityFn, priorityToRateLimiters)
scheduler_quotas.go
return priorityLimiter, nil
}
namespaceRateFn quotas.NamespaceRateFn,
hostRateFn quotas.RateFn,
hostRequestRateLimiter := quotas.NewRequestRateLimiterAdapter(
quotas.NewDefaultIncomingRateLimiter(hostRateFn),
)
namespaceRequestRateLimiterFn := func(req quotas.Request) quotas.RequestRateLimiter {
if len(req.Caller) == 0 {
return quotas.NoopRequestRateLimiter
}
quotas.NewNamespaceRequestRateLimiter(namespaceRequestRateLimiterFn),
hostRequestRateLimiter,
)
}
perInstanceQuota func() int,
globalQuota func() int,
return &OwnershipAwareQuotaCalculator{
ClusterAwareQuotaCalculator: calculator.ClusterAwareQuotaCalculator{
MemberCounter: memberCounter,
PerInstanceQuota: perInstanceQuota,
GlobalQuota: globalQuota,
},
scaler: scaler,
}
}
func (c *OwnershipAwareQuotaCalculator) GetQuota() float64 {
ownership_based_quota_calculator.go
if quota, ok := getOwnershipScaledQuota(c.scaler, c.GlobalQuota()); ok {
return quota
}
}
perInstanceQuota func(namespace string) int,
globalQuota func(namespace string) int,
return &OwnershipAwareNamespaceQuotaCalculator{
ClusterAwareNamespaceQuotaCalculator: calculator.ClusterAwareNamespaceQuotaCalculator{
MemberCounter: memberCounter,
PerInstanceQuota: perInstanceQuota,
GlobalQuota: globalQuota,
},
scaler: scaler,
}
}
func (c *OwnershipAwareNamespaceQuotaCalculator) GetQuota(namespace string) float64 {
scaler OwnershipBasedQuotaScaler,
globalLimit int,
if globalLimit > 0 && scaler != nil {
if scaleFactor, ok := scaler.ScaleFactor(); ok {
return scaleFactor * float64(globalLimit), true
}
}
}
}
func init() { file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_init() }
activity_state.pb.go
func file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_init() {
if File_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto != nil {
return
}
file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_msgTypes[7].OneofWrappers = []any{
activity_state.pb.go
(*ActivityOutcome_Successful_)(nil),
(*ActivityOutcome_Failed_)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_rawDesc), len(file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_rawDesc)),
NumEnums: 2,
NumMessages: 11,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_depIdxs,
EnumInfos: file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_enumTypes,
MessageInfos: file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto = out.File
file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_goTypes = nil
file_temporal_server_chasm_lib_activity_proto_v1_activity_state_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_init() }
operation.pb.go
func file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_init() {
if File_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto != nil {
return
}
file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_msgTypes[2].OneofWrappers = []any{
operation.pb.go
(*OperationOutcome_Successful_)(nil),
(*OperationOutcome_Failed_)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_rawDesc), len(file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_rawDesc)),
NumEnums: 2,
NumMessages: 8,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_depIdxs,
EnumInfos: file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_enumTypes,
MessageInfos: file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto = out.File
file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_goTypes = nil
file_temporal_server_chasm_lib_nexusoperation_proto_v1_operation_proto_depIdxs = nil
}
logger log.Logger,
throttledLogger log.Logger,
return &metricClient{
client: client,
metricsHandler: metricsHandler,
logger: logger,
throttledLogger: throttledLogger,
}
}
func (c *metricClient) AddActivityTask(
ctx context.Context,
operation string,
caller := headers.GetCallerInfo(ctx).CallerName
handler := c.metricsHandler.WithTags(metrics.OperationTag(operation), metrics.NamespaceTag(caller), metrics.ServiceRoleTag(metrics.MatchingRoleTagValue))
metrics.ClientRequests.With(handler).Record(1)
return handler, time.Now().UTC()
}
func (c *metricClient) finishMetricsRecording(
startTime time.Time,
err error,
if err != nil {
switch err.(type) {
case *serviceerrors.StickyWorkerUnavailable,
*serviceerror.Canceled,
*serviceerror.ResourceExhausted:
// noop - not interest and too many logs
c.throttledLogger.Info("matching client encountered error", tag.Error(err), tag.ServiceErrorType(err))
}
metrics.ClientFailures.With(metricsHandler).Record(1, metrics.ServiceErrorTypeTag(err))
metric_client.go
}
}
// Stop forwards a deterministic shutdown to the wrapped client. See
// clientImpl.Stop. It is only invoked via client.Bean.Close.
if s, ok := c.client.(interface{ Stop() }); ok {
s.Stop()
}
}
)
// This must be called in init to avoid race conditions.
resolver.Register(&globalGrpcBuilder)
}
// Most code should not use this, this is only exposed for code that has to recognize and use a
// grpc membership url outside of grpc.
return globalGrpcBuilder.getServiceResolver(u)
}
// This should only be used in unit tests. For normal code, use the *GRPCResolver provided by fx.
}
res := &GRPCResolver{monitor: monitor}
globalGrpcBuilder.resolvers.Store(fmt.Sprintf("%p", res), res)
return res
}
return fmt.Sprintf("%s://%s%s%p", grpcResolverScheme, string(service), delim, g)
}
return grpcResolverScheme
}
func (m *grpcBuilder) getServiceResolver(u *url.URL) (ServiceResolver, error) {
grpc_resolver.go
if u.Scheme != grpcResolverScheme {
return nil, errInvalidUrl
}
if !found {
return nil, errInvalidUrl
}
if !ok {
return nil, errNotInitialized
}
}
}
return &grpcStatsHandler{
mh: mh,
}
}
func (h *grpcStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
grpc_stats.go
return ctx
}
switch stat.(type) {
case *stats.ConnBegin:
ServiceConnAccepted.With(h.mh).Record(1)
newVal := h.activeConns.Add(1)
ServiceConnActive.With(h.mh).Record(float64(newVal))
ServiceConnClosed.With(h.mh).Record(1)
newVal := h.activeConns.Add(-1)
if newVal < 0 { // should never happen, but just in case
h.activeConns.Store(0)
newVal = 0
}
}
}
func (h *grpcStatsHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) context.Context {
grpc_stats.go
return ctx
}
// nothing to do here
}
hostInfoProvider membership.HostInfoProvider,
taskCategoryRegistry tasks.TaskCategoryRegistry,
return &DLQMetricsEmitter{
status: common.DaemonStatusInitialized,
shutdownCh: make(chan struct{}),
metricsHandler: metricsHandler,
emitMetricsTimer: time.NewTicker(emitDLQMetricsInterval),
logger: logger,
historyTaskQueueManager: manager,
historyServiceResolver: historyServiceResolver,
hostInfoProvider: hostInfoProvider,
taskCategoryRegistry: taskCategoryRegistry,
}
}
if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
}
if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
s.emitMetricsTimer.Stop()
}
for {
select {
return
case <-s.emitMetricsTimer.C:
if s.shouldEmitMetrics() {
logger log.Logger,
serializer serialization.Serializer,
return &sqlNexusEndpointStore{
SqlStore: NewSQLStore(db, logger, serializer),
}, nil
}
func (s *sqlNexusEndpointStore) CreateOrUpdateNexusEndpoint(
ctx context.Context,
request *p.ListNexusEndpointsRequest,
lastID := emptyID
if len(request.NextPageToken) > 0 {
token, err := deserializePageTokenJson[listEndpointsNextPageToken](request.NextPageToken)
if err != nil {
}
var rows []sqlplugin.NexusEndpointsRow
retErr := s.txExecute(ctx, "ListNexusEndpoints", func(tx sqlplugin.Tx) error {
curTableVersion, err := tx.GetNexusEndpointsTableVersion(ctx)
if err != nil {
return err
}
if request.LastKnownTableVersion != 0 && request.LastKnownTableVersion != curTableVersion {
return p.ErrNexusTableVersionConflict
}
// PageSize could be zero when fetching just the table version.
rows, err = tx.ListNexusEndpoints(ctx, &sqlplugin.ListNexusEndpointsRequest{
}
})
return &response, retErr
}
if len(rows) > 0 && len(rows) == request.PageSize {
// len(rows) could be zero when fetching just the table version.
nextPageToken, retErr = serializePageTokenJson(&listEndpointsNextPageToken{
}
}
response.Endpoints = make([]p.InternalNexusEndpoint, len(rows))
for i, row := range rows {
response.Endpoints[i].ID = primitives.UUIDString(row.ID)
response.Endpoints[i].Version = row.Version
metricsHandler metrics.Handler,
timeSource clock.TimeSource,
s := &executionQueueScheduler[T]{
shutdownChan: make(chan struct{}),
maxQueues: maxQueues,
queueTTL: queueTTL,
queueConcurrency: queueConcurrency,
queueKeyFn: queueKeyFn,
logger: logger,
metricsHandler: metricsHandler,
timeSource: timeSource,
queues: make(map[any]*executionQueue[T]),
}
s.status.Store(common.DaemonStatusInitialized)
return s
}
if !s.status.CompareAndSwap(common.DaemonStatusInitialized, common.DaemonStatusStarted) {
return
}
}
if !s.status.CompareAndSwap(common.DaemonStatusStarted, common.DaemonStatusStopped) {
return
}
go func() {
if success := common.AwaitWaitGroup(&s.shutdownWG, time.Minute); !success {
s.logger.Warn("execution queue scheduler timed out waiting for goroutines")
}
}()
}
func (*VectorClock) ProtoMessage() {}
mi := &file_temporal_server_api_clock_v1_message_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_clock_v1_message_proto_init() {
if File_temporal_server_api_clock_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_clock_v1_message_proto_rawDesc), len(file_temporal_server_api_clock_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_clock_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_clock_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_clock_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_clock_v1_message_proto = out.File
file_temporal_server_api_clock_v1_message_proto_goTypes = nil
file_temporal_server_api_clock_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_persistence_v1_nexus_proto_init() {
if File_temporal_server_api_persistence_v1_nexus_proto != nil {
return
}
file_temporal_server_api_persistence_v1_nexus_proto_msgTypes[1].OneofWrappers = []any{
nexus.pb.go
(*NexusEndpointTarget_Worker_)(nil),
(*NexusEndpointTarget_External_)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_nexus_proto_rawDesc), len(file_temporal_server_api_persistence_v1_nexus_proto_rawDesc)),
NumEnums: 0,
NumMessages: 6,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_nexus_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_nexus_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_nexus_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_nexus_proto = out.File
file_temporal_server_api_persistence_v1_nexus_proto_goTypes = nil
file_temporal_server_api_persistence_v1_nexus_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_init() }
workflow_mutable_state.pb.go
func file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_init() {
if File_temporal_server_api_persistence_v1_workflow_mutable_state_proto != nil {
return
}
file_temporal_server_api_persistence_v1_executions_proto_init()
file_temporal_server_api_persistence_v1_hsm_proto_init()
file_temporal_server_api_persistence_v1_update_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_rawDesc), len(file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_rawDesc)),
NumEnums: 0,
NumMessages: 16,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_workflow_mutable_state_proto = out.File
file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_goTypes = nil
file_temporal_server_api_persistence_v1_workflow_mutable_state_proto_depIdxs = nil
}
}
func file_temporal_server_api_schedule_v1_message_proto_init() {
if File_temporal_server_api_schedule_v1_message_proto != nil {
return
}
file_temporal_server_api_schedule_v1_message_proto_msgTypes[7].OneofWrappers = []any{
message.pb.go
(*WatchWorkflowResponse_Result)(nil),
(*WatchWorkflowResponse_Failure)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_schedule_v1_message_proto_rawDesc), len(file_temporal_server_api_schedule_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 13,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_schedule_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_schedule_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_schedule_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_schedule_v1_message_proto = out.File
file_temporal_server_api_schedule_v1_message_proto_goTypes = nil
file_temporal_server_api_schedule_v1_message_proto_depIdxs = nil
}
func (*BaseExecutionInfo) ProtoMessage() {}
mi := &file_temporal_server_api_workflow_v1_message_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
}
func file_temporal_server_api_workflow_v1_message_proto_init() {
if File_temporal_server_api_workflow_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_workflow_v1_message_proto_rawDesc), len(file_temporal_server_api_workflow_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_workflow_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_workflow_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_workflow_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_workflow_v1_message_proto = out.File
file_temporal_server_api_workflow_v1_message_proto_goTypes = nil
file_temporal_server_api_workflow_v1_message_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_callback_proto_v1_message_proto_init() }
message.pb.go
func file_temporal_server_chasm_lib_callback_proto_v1_message_proto_init() {
if File_temporal_server_chasm_lib_callback_proto_v1_message_proto != nil {
return
}
file_temporal_server_chasm_lib_callback_proto_v1_message_proto_msgTypes[1].OneofWrappers = []any{
message.pb.go
(*Callback_Nexus_)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_callback_proto_v1_message_proto_rawDesc), len(file_temporal_server_chasm_lib_callback_proto_v1_message_proto_rawDesc)),
NumEnums: 1,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_callback_proto_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_callback_proto_v1_message_proto_depIdxs,
EnumInfos: file_temporal_server_chasm_lib_callback_proto_v1_message_proto_enumTypes,
MessageInfos: file_temporal_server_chasm_lib_callback_proto_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_callback_proto_v1_message_proto = out.File
file_temporal_server_chasm_lib_callback_proto_v1_message_proto_goTypes = nil
file_temporal_server_chasm_lib_callback_proto_v1_message_proto_depIdxs = nil
}
func NewBackgroundHighCallerInfo(
callerName string,
return CallerInfo{
CallerName: callerName,
CallerType: CallerTypeBackgroundHigh,
}
}
// NewBackgroundLowCallerInfo creates a new CallerInfo with BackgroundLow callerType
ctx context.Context,
info CallerInfo,
return setIncomingMD(ctx, map[string]string{
CallerNameHeaderName: info.CallerName,
CallerTypeHeaderName: info.CallerType,
CallOriginHeaderName: info.CallOrigin,
})
}
// SetCallerName set caller name in the context.
ctx context.Context,
callerType string,
return setIncomingMD(ctx, map[string]string{CallerTypeHeaderName: callerType})
}
// SetOrigin set call origin in the context.
func GetCallerInfo(
ctx context.Context,
values := GetValues(ctx, CallerNameHeaderName, CallerTypeHeaderName, CallOriginHeaderName)
return CallerInfo{
CallerName: values[0],
CallerType: values[1],
CallOrigin: values[2],
}
}
}
func NewDefaultReplicationResolverFactory() ReplicationResolverFactory {
replication_resolver.go
return func(detail *persistencespb.NamespaceDetail) ReplicationResolver {
// By convention, a namespace with non-zero failover version is a global namespace
// This can be overridden by WithGlobalFlag mutation if needed
isGlobal := detail.FailoverVersion != 0
return &defaultReplicationResolver{
replicationConfig: detail.ReplicationConfig,
isGlobalNamespace: isGlobal,
failoverVersion: detail.FailoverVersion,
failoverNotificationVersion: detail.FailoverNotificationVersion,
}
}
}
}
func (r *defaultReplicationResolver) ActiveInCluster(clusterName string) bool {
replication_resolver.go
if !r.IsGlobalNamespace() {
// "active" within each cluster
return true
}
return r.replicationConfig.ActiveClusterName == clusterName
}
}
return r.isGlobalNamespace
}
func (r *defaultReplicationResolver) FailoverVersion(businessID string) int64 {
}
r.isGlobalNamespace = isGlobal
}
func (r *defaultReplicationResolver) SetActiveCluster(clusterName string) {
serializer serialization.Serializer,
logger log.Logger,
return &nexusEndpointManagerImpl{
persistence: persistence,
serializer: serializer,
logger: logger,
}
}
func (m *nexusEndpointManagerImpl) GetName() string {
}
m.persistence.Close()
}
func (m *nexusEndpointManagerImpl) GetNexusEndpoint(
ctx context.Context,
request *ListNexusEndpointsRequest,
if request.PageSize < 0 {
return nil, ErrNegativeListNexusEndpointsPageSize
}
resp, err := m.persistence.ListNexusEndpoints(ctx, request)
if resp != nil {
result.TableVersion = resp.TableVersion
}
if err != nil {
return result, err
}
entries := make([]*persistencespb.NexusEndpointEntry, len(resp.Endpoints))
nexus_endpoint_manager.go
for i, entry := range resp.Endpoints {
endpoint, err := m.serializer.NexusEndpointFromBlob(entry.Data)
if err != nil {
}
result.Entries = entries
return result, nil
}
var _ sdkclient.MetricsHandler = &MetricsHandler{}
return &MetricsHandler{provider: provider}
}
func (m *MetricsHandler) WithTags(tags map[string]string) sdkclient.MetricsHandler {
metrics_handler.go
t := make([]metrics.Tag, 0, len(tags))
for k, v := range tags {
t = append(t, metrics.StringTag(k, v))
}
}
return &metricsCounter{name: name, provider: m.provider}
}
return &metricsGauge{name: name, provider: m.provider}
}
return &metricsTimer{name: name, provider: m.provider}
}
m.provider.Counter(m.name).Record(i)
}
func (m metricsGauge) Update(f float64) {
}
m.provider.Timer(m.name).Record(duration)
}
}
return file_temporal_server_api_enums_v1_nexus_proto_enumTypes[0].Descriptor()
}
func (NexusOperationState) Type() protoreflect.EnumType {
}
func file_temporal_server_api_enums_v1_nexus_proto_init() {
if File_temporal_server_api_enums_v1_nexus_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_nexus_proto_rawDesc), len(file_temporal_server_api_enums_v1_nexus_proto_rawDesc)),
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_nexus_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_nexus_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_nexus_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_nexus_proto = out.File
file_temporal_server_api_enums_v1_nexus_proto_goTypes = nil
file_temporal_server_api_enums_v1_nexus_proto_depIdxs = nil
}
}
return file_temporal_server_api_enums_v1_predicate_proto_enumTypes[0].Descriptor()
}
func (PredicateType) Type() protoreflect.EnumType {
}
func file_temporal_server_api_enums_v1_predicate_proto_init() {
if File_temporal_server_api_enums_v1_predicate_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_predicate_proto_rawDesc), len(file_temporal_server_api_enums_v1_predicate_proto_rawDesc)),
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_predicate_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_predicate_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_predicate_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_predicate_proto = out.File
file_temporal_server_api_enums_v1_predicate_proto_goTypes = nil
file_temporal_server_api_enums_v1_predicate_proto_depIdxs = nil
}
}
return file_temporal_server_api_enums_v1_workflow_task_type_proto_enumTypes[0].Descriptor()
}
func (WorkflowTaskType) Type() protoreflect.EnumType {
}
func init() { file_temporal_server_api_enums_v1_workflow_task_type_proto_init() }
workflow_task_type.pb.go
func file_temporal_server_api_enums_v1_workflow_task_type_proto_init() {
if File_temporal_server_api_enums_v1_workflow_task_type_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_workflow_task_type_proto_rawDesc), len(file_temporal_server_api_enums_v1_workflow_task_type_proto_rawDesc)),
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_workflow_task_type_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_workflow_task_type_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_workflow_task_type_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_workflow_task_type_proto = out.File
file_temporal_server_api_enums_v1_workflow_task_type_proto_goTypes = nil
file_temporal_server_api_enums_v1_workflow_task_type_proto_depIdxs = nil
}
}
return &Config{
BlobSizeLimitError: dynamicconfig.BlobSizeLimitError.Get(dc),
BlobSizeLimitWarn: dynamicconfig.BlobSizeLimitWarn.Get(dc),
BreakdownMetricsByTaskQueue: dynamicconfig.MetricsBreakdownByTaskQueue.Get(dc),
DefaultActivityRetryPolicy: dynamicconfig.DefaultActivityRetryPolicy.Get(dc),
EnableCallbacks: EnableCallbacks.Get(dc),
Enabled: Enabled.Get(dc),
LongPollBuffer: LongPollBuffer.Get(dc),
LongPollTimeout: LongPollTimeout.Get(dc),
MaxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
StartDelayEnabled: StartDelayEnabled.Get(dc),
MaxCallbacksPerExecution: callback.MaxPerExecution.Get(dc),
VisibilityMaxPageSize: dynamicconfig.FrontendVisibilityMaxPageSize.Get(dc),
}
}
// linkValidatorProvider builds the linkValidator from dynamic config.
return newLinkValidator(
dynamicconfig.FrontendMaxLinksPerRequest.Get(dc),
dynamicconfig.MaxLinksPerComponent.Get(dc),
dynamicconfig.FrontendLinkMaxSize.Get(dc),
)
}
}
func init() { file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_init() {
if File_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto != nil {
return
}
file_temporal_server_chasm_lib_scheduler_proto_v1_message_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_rawDesc), len(file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 18,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto = out.File
file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_goTypes = nil
file_temporal_server_chasm_lib_scheduler_proto_v1_request_response_proto_depIdxs = nil
}
}
func newNexusLibrary(config *nexusoperation.Config, nexusProcessor *chasm.NexusEndpointProcessor) *nexusLibrary {
nexus_library.go
return &nexusLibrary{config: config, nexusProcessor: nexusProcessor}
}
func (l *nexusLibrary) CommandHandlers() map[enumspb.CommandType]CommandHandler {
nexus_library.go
h := &nexusCommandHandler{config: l.config, nexusProcessor: l.nexusProcessor}
return map[enumspb.CommandType]CommandHandler{
enumspb.COMMAND_TYPE_SCHEDULE_NEXUS_OPERATION: h.handleScheduleCommand,
enumspb.COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION: h.handleCancelCommand,
}
}
return []EventDefinition{
ScheduledEventDefinition{},
CancelRequestedEventDefinition{},
CancelRequestCompletedEventDefinition{},
CancelRequestFailedEventDefinition{},
StartedEventDefinition{},
CompletedEventDefinition{},
FailedEventDefinition{},
CanceledEventDefinition{},
TimedOutEventDefinition{},
}
}
// Validate validates this config
if err := c.Persistence.Validate(); err != nil {
return err
}
return err
}
if hasIFE && (c.PublicClient.HostPort != "" || c.PublicClient.ForceTLSConfig != "" || c.PublicClient.HTTPHostPort != "") {
return fmt.Errorf("when using internal-frontend, publicClient must be empty")
}
case ForceTLSConfigAuto, ForceTLSConfigInternode, ForceTLSConfigFrontend:
default:
return fmt.Errorf("invalid value for publicClient.forceTLSConfig: %q", c.PublicClient.ForceTLSConfig)
}
}
// String converts the config object into a string
var buf bytes.Buffer
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2)
_ = encoder.Encode(c)
maskedYaml, _ := masker.MaskYaml(buf.String(), masker.DefaultYAMLFieldNames)
return maskedYaml
}
return r.Server.KeyFile != "" || r.Server.KeyData != ""
}
return len(r.Client.RootCAFiles) > 0 || len(r.Client.RootCAData) > 0 ||
r.Client.ForceTLS
}
func (p *JWTKeyProvider) HasSourceURIsConfigured() bool {
)
if path := hasSharedStructure(reflect.ValueOf(def), "root"); path != "" {
sharedStructureWarnings.Store(key, path)
}
}
// If you see this warning, it means that a default value used in New*TypedSetting has a
// non-nil slice or map in it. That can lead to confusing behavior since the value from
// dynamic config will be merged over the default value (e.g. the slice will be appended
// to, not replaced). If that behavior is desired, you can avoid this warning by using
// New*TypedSettingWithConverter and referring to dynamicconfig.ConvertStructure
// explicitly. Otherwise use nil slices and maps, including at the top level
// (so `[]string(nil)` instead of `[]string{}`).
logSharedStructureWarningsOnce.Do(func() {
sharedStructureWarnings.Range(func(key, path any) bool {
softassert.Fail(logger,
"default value contains shared structure",
}
// nolint:exhaustive // deliberately not exhaustive
switch v.Kind() {
case reflect.Map, reflect.Slice, reflect.Pointer:
if !v.IsNil() {
return path
}
if !v.IsNil() {
return hasSharedStructure(v.Elem(), path)
}
for i := range v.NumField() {
if p := hasSharedStructure(v.Field(i), path+"."+v.Type().Field(i).Name); p != "" {
return p
}
//
// Fatal/Panic/DPanic logs are always emitted without any throttling
func NewThrottledLogger(logger Logger, rps quotas.RateFn) *throttledLogger {
throttle_logger.go
if sl, ok := logger.(SkipLogger); ok {
logger = sl.Skip(extraSkipForThrottleLogger)
}
tl := &throttledLogger{
limiter: limiter,
logger: logger,
}
return tl
}
}
tl.rateLimit(func() {
tl.logger.Info(msg, tags...)
})
}
// Return a logger with the specified key-value pairs set, to be included in a subsequent normal logging call
result := &throttledLogger{
limiter: tl.limiter,
logger: With(tl.logger, tags...),
}
return result
}
if tl.limiter.Allow() {
f()
}
}
metricsHandler metrics.Handler,
serializer serialization.Serializer,
refDbConn := persistencesql.NewRefCountedDBConn(sqlplugin.DbKindVisibility, &cfg, r, logger, metricsHandler)
db, err := refDbConn.Get()
if err != nil {
return nil, err
}
sqlStore: persistencesql.NewSQLStore(db, logger, serializer),
searchAttributesProvider: searchAttributesProvider,
searchAttributesMapperProvider: searchAttributesMapperProvider,
chasmRegistry: chasmRegistry,
metricsHandler: metricsHandler,
logger: logger,
enableUnifiedQueryConverter: enableUnifiedQueryConverter,
}, nil
}
s.sqlStore.Close()
}
return s.sqlStore.GetName()
}
func convertSQLError(message string, err error) error {
}
return s.sqlStore.GetDbName()
}
func (s *VisibilityStore) ValidateCustomSearchAttributes(
// NewInitializer create a new instance of PProf Initializer
return &PProfInitializerImpl{
PProf: cfg,
Logger: logger,
}
}
// Start the pprof based on config
port := initializer.PProf.Port
if port == 0 {
initializer.Logger.Info("PProf not started due to port not set")
return nil
}
if host == "" {
// default to localhost which will favor ipv4 on dual stack
// environments - configure host as `::1` to bind on ipv6 localhost
host = "localhost"
}
if atomic.CompareAndSwapInt32(&pprofStatus, pprofNotInitialized, pprofInitialized) {
go func() {
initializer.Logger.Info("PProf listen on ", tag.Host(host), tag.Port(port))
err := http.ListenAndServe(hostPort, nil)
if err != nil {
initializer.Logger.Error("listen and serve err", tag.Error(err))
}
}()
}
}
extractors []RoutingKeyExtractorFunc,
logger log.Logger,
return &RoutingKeyInterceptor{
extractors: extractors,
logger: logger,
}
}
// WithExtractors returns a new interceptor with additional extractors prepended.
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
// Try each extractor until one returns a non-empty businessID
for _, extractor := range i.extractors {
if key := extractor(ctx, req, info.FullMethod); key.ID != "" || key.Strategy != namespace.RoutingStrategyDefault {
i.logger.Debug("routing key extraction: adding routing key to context",
routing_key_interceptor.go
tag.WorkflowID(key.ID),
tag.String("grpc-method", info.FullMethod),
)
ctx = AddRoutingKeyToContext(ctx, key)
break
}
}
}
// AddRoutingKeyToContext adds the routing Key to the context
func AddRoutingKeyToContext(ctx context.Context, routingKey namespace.RoutingKey) context.Context {
routing_key_interceptor.go
return context.WithValue(ctx, routingKeyCtxKey, routingKey)
}
// GetRoutingKeyFromContext retrieves the routing Key from the context.
// Returns a zero-value RoutingKey if not found.
func GetRoutingKeyFromContext(ctx context.Context) namespace.RoutingKey {
routing_key_interceptor.go
if key, ok := ctx.Value(routingKeyCtxKey).(namespace.RoutingKey); ok {
return key
}
}
// Start starts the handler
if !atomic.CompareAndSwapInt32(
&h.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
h.streamReceiverMonitor.Start()
// events notifier must starts before controller
h.eventNotifier.Start()
h.controller.Start()
h.dlqMetricsEmitter.Start()
}
// Stop stops the handler
if !atomic.CompareAndSwapInt32(
&h.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
h.replicationTaskFetcherFactory.Stop()
h.controller.Stop()
h.eventNotifier.Stop()
h.dlqMetricsEmitter.Stop()
}
visibilityManager manager.VisibilityManager,
chasmEngine chasm.Engine,
return &transferQueueTaskExecutorBase{
currentClusterName: shardContext.GetClusterMetadata().GetCurrentClusterName(),
shardContext: shardContext,
registry: shardContext.GetNamespaceRegistry(),
cache: workflowCache,
logger: logger,
metricHandler: metricHandler,
historyRawClient: historyRawClient,
matchingRawClient: matchingRawClient,
config: shardContext.GetConfig(),
searchAttributesProvider: shardContext.GetSearchAttributesProvider(),
visibilityManager: visibilityManager,
workflowDeleteManager: deletemanager.NewDeleteManager(
shardContext,
workflowCache,
shardContext.GetConfig(),
shardContext.GetTimeSource(),
visibilityManager,
),
chasmEngine: chasmEngine,
}
}
func (t *transferQueueTaskExecutorBase) pushActivity(
}
func init() { file_temporal_server_api_common_v1_api_category_proto_init() }
api_category.pb.go
func file_temporal_server_api_common_v1_api_category_proto_init() {
if File_temporal_server_api_common_v1_api_category_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_common_v1_api_category_proto_rawDesc), len(file_temporal_server_api_common_v1_api_category_proto_rawDesc)),
NumEnums: 1,
NumMessages: 1,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_temporal_server_api_common_v1_api_category_proto_goTypes,
DependencyIndexes: file_temporal_server_api_common_v1_api_category_proto_depIdxs,
EnumInfos: file_temporal_server_api_common_v1_api_category_proto_enumTypes,
MessageInfos: file_temporal_server_api_common_v1_api_category_proto_msgTypes,
ExtensionInfos: file_temporal_server_api_common_v1_api_category_proto_extTypes,
}.Build()
File_temporal_server_api_common_v1_api_category_proto = out.File
file_temporal_server_api_common_v1_api_category_proto_goTypes = nil
file_temporal_server_api_common_v1_api_category_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_init() {
if File_temporal_server_chasm_lib_activity_proto_v1_request_response_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_rawDesc), len(file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 20,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_activity_proto_v1_request_response_proto = out.File
file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_goTypes = nil
file_temporal_server_chasm_lib_activity_proto_v1_request_response_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_init() {
if File_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_rawDesc), len(file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto = out.File
file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_goTypes = nil
file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_init() {
if File_temporal_server_chasm_lib_tests_proto_v1_request_response_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_rawDesc), len(file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_tests_proto_v1_request_response_proto = out.File
file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_goTypes = nil
file_temporal_server_chasm_lib_tests_proto_v1_request_response_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_persistence_v1_task_queues_proto_init() }
task_queues.pb.go
func file_temporal_server_api_persistence_v1_task_queues_proto_init() {
if File_temporal_server_api_persistence_v1_task_queues_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_task_queues_proto_rawDesc), len(file_temporal_server_api_persistence_v1_task_queues_proto_rawDesc)),
NumEnums: 1,
NumMessages: 13,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_task_queues_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_task_queues_proto_depIdxs,
EnumInfos: file_temporal_server_api_persistence_v1_task_queues_proto_enumTypes,
MessageInfos: file_temporal_server_api_persistence_v1_task_queues_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_task_queues_proto = out.File
file_temporal_server_api_persistence_v1_task_queues_proto_goTypes = nil
file_temporal_server_api_persistence_v1_task_queues_proto_depIdxs = nil
}
}
func file_temporal_server_api_routing_v1_extension_proto_init() {
if File_temporal_server_api_routing_v1_extension_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_routing_v1_extension_proto_rawDesc), len(file_temporal_server_api_routing_v1_extension_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_temporal_server_api_routing_v1_extension_proto_goTypes,
DependencyIndexes: file_temporal_server_api_routing_v1_extension_proto_depIdxs,
MessageInfos: file_temporal_server_api_routing_v1_extension_proto_msgTypes,
ExtensionInfos: file_temporal_server_api_routing_v1_extension_proto_extTypes,
}.Build()
File_temporal_server_api_routing_v1_extension_proto = out.File
file_temporal_server_api_routing_v1_extension_proto_goTypes = nil
file_temporal_server_api_routing_v1_extension_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_init() }
tasks.pb.go
func file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_init() {
if File_temporal_server_chasm_lib_activity_proto_v1_tasks_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_rawDesc), len(file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_rawDesc)),
NumEnums: 2,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_depIdxs,
EnumInfos: file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_enumTypes,
MessageInfos: file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_activity_proto_v1_tasks_proto = out.File
file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_goTypes = nil
file_temporal_server_chasm_lib_activity_proto_v1_tasks_proto_depIdxs = nil
}
// MaskYaml replace password values with mask and returns copy of the string.
// Does recursive replacement for entire yamlStr.
fns := make(map[string]struct{}, len(fieldNamesToMask))
for _, fieldName := range fieldNamesToMask {
fns[fieldName] = struct{}{}
}
err := yaml.Unmarshal([]byte(yamlStr), &parsedYaml)
if err != nil {
return yamlStr, err
}
strBytes, err := yaml.Marshal(parsedYaml)
if err != nil {
return yamlStr, err
}
}
// RegisterPlugin will register a SQL plugin
if _, ok := supportedPlugins[pluginName]; ok {
panic("plugin " + pluginName + " already registered")
}
}
logger log.Logger,
mh metrics.Handler,
return createDB[sqlplugin.DB](dbKind, cfg, r, logger, mh)
}
// NewSQLAdminDB returns a AdminDB.
logger log.Logger,
mh metrics.Handler,
return createDB[sqlplugin.AdminDB](dbKind, cfg, r, logger, mh)
}
func createDB[T any](
logger log.Logger,
mh metrics.Handler,
var res T
plugin, err := getPlugin(cfg.PluginName)
if err != nil {
return res, err
}
if err != nil {
return res, err
}
//revive:disable-next-line:unchecked-type-assertion
return res, err
}
plugin, ok := supportedPlugins[pluginName]
if !ok {
keys := expmaps.Keys(supportedPlugins)
slices.Sort(keys)
r resolver.ServiceResolver,
logger log.Logger,
if err := checkMainDatabase(cfg, r, logger); err != nil {
return err
}
return checkVisibilityDatabase(cfg, r, logger)
}
return nil
}
r resolver.ServiceResolver,
logger log.Logger,
ds, ok := cfg.DataStores[cfg.DefaultStore]
if ok && ds.SQL != nil {
return checkCompatibleVersion(ds.SQL, r, sqlplugin.DbKindMain, logger)
}
return nil
}
r resolver.ServiceResolver,
logger log.Logger,
ds, ok := cfg.DataStores[cfg.VisibilityStore]
if ok && ds.SQL != nil {
return checkCompatibleVersion(ds.SQL, r, sqlplugin.DbKindVisibility, logger)
}
return nil
}
dbKind sqlplugin.DbKind,
logger log.Logger,
db, err := NewSQLAdminDB(dbKind, cfg, r, logger, metrics.NoopMetricsHandler)
if err != nil {
return err
}
}
// NewGroupByScheduler creates a new [GroupByScheduler] from given options.
func NewGroupByScheduler[K comparable, T Task](options GroupBySchedulerOptions[K, T]) *GroupByScheduler[K, T] {
group_by_scheduler.go
return &GroupByScheduler[K, T]{
options: options,
schedulers: make(map[K]RunnableScheduler),
}
}
// noop
}
// Stop signals running tasks to stop, aborts any pending tasks and waits up to a minute for all running tasks to
// complete.
if !s.stopped.CompareAndSwap(false, true) {
return
}
for _, lim := range s.schedulers {
lim.InitiateShutdown()
}
if success := common.BlockWithTimeout(s.waitShutdown, time.Minute); !success {
s.options.Logger.Warn("GroupByScheduler timed out waiting for groups to complete shutdown")
s.options.Logger.Debug("GroupByScheduler shutdown complete")
}
}
for _, lim := range s.schedulers {
lim.WaitShutdown()
}
rateLimitInterceptor *interceptor.RateLimitInterceptor,
logger log.Logger,
return &OpenAPIHTTPHandler{
logger: logger,
rateLimitInterceptor: rateLimitInterceptor,
}
}
serve := func(version int, apiName string, contentType string, spec []byte) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if err := h.rateLimitInterceptor.Allow(apiName, r.Header); err != nil {
w.WriteHeader(http.StatusTooManyRequests)
}
2,
configs.OpenAPIV2APIName,
"application/vnd.oai.openapi+json;version=2.0",
openapi.OpenAPIV2JSONSpec,
))
r.PathPrefix("/openapi.yaml").Methods("GET").HandlerFunc(serve(
3,
configs.OpenAPIV3APIName,
"application/vnd.oai.openapi;version=3.0",
openapi.OpenAPIV3YAMLSpec,
))
}
dlqInternalErrors dynamicconfig.BoolPropertyFn,
dlqErrorPattern dynamicconfig.StringPropertyFn,
return &executableFactoryImpl{
executor: executor,
scheduler: scheduler,
rescheduler: rescheduler,
priorityAssigner: priorityAssigner,
timeSource: timeSource,
namespaceRegistry: namespaceRegistry,
clusterMetadata: clusterMetadata,
chasmRegistry: chasmRegistry,
taskTypeTagProvider: taskTypeTagProvider,
logger: logger,
metricsHandler: metricsHandler,
tracer: tracer,
dlqWriter: dlqWriter,
dlqEnabled: dlqEnabled,
attemptsBeforeSendingToDlq: attemptsBeforeSendingToDlq,
dlqInternalErrors: dlqInternalErrors,
dlqErrorPattern: dlqErrorPattern,
}
}
func (f *executableFactoryImpl) NewExecutable(task tasks.Task, readerID int64) Executable {
)
so := &serverOptions{
// Set defaults here.
persistenceServiceResolver: resolver.NewNoopResolver(),
}
for _, opt := range opts {
opt.apply(so)
}
}
for serviceName := range so.serviceNames {
if !slices.Contains(Services, string(serviceName)) {
return fmt.Errorf("invalid service %q in service list %v", serviceName, so.serviceNames)
}
}
err := so.loadConfig()
if err != nil {
}
if err != nil {
return fmt.Errorf("config validation error: %w", err)
}
}
}
if err := so.config.Validate(); err != nil {
return err
}
if _, ok := so.config.Services[string(name)]; !ok {
return fmt.Errorf("%q service is missing in config", name)
}
}
}
}
func file_temporal_server_api_adminservice_v1_service_proto_init() {
if File_temporal_server_api_adminservice_v1_service_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_adminservice_v1_service_proto_rawDesc), len(file_temporal_server_api_adminservice_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_api_adminservice_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_api_adminservice_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_api_adminservice_v1_service_proto = out.File
file_temporal_server_api_adminservice_v1_service_proto_goTypes = nil
file_temporal_server_api_adminservice_v1_service_proto_depIdxs = nil
}
}
func file_temporal_server_api_archiver_v1_message_proto_init() {
if File_temporal_server_api_archiver_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_archiver_v1_message_proto_rawDesc), len(file_temporal_server_api_archiver_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_archiver_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_archiver_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_archiver_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_archiver_v1_message_proto = out.File
file_temporal_server_api_archiver_v1_message_proto_goTypes = nil
file_temporal_server_api_archiver_v1_message_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_batch_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_api_batch_v1_request_response_proto_init() {
if File_temporal_server_api_batch_v1_request_response_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_batch_v1_request_response_proto_rawDesc), len(file_temporal_server_api_batch_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_batch_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_api_batch_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_api_batch_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_api_batch_v1_request_response_proto = out.File
file_temporal_server_api_batch_v1_request_response_proto_goTypes = nil
file_temporal_server_api_batch_v1_request_response_proto_depIdxs = nil
}
}
func file_temporal_server_api_chasm_v1_message_proto_init() {
if File_temporal_server_api_chasm_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_chasm_v1_message_proto_rawDesc), len(file_temporal_server_api_chasm_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_chasm_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_chasm_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_chasm_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_chasm_v1_message_proto = out.File
file_temporal_server_api_chasm_v1_message_proto_goTypes = nil
file_temporal_server_api_chasm_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_checksum_v1_message_proto_init() {
if File_temporal_server_api_checksum_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_checksum_v1_message_proto_rawDesc), len(file_temporal_server_api_checksum_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_checksum_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_checksum_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_checksum_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_checksum_v1_message_proto = out.File
file_temporal_server_api_checksum_v1_message_proto_goTypes = nil
file_temporal_server_api_checksum_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_cluster_v1_message_proto_init() {
if File_temporal_server_api_cluster_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_cluster_v1_message_proto_rawDesc), len(file_temporal_server_api_cluster_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_cluster_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_cluster_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_cluster_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_cluster_v1_message_proto = out.File
file_temporal_server_api_cluster_v1_message_proto_goTypes = nil
file_temporal_server_api_cluster_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_common_v1_dlq_proto_init() {
if File_temporal_server_api_common_v1_dlq_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_common_v1_dlq_proto_rawDesc), len(file_temporal_server_api_common_v1_dlq_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_common_v1_dlq_proto_goTypes,
DependencyIndexes: file_temporal_server_api_common_v1_dlq_proto_depIdxs,
MessageInfos: file_temporal_server_api_common_v1_dlq_proto_msgTypes,
}.Build()
File_temporal_server_api_common_v1_dlq_proto = out.File
file_temporal_server_api_common_v1_dlq_proto_goTypes = nil
file_temporal_server_api_common_v1_dlq_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_contextpropagation_v1_message_proto_init() }
message.pb.go
func file_temporal_server_api_contextpropagation_v1_message_proto_init() {
if File_temporal_server_api_contextpropagation_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_contextpropagation_v1_message_proto_rawDesc), len(file_temporal_server_api_contextpropagation_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_contextpropagation_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_contextpropagation_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_contextpropagation_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_contextpropagation_v1_message_proto = out.File
file_temporal_server_api_contextpropagation_v1_message_proto_goTypes = nil
file_temporal_server_api_contextpropagation_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_deployment_v1_message_proto_init() {
if File_temporal_server_api_deployment_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_deployment_v1_message_proto_rawDesc), len(file_temporal_server_api_deployment_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 75,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_deployment_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_deployment_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_deployment_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_deployment_v1_message_proto = out.File
file_temporal_server_api_deployment_v1_message_proto_goTypes = nil
file_temporal_server_api_deployment_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_enums_v1_cluster_proto_init() {
if File_temporal_server_api_enums_v1_cluster_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_cluster_proto_rawDesc), len(file_temporal_server_api_enums_v1_cluster_proto_rawDesc)),
NumEnums: 2,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_cluster_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_cluster_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_cluster_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_cluster_proto = out.File
file_temporal_server_api_enums_v1_cluster_proto_goTypes = nil
file_temporal_server_api_enums_v1_cluster_proto_depIdxs = nil
}
}
func file_temporal_server_api_enums_v1_dlq_proto_init() {
if File_temporal_server_api_enums_v1_dlq_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_dlq_proto_rawDesc), len(file_temporal_server_api_enums_v1_dlq_proto_rawDesc)),
NumEnums: 2,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_dlq_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_dlq_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_dlq_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_dlq_proto = out.File
file_temporal_server_api_enums_v1_dlq_proto_goTypes = nil
file_temporal_server_api_enums_v1_dlq_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_enums_v1_fairness_state_proto_init() }
fairness_state.pb.go
func file_temporal_server_api_enums_v1_fairness_state_proto_init() {
if File_temporal_server_api_enums_v1_fairness_state_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_fairness_state_proto_rawDesc), len(file_temporal_server_api_enums_v1_fairness_state_proto_rawDesc)),
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_fairness_state_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_fairness_state_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_fairness_state_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_fairness_state_proto = out.File
file_temporal_server_api_enums_v1_fairness_state_proto_goTypes = nil
file_temporal_server_api_enums_v1_fairness_state_proto_depIdxs = nil
}
}
func file_temporal_server_api_enums_v1_replication_proto_init() {
if File_temporal_server_api_enums_v1_replication_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_enums_v1_replication_proto_rawDesc), len(file_temporal_server_api_enums_v1_replication_proto_rawDesc)),
NumEnums: 3,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_enums_v1_replication_proto_goTypes,
DependencyIndexes: file_temporal_server_api_enums_v1_replication_proto_depIdxs,
EnumInfos: file_temporal_server_api_enums_v1_replication_proto_enumTypes,
}.Build()
File_temporal_server_api_enums_v1_replication_proto = out.File
file_temporal_server_api_enums_v1_replication_proto_goTypes = nil
file_temporal_server_api_enums_v1_replication_proto_depIdxs = nil
}
}
func file_temporal_server_api_errordetails_v1_message_proto_init() {
if File_temporal_server_api_errordetails_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_errordetails_v1_message_proto_rawDesc), len(file_temporal_server_api_errordetails_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 10,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_errordetails_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_errordetails_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_errordetails_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_errordetails_v1_message_proto = out.File
file_temporal_server_api_errordetails_v1_message_proto_goTypes = nil
file_temporal_server_api_errordetails_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_health_v1_message_proto_init() {
if File_temporal_server_api_health_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_health_v1_message_proto_rawDesc), len(file_temporal_server_api_health_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_health_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_health_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_health_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_health_v1_message_proto = out.File
file_temporal_server_api_health_v1_message_proto_goTypes = nil
file_temporal_server_api_health_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_historyservice_v1_service_proto_init() {
if File_temporal_server_api_historyservice_v1_service_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_historyservice_v1_service_proto_rawDesc), len(file_temporal_server_api_historyservice_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_api_historyservice_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_api_historyservice_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_api_historyservice_v1_service_proto = out.File
file_temporal_server_api_historyservice_v1_service_proto_goTypes = nil
file_temporal_server_api_historyservice_v1_service_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_matchingservice_v1_service_proto_init() }
service.pb.go
func file_temporal_server_api_matchingservice_v1_service_proto_init() {
if File_temporal_server_api_matchingservice_v1_service_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_matchingservice_v1_service_proto_rawDesc), len(file_temporal_server_api_matchingservice_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_api_matchingservice_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_api_matchingservice_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_api_matchingservice_v1_service_proto = out.File
file_temporal_server_api_matchingservice_v1_service_proto_goTypes = nil
file_temporal_server_api_matchingservice_v1_service_proto_depIdxs = nil
}
}
func file_temporal_server_api_metrics_v1_message_proto_init() {
if File_temporal_server_api_metrics_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_metrics_v1_message_proto_rawDesc), len(file_temporal_server_api_metrics_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_metrics_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_metrics_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_metrics_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_metrics_v1_message_proto = out.File
file_temporal_server_api_metrics_v1_message_proto_goTypes = nil
file_temporal_server_api_metrics_v1_message_proto_depIdxs = nil
}
}
func file_temporal_server_api_namespace_v1_message_proto_init() {
if File_temporal_server_api_namespace_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_namespace_v1_message_proto_rawDesc), len(file_temporal_server_api_namespace_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_namespace_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_namespace_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_namespace_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_namespace_v1_message_proto = out.File
file_temporal_server_api_namespace_v1_message_proto_goTypes = nil
file_temporal_server_api_namespace_v1_message_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_persistence_v1_chasm_visibility_proto_init() }
chasm_visibility.pb.go
func file_temporal_server_api_persistence_v1_chasm_visibility_proto_init() {
if File_temporal_server_api_persistence_v1_chasm_visibility_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_chasm_visibility_proto_rawDesc), len(file_temporal_server_api_persistence_v1_chasm_visibility_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_chasm_visibility_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_chasm_visibility_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_chasm_visibility_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_chasm_visibility_proto = out.File
file_temporal_server_api_persistence_v1_chasm_visibility_proto_goTypes = nil
file_temporal_server_api_persistence_v1_chasm_visibility_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_persistence_v1_history_tree_proto_init() }
history_tree.pb.go
func file_temporal_server_api_persistence_v1_history_tree_proto_init() {
if File_temporal_server_api_persistence_v1_history_tree_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_history_tree_proto_rawDesc), len(file_temporal_server_api_persistence_v1_history_tree_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_history_tree_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_history_tree_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_history_tree_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_history_tree_proto = out.File
file_temporal_server_api_persistence_v1_history_tree_proto_goTypes = nil
file_temporal_server_api_persistence_v1_history_tree_proto_depIdxs = nil
}
}
func file_temporal_server_api_persistence_v1_tasks_proto_init() {
if File_temporal_server_api_persistence_v1_tasks_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_persistence_v1_tasks_proto_rawDesc), len(file_temporal_server_api_persistence_v1_tasks_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_persistence_v1_tasks_proto_goTypes,
DependencyIndexes: file_temporal_server_api_persistence_v1_tasks_proto_depIdxs,
MessageInfos: file_temporal_server_api_persistence_v1_tasks_proto_msgTypes,
}.Build()
File_temporal_server_api_persistence_v1_tasks_proto = out.File
file_temporal_server_api_persistence_v1_tasks_proto_goTypes = nil
file_temporal_server_api_persistence_v1_tasks_proto_depIdxs = nil
}
}
func file_temporal_server_api_token_v1_message_proto_init() {
if File_temporal_server_api_token_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_token_v1_message_proto_rawDesc), len(file_temporal_server_api_token_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_token_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_api_token_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_api_token_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_api_token_v1_message_proto = out.File
file_temporal_server_api_token_v1_message_proto_goTypes = nil
file_temporal_server_api_token_v1_message_proto_depIdxs = nil
}
}
func init() { file_temporal_server_api_visibilityservice_v1_request_response_proto_init() }
request_response.pb.go
func file_temporal_server_api_visibilityservice_v1_request_response_proto_init() {
if File_temporal_server_api_visibilityservice_v1_request_response_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_api_visibilityservice_v1_request_response_proto_rawDesc), len(file_temporal_server_api_visibilityservice_v1_request_response_proto_rawDesc)),
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_api_visibilityservice_v1_request_response_proto_goTypes,
DependencyIndexes: file_temporal_server_api_visibilityservice_v1_request_response_proto_depIdxs,
MessageInfos: file_temporal_server_api_visibilityservice_v1_request_response_proto_msgTypes,
}.Build()
File_temporal_server_api_visibilityservice_v1_request_response_proto = out.File
file_temporal_server_api_visibilityservice_v1_request_response_proto_goTypes = nil
file_temporal_server_api_visibilityservice_v1_request_response_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_activity_proto_v1_service_proto_init() }
service.pb.go
func file_temporal_server_chasm_lib_activity_proto_v1_service_proto_init() {
if File_temporal_server_chasm_lib_activity_proto_v1_service_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_activity_proto_v1_service_proto_rawDesc), len(file_temporal_server_chasm_lib_activity_proto_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_chasm_lib_activity_proto_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_activity_proto_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_chasm_lib_activity_proto_v1_service_proto = out.File
file_temporal_server_chasm_lib_activity_proto_v1_service_proto_goTypes = nil
file_temporal_server_chasm_lib_activity_proto_v1_service_proto_depIdxs = nil
}
logger log.Logger,
metricsHandler metrics.Handler,
resolver, err := monitor.GetResolver(primitives.HistoryService)
if err != nil {
return nil, err
}
connections := history.NewConnectionPool(resolver, rpcFactory, NewActivityServiceClient, logger, dynamicconfig.HistoryConnectionCloseDelay.Get(dc))
service_client.pb.go
var redirector history.Redirector[ActivityServiceClient]
if dynamicconfig.HistoryClientOwnershipCachingEnabled.Get(dc)() {
redirector = history.NewCachingRedirector(
connections,
dynamicconfig.HistoryClientOwnershipCachingStaleTTL.Get(dc),
)
redirector = history.NewBasicRedirector(connections, resolver)
}
client := &ActivityServiceLayeredClient{
metricsHandler: metricsHandler,
redirector: redirector,
numShards: config.NumHistoryShards,
retryPolicy: common.CreateHistoryClientRetryPolicy(dynamicconfig.RetryUnboundedOnSystemResourceExhausted.Get(dc)),
}
lc.Append(fx.StopHook(client.Stop))
return client, nil
}
c.redirector.Close()
}
func (c *ActivityServiceLayeredClient) callStartActivityExecutionNoRetry(
ctx context.Context,
}
func init() { file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_init() }
tasks.pb.go
func file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_init() {
if File_temporal_server_chasm_lib_callback_proto_v1_tasks_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_rawDesc), len(file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_callback_proto_v1_tasks_proto = out.File
file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_goTypes = nil
file_temporal_server_chasm_lib_callback_proto_v1_tasks_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_init() }
service.pb.go
func file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_init() {
if File_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto != nil {
return
}
file_temporal_server_chasm_lib_nexusoperation_proto_v1_request_response_proto_init()
service.pb.go
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_rawDesc), len(file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto = out.File
file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_goTypes = nil
file_temporal_server_chasm_lib_nexusoperation_proto_v1_service_proto_depIdxs = nil
}
logger log.Logger,
metricsHandler metrics.Handler,
resolver, err := monitor.GetResolver(primitives.HistoryService)
if err != nil {
return nil, err
}
connections := history.NewConnectionPool(resolver, rpcFactory, NewNexusOperationServiceClient, logger, dynamicconfig.HistoryConnectionCloseDelay.Get(dc))
service_client.pb.go
var redirector history.Redirector[NexusOperationServiceClient]
if dynamicconfig.HistoryClientOwnershipCachingEnabled.Get(dc)() {
redirector = history.NewCachingRedirector(
connections,
dynamicconfig.HistoryClientOwnershipCachingStaleTTL.Get(dc),
)
redirector = history.NewBasicRedirector(connections, resolver)
}
client := &NexusOperationServiceLayeredClient{
metricsHandler: metricsHandler,
redirector: redirector,
numShards: config.NumHistoryShards,
retryPolicy: common.CreateHistoryClientRetryPolicy(dynamicconfig.RetryUnboundedOnSystemResourceExhausted.Get(dc)),
}
lc.Append(fx.StopHook(client.Stop))
return client, nil
}
c.redirector.Close()
}
func (c *NexusOperationServiceLayeredClient) callStartNexusOperationNoRetry(
ctx context.Context,
}
func init() { file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_init() }
tasks.pb.go
func file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_init() {
if File_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_rawDesc), len(file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto = out.File
file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_goTypes = nil
file_temporal_server_chasm_lib_nexusoperation_proto_v1_tasks_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_init() }
service.pb.go
func file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_init() {
if File_temporal_server_chasm_lib_scheduler_proto_v1_service_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_rawDesc), len(file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_chasm_lib_scheduler_proto_v1_service_proto = out.File
file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_goTypes = nil
file_temporal_server_chasm_lib_scheduler_proto_v1_service_proto_depIdxs = nil
}
logger log.Logger,
metricsHandler metrics.Handler,
resolver, err := monitor.GetResolver(primitives.HistoryService)
if err != nil {
return nil, err
}
connections := history.NewConnectionPool(resolver, rpcFactory, NewSchedulerServiceClient, logger, dynamicconfig.HistoryConnectionCloseDelay.Get(dc))
service_client.pb.go
var redirector history.Redirector[SchedulerServiceClient]
if dynamicconfig.HistoryClientOwnershipCachingEnabled.Get(dc)() {
redirector = history.NewCachingRedirector(
connections,
dynamicconfig.HistoryClientOwnershipCachingStaleTTL.Get(dc),
)
redirector = history.NewBasicRedirector(connections, resolver)
}
client := &SchedulerServiceLayeredClient{
metricsHandler: metricsHandler,
redirector: redirector,
numShards: config.NumHistoryShards,
retryPolicy: common.CreateHistoryClientRetryPolicy(dynamicconfig.RetryUnboundedOnSystemResourceExhausted.Get(dc)),
}
lc.Append(fx.StopHook(client.Stop))
return client, nil
}
c.redirector.Close()
}
func (c *SchedulerServiceLayeredClient) callCreateScheduleNoRetry(
ctx context.Context,
}
func init() { file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_init() }
tasks.pb.go
func file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_init() {
if File_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_rawDesc), len(file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto = out.File
file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_goTypes = nil
file_temporal_server_chasm_lib_scheduler_proto_v1_tasks_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_tests_proto_v1_message_proto_init() }
message.pb.go
func file_temporal_server_chasm_lib_tests_proto_v1_message_proto_init() {
if File_temporal_server_chasm_lib_tests_proto_v1_message_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_tests_proto_v1_message_proto_rawDesc), len(file_temporal_server_chasm_lib_tests_proto_v1_message_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_tests_proto_v1_message_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_tests_proto_v1_message_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_tests_proto_v1_message_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_tests_proto_v1_message_proto = out.File
file_temporal_server_chasm_lib_tests_proto_v1_message_proto_goTypes = nil
file_temporal_server_chasm_lib_tests_proto_v1_message_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_tests_proto_v1_service_proto_init() }
service.pb.go
func file_temporal_server_chasm_lib_tests_proto_v1_service_proto_init() {
if File_temporal_server_chasm_lib_tests_proto_v1_service_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_tests_proto_v1_service_proto_rawDesc), len(file_temporal_server_chasm_lib_tests_proto_v1_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_temporal_server_chasm_lib_tests_proto_v1_service_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_tests_proto_v1_service_proto_depIdxs,
}.Build()
File_temporal_server_chasm_lib_tests_proto_v1_service_proto = out.File
file_temporal_server_chasm_lib_tests_proto_v1_service_proto_goTypes = nil
file_temporal_server_chasm_lib_tests_proto_v1_service_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_init() }
state.pb.go
func file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_init() {
if File_temporal_server_chasm_lib_workflow_proto_v1_state_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDesc), len(file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_workflow_proto_v1_state_proto = out.File
file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_goTypes = nil
file_temporal_server_chasm_lib_workflow_proto_v1_state_proto_depIdxs = nil
}
}
func init() { file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_init() }
update_state.pb.go
func file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_init() {
if File_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto != nil {
return
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_rawDesc), len(file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_goTypes,
DependencyIndexes: file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_depIdxs,
MessageInfos: file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_msgTypes,
}.Build()
File_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto = out.File
file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_goTypes = nil
file_temporal_server_chasm_lib_workflow_proto_v1_update_state_proto_depIdxs = nil
}
func mustNewWorkflowServiceNexusHandler(
handler *workflowServiceNexusHandler,
svc := nexus.NewService(workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.ServiceName)
svc.MustRegister(nexus.NewSyncOperation(
workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.SignalWithStartWorkflowExecution.Name(),
handler.signalWithStartWorkflowExecution,
))
return svc
}
func (h *workflowServiceNexusHandler) setHistoryHandler(handler historyservice.HistoryServiceServer) {
nexus_service.go
h.historyHandler = handler
}
type SignalWithStartOperationProcessor struct {
saMapperProvider searchattribute.MapperProvider,
saValidator *searchattribute.Validator,
sp := chasm.NewNexusServiceProcessor(workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.ServiceName)
op := SignalWithStartOperationProcessor{validator: NewValidator(config, saMapperProvider, saValidator)}
sp.MustRegisterOperation(
workflowservicenexus.TemporalAPIWorkflowserviceV1WorkflowService.SignalWithStartWorkflowExecution.Name(),
chasm.NewRegisterableNexusOperationProcessor(op),
)
return sp
}
rpcFactory RPCFactory,
timeout time.Duration,
connections := NewConnectionPool(historyServiceResolver, rpcFactory, historyservice.NewHistoryServiceClient, logger, dynamicconfig.HistoryConnectionCloseDelay.Get(dc))
var redirector Redirector[historyservice.HistoryServiceClient]
if dynamicconfig.HistoryClientOwnershipCachingEnabled.Get(dc)() {
logger.Info("historyClient: ownership caching enabled")
redirector = NewCachingRedirector(
dynamicconfig.HistoryClientOwnershipCachingStaleTTL.Get(dc),
)
logger.Info("historyClient: ownership caching disabled")
redirector = NewBasicRedirector(connections, historyServiceResolver)
}
connections: connections,
logger: logger,
numberOfShards: numberOfShards,
redirector: redirector,
timeout: timeout,
tokenSerializer: tasktoken.NewSerializer(),
}
}
// Stop stops the membership watcher and closes pooled connections.
c.redirector.Close()
}
func checkShardID(shardID int32) error {
)
return &ShutdownOnceImpl{
status: shutdownOnceStatusOpen,
channel: make(chan struct{}),
}
}
if atomic.CompareAndSwapInt32(
&c.status,
shutdownOnceStatusOpen,
shutdownOnceStatusClosed,
) {
close(c.channel)
}
}
return atomic.LoadInt32(&c.status) == shutdownOnceStatusClosed
}
return c.channel
}
clientProvider clientProvider,
logger log.Logger,
return &clientCacheImpl{
keyResolver: keyResolver,
clientProvider: clientProvider,
clients: make(map[string]cachedEntry),
logger: logger,
}
}
return c.keyResolver.Lookup(key, index)
}
func (c *clientCacheImpl) GetClientForKey(key string, index int) (any, error) {
}
c.cacheLock.Lock()
entries := c.clients
c.clients = make(map[string]cachedEntry)
c.cacheLock.Unlock()
for _, entry := range entries {
if entry.release != nil {
if err := entry.release(); err != nil {
// WildCardStringToRegexps converts a given slices of string patterns to a slice of regular expressions matching
// wildcards (*) with any substring.
var result strings.Builder
result.WriteRune('^')
for i, pattern := range patterns {
result.WriteRune('(')
first := true
for literal := range strings.SplitSeq(pattern, "*") {
if !first {
// Replace * with .*
result.WriteString(".*")
}
first = false
}
if i < len(patterns)-1 {
}
}
return regexp.Compile(result.String())
}
// MustWildCardStringsToRegexp is like WildCardStringsToRegexp but panics on error.
re, err := WildCardStringsToRegexp(patterns)
if err != nil {
panic(err) //nolint:forbidigo // Must* functions conventionally panic on error.
}
}
chasmEngine chasm.Engine,
matchingClient resource.MatchingClient,
scopedMetricsHandler := metricsHandler.WithTags(
metrics.OperationTag(metrics.OperationOutboundQueueProcessorScope),
)
return &outboundQueueActiveTaskExecutor{
stateMachineEnvironment: stateMachineEnvironment{
shardContext: shardCtx,
cache: workflowCache,
logger: logger,
metricsHandler: scopedMetricsHandler,
},
chasmEngine: chasmEngine,
workerCommandsDispatcher: workercommands.NewDispatcher(
matchingClient,
shardCtx.GetConfig(),
scopedMetricsHandler,
logger,
),
}
}
func (e *outboundQueueActiveTaskExecutor) Execute(
totalNumShards int,
updateAppliedCallback chan struct{},
if totalNumShards <= 0 {
return nil, fmt.Errorf("%w: %d", ErrNonPositiveTotalNumShards, totalNumShards)
}
shardCounter: shardCounter,
totalNumShards: totalNumShards,
updateAppliedCallback: updateAppliedCallback,
subscription: shardCounter.SubscribeShardCount(),
}
scaler.shardCount.Store(shardCountNotSet)
scaler.shutdownWG.Go(func() {
for count := range scaler.subscription.ShardCount() {
scaler.shardCount.Store(int64(count))
if scaler.updateAppliedCallback != nil {
scaler.updateAppliedCallback <- struct{}{}
}
}
s.subscription.Unsubscribe()
s.shutdownWG.Wait()
}
func (s LazyLoadedOwnershipBasedQuotaScaler) ScaleFactor() (float64, bool) {
// NewRegistry creates a new [Registry].
return &Registry{
commandHandlers: make(map[enumspb.CommandType]CommandHandler),
eventDefinitions: make(map[enumspb.EventType]EventDefinition),
eventDefinitionsByGoType: make(map[reflect.Type]EventDefinition),
}
}
// Register registers all command handlers and event definitions from a [Library].
// Returns an [ErrDuplicateRegistration] if a handler or definition is already registered.
// All registration is expected to happen in a single thread on process initialization.
for t, handler := range lib.CommandHandlers() {
return fmt.Errorf("%w: command handler for %v: %v", ErrDuplicateRegistration, t, existing)
}
}
return fmt.Errorf("%w: event handler for %v: %v", ErrDuplicateRegistration, def.Type(), existing)
}
for goType.Kind() == reflect.Pointer {
goType = goType.Elem()
}
return fmt.Errorf("%w: event definition for Go type %v: %v", ErrDuplicateRegistration, goType, existing)
}
r.eventDefinitionsByGoType[goType] = def
}
}
logger log.Logger,
metricsHandler metrics.Handler,
return &Finalizer{
logger: logger,
metricsHandler: metricsHandler,
callbacks: make(map[string]func(context.Context) error),
}
}
// Register adds a callback to the finalizer.
func (f *Finalizer) Run(
timeout time.Duration,
if timeout == 0 {
f.logger.Debug("finalizer skipped: zero timeout")
return 0
}
if f.finalized {
f.logger.Warn("finalizer skipped: called more than once")
f.mu.Unlock()
return 0
}
f.mu.Unlock() // unlocking immediately to unblock any calls to Register/Deregister
totalCount := len(f.callbacks)
if totalCount == 0 {
return 0
}
f.logger.Debug("finalizer starting",
metricsHandler metrics.Handler,
logger log.Logger,
blob, err := serializer.QueueMetadataToBlob(
&persistencespb.QueueMetadata{
ClusterAckLevels: make(map[string]int64),
})
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
queue: queue,
clusterName: clusterName,
metricsHandler: metricsHandler,
logger: logger,
serializer: serializer,
}, nil
}
)
q.queue.Close()
}
func (q *namespaceReplicationQueueImpl) Publish(ctx context.Context, task *replicationspb.ReplicationTask) error {
// NewHealthCheckInterceptor creates a new health check interceptor
func NewHealthCheckInterceptor(healthSignalAggregator HealthSignalAggregator) *HealthCheckInterceptor {
health_check.go
return &HealthCheckInterceptor{
healthSignalAggregator: healthSignalAggregator,
}
}
// UnaryIntercept implements the gRPC unary interceptor interface
latencyWindowSize time.Duration,
latencyWindowCount int,
latencyDistribution, err := stats.NewWindowedTDigest(stats.WindowConfig{
WindowSize: latencyWindowSize,
WindowCount: latencyWindowCount,
})
if err != nil {
logger.Error("failed to create latency distribution helper, falling back to default config", tag.Error(err))
latencyDistribution, err = stats.NewWindowedTDigest(stats.WindowConfig{
}
logger: logger,
aggregatorEnabled: aggregatorEnabled,
percentilesEnabled: percentilesEnabled,
latencyAverage: aggregate.NewMovingWindowAvgImpl(windowSize, maxBufferSize),
latencyDistribution: latencyDistribution,
errorRatio: aggregate.NewMovingWindowAvgImpl(windowSize, maxBufferSize),
}
}
logger log.Logger,
disabled bool,
return newEventsCache(executionManager, handler, logger, config.EventsHostLevelCacheMaxSizeBytes(), config.EventsCacheTTL(), disabled)
}
func NewShardLevelEventsCache(
logger log.Logger,
disabled bool,
return newEventsCache(executionManager, handler, logger, config.EventsShardLevelCacheMaxSizeBytes(), config.EventsCacheTTL(), disabled)
}
func newEventsCache(
ttl time.Duration,
disabled bool,
opts := &cache.Options{}
opts.TTL = ttl
taggedMetricHandler := metricsHandler.WithTags(metrics.CacheTypeTag(metrics.EventsCacheTypeTagValue))
return &CacheImpl{
Cache: cache.NewWithMetrics(maxSize, opts, taggedMetricHandler),
executionManager: executionManager,
metricsHandler: taggedMetricHandler,
logger: logger,
disabled: disabled,
}
}
func (e *CacheImpl) validateKey(key EventKey) bool {
inclusiveMin tasks.Key,
exclusiveMax tasks.Key,
if inclusiveMin.CompareTo(exclusiveMax) > 0 {
panic(fmt.Sprintf("invalid task range, min %v is larger than max %v", inclusiveMin, exclusiveMax))
}
InclusiveMin: inclusiveMin,
ExclusiveMax: exclusiveMax,
}
}
return r.InclusiveMin.CompareTo(r.ExclusiveMax) == 0
}
func (r *Range) ContainsKey(
key tasks.Key,
return key.CompareTo(r.InclusiveMin) >= 0 &&
key.CompareTo(r.ExclusiveMax) < 0
}
func (r *Range) ContainsRange(
func (r *Range) CanSplit(
key tasks.Key,
return r.ContainsKey(key) || r.ExclusiveMax.CompareTo(key) == 0
}
func (r *Range) Split(
key tasks.Key,
if !r.CanSplit(key) {
panic(fmt.Sprintf("Unable to split range %v at %v", r, key))
}
}
// each entry point that uses it. Essentially, get it from the dependency graph instead of calling this method, unless
// you're in a test.
return &MutableTaskCategoryRegistry{
categories: map[int]Category{
CategoryTransfer.ID(): CategoryTransfer,
CategoryTimer.ID(): CategoryTimer,
CategoryVisibility.ID(): CategoryVisibility,
CategoryReplication.ID(): CategoryReplication,
CategoryMemoryTimer.ID(): CategoryMemoryTimer,
CategoryOutbound.ID(): CategoryOutbound,
},
}
}
// AddCategory register a Category with the registry or panics if a Category with the same ID has already been
// GetCategoryByID returns a registered Category with the same ID from the registry or false if no such Category exists.
func (r *MutableTaskCategoryRegistry) GetCategoryByID(id int) (Category, bool) {
task_category_registry.go
category, ok := r.categories[id]
return category, ok
}
// GetCategories returns a deep copy of all registered Category objects from the registry.
func (r *MutableTaskCategoryRegistry) GetCategories() map[int]Category {
task_category_registry.go
return maps.Clone(r.categories)
}
)
func NewInvokerExecuteTaskHandler(opts InvokerTaskHandlerOptions) *InvokerExecuteTaskHandler {
invoker_tasks.go
return &InvokerExecuteTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
baseLogger: opts.BaseLogger,
historyClient: opts.HistoryClient,
frontendClient: opts.FrontendClient,
}
}
func NewInvokerProcessBufferTaskHandler(opts InvokerTaskHandlerOptions) *InvokerProcessBufferTaskHandler {
invoker_tasks.go
return &InvokerProcessBufferTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
baseLogger: opts.BaseLogger,
historyClient: opts.HistoryClient,
frontendClient: opts.FrontendClient,
}
}
// recordDuplicateExecuteDrops emits a metric + Debug log for CompletedStarts
windowSize time.Duration,
maxBufferSize int,
return &MovingWindowAvgImpl{
windowSize: windowSize,
maxBufferSize: maxBufferSize,
buffer: make([]timestampedData, maxBufferSize),
}
}
a.Lock()
defer a.Unlock()
a.buffer[a.tailIdx] = timestampedData{timestamp: time.Now(), value: val}
a.tailIdx = (a.tailIdx + 1) % a.maxBufferSize
a.sum += val
a.count++
if a.tailIdx == a.headIdx {
// buffer full, expire oldest element
a.sum -= a.buffer[a.headIdx].value
// StaticGradualChange returns a GradualChange whose Value always returns def and whose When
// always returns a time in the past.
return GradualChange[T]{New: def}
}
// Value returns the value for the given key at the given time.
if !now.Before(c.End) {
return c.Old
}
// of type GradualChange into a GradualChange.
// nolint:revive // cognitive-complexity // this looks complicated but each case is fairly simple
func ConvertGradualChange[T any](def T) func(v any) (GradualChange[T], error) {
gradual_change.go
changeConverter := ConvertStructure(StaticGradualChange(def))
// Call this once so that if it's going to panic, it panics at static init time.
_, _ = changeConverter(nil)
switch reflect.TypeFor[T]() {
case reflect.TypeFor[bool]():
return func(v any) (GradualChange[T], error) {
if b, err := convertBool(v); err == nil {
var change GradualChange[T]
return changeConverter(v)
}
return func(v any) (GradualChange[T], error) {
if i, err := convertInt(v); err == nil {
var change GradualChange[T]
type MultiStatsHandler []stats.Handler
func (m MultiStatsHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {
multistats.go
for _, h := range m {
ctx = h.TagConn(ctx, info)
}
return ctx
}
for _, h := range m {
h.HandleConn(ctx, cs)
}
}
func (m MultiStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
multistats.go
for _, h := range m {
ctx = h.TagRPC(ctx, info)
}
return ctx
}
for _, h := range m {
h.HandleRPC(ctx, rs)
}
}
logger log.Logger,
metricsHandler metrics.Handler,
return &RateLimitedScheduler[T]{
scheduler: scheduler,
rateLimiter: rateLimiter,
timeSource: timeSource,
quotaRequestFn: quotaRequestFn,
metricTagsFn: metricTagsFn,
options: options,
logger: logger,
metricsHandler: metricsHandler,
}
}
func (s *RateLimitedScheduler[T]) Submit(task T) {
}
s.scheduler.Start()
}
s.scheduler.Stop()
}
func (s *RateLimitedScheduler[T]) wait(task T) {
}
func ConfigProvider(dc *dynamicconfig.Collection, cfg *config.Persistence) *Config {
config.go
return &Config{
RequestTimeout: RequestTimeout.Get(dc),
MinRequestTimeout: MinRequestTimeout.Get(dc),
MaxConcurrentOperations: MaxConcurrentOperations.Get(dc),
MaxServiceNameLength: MaxServiceNameLength.Get(dc),
MaxOperationNameLength: MaxOperationNameLength.Get(dc),
MaxOperationTokenLength: MaxOperationTokenLength.Get(dc),
MaxOperationHeaderSize: MaxOperationHeaderSize.Get(dc),
DisallowedOperationHeaders: DisallowedOperationHeaders.Get(dc),
MaxOperationScheduleToCloseTimeout: MaxOperationScheduleToCloseTimeout.Get(dc),
PayloadSizeLimit: dynamicconfig.BlobSizeLimitError.Get(dc),
CallbackURLTemplate: CallbackURLTemplate.Get(dc),
UseSystemCallbackURL: UseSystemCallbackURL.Get(dc),
UseNewFailureWireFormat: chasmnexus.UseNewFailureWireFormat.Get(dc),
RecordCancelRequestCompletionEvents: RecordCancelRequestCompletionEvents.Get(dc),
MetricTagConfig: MetricTagConfiguration.Get(dc),
RetryPolicy: func() backoff.RetryPolicy {
return backoff.NewExponentialRetryPolicy(
RetryPolicyInitialInterval.Get(dc)(),
fx.Provide(NewChasmNotifier),
fx.Provide(newChasmEngine),
fx.Invoke(func(impl *ChasmEngine, shardController shard.Controller) {
impl.SetShardController(shardController)
}),
)
historyServiceResolver membership.ServiceResolver,
hostInfoProvider membership.HostInfoProvider,
return &ChasmEngine{
executionCache: executionCache,
registry: registry,
config: config,
notifier: notifier,
logger: logger,
historyServiceResolver: historyServiceResolver,
hostInfoProvider: hostInfoProvider,
}
}
// This is for breaking fx cycle dependency.
func (e *ChasmEngine) SetShardController(
shardController shard.Controller,
e.shardController = shardController
}
func (e *ChasmEngine) NotifyExecution(key chasm.ExecutionKey) {
}
func newActivityDispatchTaskHandler(opts activityDispatchTaskHandlerOptions) *activityDispatchTaskHandler {
activity_tasks.go
return &activityDispatchTaskHandler{
opts: opts,
}
}
func (h *activityDispatchTaskHandler) Validate(
}
func newScheduleToStartTimeoutTaskHandler() *scheduleToStartTimeoutTaskHandler {
activity_tasks.go
return &scheduleToStartTimeoutTaskHandler{}
}
func (h *scheduleToStartTimeoutTaskHandler) Validate(
type scheduleToCloseTimeoutTaskHandler struct{ chasm.PureTaskHandlerBase }
func newScheduleToCloseTimeoutTaskHandler() *scheduleToCloseTimeoutTaskHandler {
activity_tasks.go
return &scheduleToCloseTimeoutTaskHandler{}
}
func (h *scheduleToCloseTimeoutTaskHandler) Validate(
type startToCloseTimeoutTaskHandler struct{ chasm.PureTaskHandlerBase }
return &startToCloseTimeoutTaskHandler{}
}
func (h *startToCloseTimeoutTaskHandler) Validate(
type heartbeatTimeoutTaskHandler struct{ chasm.PureTaskHandlerBase }
return &heartbeatTimeoutTaskHandler{}
}
// Validate validates a HeartbeatTimeoutTask.
ctx context.Context,
row *sqlplugin.QueueMetadataRow,
return mdb.conn.NamedExecContext(ctx,
templateCreateQueueMetadataQuery,
row,
)
}
func (mdb *db) UpdateQueueMetadata(
ctx context.Context,
filter sqlplugin.QueueMetadataFilter,
var row sqlplugin.QueueMetadataRow
err := mdb.conn.GetContext(ctx,
&row,
templateGetQueueMetadataQuery,
filter.QueueType,
)
if err != nil {
}
}
enableFairness bool,
serializer serialization.Serializer,
store := SqlStore{
DB: db,
logger: logger,
serializer: serializer,
}
userDataStore := userDataStore{SqlStore: store}
taskQueueStore := taskQueueStore{
SqlStore: store,
version: sqlplugin.MatchingTaskVersion1,
taskScanPartitions: uint32(taskScanPartitions),
}
if enableFairness {
return newTaskManagerV2(db, userDataStore, taskQueueStore, logger, serializer)
}
}
maxQPS dynamicconfig.IntPropertyFn,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
rateLimiters := make(map[int]quotas.RequestRateLimiter)
for priority := range PrioritiesOrdered {
if priority == OperatorPriority {
rateLimiters[priority] = quotas.NewRequestRateLimiterAdapter(quotas.NewDefaultOutgoingRateLimiter(operatorRateFn(maxQPS, operatorRPSRatio)))
} else {
rateLimiters[priority] = quotas.NewRequestRateLimiterAdapter(quotas.NewDefaultOutgoingRateLimiter(rateFn(maxQPS)))
}
}
if req.CallerType == headers.CallerTypeOperator {
return OperatorPriority
}
return func() float64 {
return float64(maxQPS())
}
}
func operatorRateFn(maxQPS dynamicconfig.IntPropertyFn, operatorRPSRatio dynamicconfig.FloatPropertyFn) quotas.RateFn {
quotas.go
return func() float64 {
return float64(maxQPS()) * operatorRPSRatio()
}
}
visibilityPluginNameMetricsTag metrics.Tag,
visibilityIndexNameMetricsTag metrics.Tag,
return &visibilityManagerMetrics{
metricHandler: metricHandler,
logger: logger,
delegate: delegate,
slowQueryThreshold: slowQueryThreshold,
visibilityPluginNameMetricsTag: visibilityPluginNameMetricsTag,
visibilityIndexNameMetricsTag: visibilityIndexNameMetricsTag,
}
}
m.delegate.Close()
}
func (m *visibilityManagerMetrics) GetReadStoreName(nsName namespace.Name) string {
}
return m.delegate.GetStoreNames()
}
func (m *visibilityManagerMetrics) HasStoreName(stName string) bool {
// NewContextMetadataInterceptor creates a new ContextMetadataInterceptor
func NewContextMetadataInterceptor(setTrailer bool, logger log.Logger) *ContextMetadataInterceptor {
context_metadata_interceptor.go
cmi := &ContextMetadataInterceptor{
setTrailer: setTrailer,
}
if setTrailer {
cmi.throttledLogger = log.NewThrottledLogger(logger, func() float64 {
return 1.0 / 30.0 // 1 log per 30 seconds
})
}
}
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
ctx = contextutil.WithMetadataContext(ctx)
resp, err := handler(ctx, req)
if c.setTrailer {
c.appendContextMetadataToTrailer(ctx, info)
}
}
}
return RoutingKeyExtractor{
serializer: *tasktoken.NewSerializer(),
}
}
// WorkflowServiceExtractor returns a RoutingKeyExtractorFunc that extracts the
// routing key from WorkflowService API requests using the provided
// RoutingKeyExtractor.
func WorkflowServiceExtractor(extractor RoutingKeyExtractor) RoutingKeyExtractorFunc {
routing_key_extractor.go
return func(_ context.Context, req any, fullMethod string) namespace.RoutingKey {
// Only process WorkflowService APIs
if !strings.HasPrefix(fullMethod, api.WorkflowServicePrefix) {
return namespace.RoutingKey{}
}
// Prefer the generated extractor driven by temporal-resource-id proto
// annotations.
}
// Fall back to pattern-based logic as a compatibility path for methods
// whose callers haven't populated the resource_id field yet.
pattern, hasPattern := methodToPattern[methodName]
if !hasPattern {
}
return extractor.Extract(req, pattern)
}
return TestHooks{data: &sync.Map{}}
}
// Get gets the value of a test hook from the registry.
//
// TestHooks should be used sparingly, see comment on TestHooks.
var zero T
if th.data == nil {
// This means TestHooks wasn't created via NewTestHooks. Ignore.
return zero, false
}
return val.(T), true //nolint:revive
}
}
var keyCounter atomic.Int64
var zero S
var s ScopeType
switch any(zero).(type) {
case namespace.ID, namespace.Name:
s = ScopeNamespace
case global:
s = ScopeGlobal
default:
panic("testhooks: unknown scope type")
}
}
r Range,
predicate tasks.Predicate,
return Scope{
Range: r,
Predicate: predicate,
}
}
func (s *Scope) Contains(task tasks.Task) bool {
func (s *Scope) CanSplitByRange(
key tasks.Key,
return s.Range.CanSplit(key)
}
func (s *Scope) SplitByRange(
key tasks.Key,
if !s.CanSplitByRange(key) {
panic(fmt.Sprintf("Unable to split scope with range %v at %v", s.Range, key))
}
return NewScope(leftRange, s.Predicate), NewScope(rightRange, s.Predicate)
}
// NewExecutionManagerDLQWriter creates a new DLQWriter that uses the [ExecutionManager].
func NewExecutionManagerDLQWriter(executionManager ExecutionManager) *executionManagerDLQWriter {
dlq_writer.go
return &executionManagerDLQWriter{
executionManager: executionManager,
}
}
// NewDLQWriterAdapter creates a new DLQWriter from a QueueV2 [queues.DLQWriter].
replicationTaskSerializer TaskSerializer,
currentClusterName string,
return &DLQWriterAdapter{
dlqWriter: dlqWriter,
replicationTaskSerializer: replicationTaskSerializer,
currentClusterName: currentClusterName,
}
}
// This creates a new [DLQWriter] that can be toggled between the two implementations.
func newDLQWriterToggle(
params dlqWriterToggleParams,
return &dlqWriterToggle{
dlqWriterToggleParams: ¶ms,
}
}
// WriteTaskToDLQ implements [DLQWriter.WriteTaskToDLQ] by calling either
config *configs.Config,
clientBean client.Bean,
return &timerQueueStandbyTaskExecutor{
timerQueueTaskExecutorBase: newTimerQueueTaskExecutorBase(
shard,
workflowCache,
workflowDeleteManager,
matchingRawClient,
chasmEngine,
logger,
metricProvider,
config,
false,
),
clusterName: clusterName,
clientBean: clientBean,
}
}
func (t *timerQueueStandbyTaskExecutor) Execute(
config *configs.Config,
isActive bool,
return &timerQueueTaskExecutorBase{
stateMachineEnvironment: stateMachineEnvironment{
shardContext: shardContext,
cache: workflowCache,
logger: logger,
metricsHandler: metricsHandler,
},
currentClusterName: shardContext.GetClusterMetadata().GetCurrentClusterName(),
registry: shardContext.GetNamespaceRegistry(),
chasmEngine: chasmEngine,
deleteManager: deleteManager,
matchingRawClient: matchingRawClient,
config: config,
isActive: isActive,
}
}
func (t *timerQueueTaskExecutorBase) executeDeleteHistoryEventTask(
logger log.Logger,
handler metrics.Handler,
maxSize := config.HistoryHostLevelCacheMaxSize()
if config.HistoryCacheLimitSizeBased {
maxSize = config.HistoryHostLevelCacheMaxSizeBytes()
}
TTL: config.HistoryCacheTTL(),
Pin: true,
BackgroundEvict: config.HistoryCacheBackgroundEvict,
OnPut: func(val any) {
//revive:disable-next-line:unchecked-type-assertion
item := val.(*cacheItem)
}
taggedHandler := handler.WithTags(metrics.CacheTypeTag(metrics.MutableStateCacheTypeTagValue))
cache.go
c := cache.NewWithMetrics(maxSize, opts, taggedHandler)
return &cacheImpl{
Cache: c,
nonUserContextLockTimeout: config.HistoryCacheNonUserContextLockTimeout(),
}
}
c.Cache.(cache.StoppableCache).Stop()
}
func (c *cacheImpl) GetOrCreateWorkflowExecution(
metricsHandler metrics.Handler,
logger log.Logger,
return &Activities{
visibilityManager: visibilityManager,
historyClient: historyClient,
deleteActivityRPS: deleteActivityRPS,
useChasmDeleteExecution: useChasmDeleteExecution,
metricsHandler: metricsHandler,
logger: logger,
}
}
func NewLocalActivities(
metricsHandler metrics.Handler,
logger log.Logger,
return &LocalActivities{
visibilityManager: visibilityManager,
metricsHandler: metricsHandler,
logger: logger,
}
}
func (a *LocalActivities) GetNextPageTokenActivity(ctx context.Context, params GetNextPageTokenParams) ([]byte, error) {
logger log.Logger,
metricsHandler metrics.Handler,
return &ChasmEngineInterceptor{
engine: engine,
logger: logger,
metricsHandler: metricsHandler,
}
}
// ChasmVisibilityInterceptor intercepts RPC requests and adds the CHASM
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
ctx = NewVisibilityManagerContext(ctx, i.visibilityMgr)
return handler(ctx, req)
}
func ChasmVisibilityInterceptorProvider(visibilityMgr VisibilityManager) *ChasmVisibilityInterceptor {
interceptors.go
return &ChasmVisibilityInterceptor{
visibilityMgr: visibilityMgr,
}
}
}
return "core"
}
return []*RegistrableComponent{
NewRegistrableComponent[*Visibility]("vis", WithDetached()),
}
}
return []*RegistrableTask{
NewRegistrableSideEffectTask(
"visTask",
defaultVisibilityTaskHandler,
),
}
}
// NewHTTPHandler constructs an [http.Handler] from given options for handling Nexus service requests.
if options.Logger == nil {
options.Logger = slog.Default()
}
options.GetResultTimeout = time.Minute
}
options.Serializer = nexus.DefaultSerializer()
}
}
BaseHTTPHandler: BaseHTTPHandler{
Logger: options.Logger,
FailureConverter: options.FailureConverter,
},
options: options,
}
return http.HandlerFunc(handler.handleRequest)
}
ok bool,
reservations []Reservation,
if ok && len(reservations) == 0 {
panic("expect at least one reservation")
}
ok: ok,
reservations: reservations,
}
}
// OK returns whether the limiter can provide the requested number of tokens
return r.ok
}
// Cancel indicates that the reservation holder will not perform the reserved action
// before taking the reserved action. Zero duration means act immediately.
// MultiReservation DelayFrom returns the maximum delay of all its sub-reservations.
func (r *MultiReservationImpl) DelayFrom(now time.Time) time.Duration {
multi_reservation_impl.go
if !r.ok {
return InfDuration
}
for _, reservation := range r.reservations {
duration := reservation.DelayFrom(now)
if result < duration {
result = duration
}
}
}
type tlsCertFetcher func() (*tls.Certificate, error)
if s.refreshInterval != 0 {
s.stop = make(chan bool)
s.ticker = time.NewTicker(s.refreshInterval)
legacyWorkerSettings *config.ClientTLS,
refreshInterval time.Duration,
provider := &localStoreCertProvider{
tlsSettings: tlsSettings,
workerTLSSettings: workerTlsSettings,
legacyWorkerSettings: legacyWorkerSettings,
isLegacyWorkerConfig: legacyWorkerSettings != nil,
logger: logger,
refreshInterval: refreshInterval,
}
provider.initialize()
return provider
}
func (s *localStoreCertProvider) Close() {
func NewServiceErrorInterceptor(
maxMessageLength dynamicconfig.IntPropertyFn,
return &ServiceErrorInterceptor{
maxMessageLength: maxMessageLength,
}
}
func (i *ServiceErrorInterceptor) Intercept(
_ *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
resp, err := handler(ctx, req)
var deserializationError *serialization.DeserializationError
var serializationError *serialization.SerializationError
// convert serialization errors to be captured as serviceerrors across gRPC calls
if errors.As(err, &deserializationError) || errors.As(err, &serializationError) {
err = serviceerror.NewDataLoss(err.Error())
}
// truncate message length if needed
st := serviceerror.ToStatus(err)
if len(st.Message()) > maxLength {
p := st.Proto()
p.Message = util.TruncateUTF8(p.Message, maxLength-len(truncatedSuffix)) + truncatedSuffix
logger log.Logger,
slowRequestThreshold dynamicconfig.DurationPropertyFn,
return &SlowRequestLoggerInterceptor{
logger: logger,
workflowTags: logtags.NewWorkflowTags(tasktoken.NewSerializer(), logger),
slowRequestThreshold: slowRequestThreshold,
}
}
func (i *SlowRequestLoggerInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
// Long-polled methods aren't useful logged.
if api.GetMethodMetadata(info.FullMethod).Polling == api.PollingNone {
startTime := time.Now()
defer func() {
elapsed := time.Since(startTime)
if elapsed > i.slowRequestThreshold() {
i.logSlowRequest(request, info, elapsed)
}
logger log.Logger,
forceRefresh dynamicconfig.BoolPropertyFn,
var saCache atomic.Value
saCache.Store(cache{
searchAttributes: map[string]NameTypeMap{},
dbVersion: 0,
expireOn: time.Time{},
})
return &managerImpl{
logger: logger,
timeSource: timeSource,
cache: saCache,
clusterMetadataManager: clusterMetadataManager,
forceRefresh: forceRefresh,
}
}
// GetSearchAttributes returns all search attributes (including system and build-in) for specified index.
)
func newNexusEndpointClientConfig(dc *dynamicconfig.Collection) *nexusEndpointClientConfig {
nexus_endpoint_client.go
maxDescriptionSizeFn := dynamicconfig.NexusEndpointDescriptionMaxSize.Get(dc)
return &nexusEndpointClientConfig{
maxNameLength: dynamicconfig.NexusEndpointNameMaxLength.Get(dc),
maxTaskQueueLength: dynamicconfig.MaxIDLengthLimit.Get(dc),
maxDescriptionSize: func() int {
return maxDescriptionSizeFn("") // Ignore namespace for endpoints since they are global resources.
},
persistence p.NexusEndpointManager,
logger log.Logger,
return &NexusEndpointClient{
config: config,
namespaceRegistry: namespaceRegistry,
matchingClient: matchingClient,
persistence: persistence,
logger: logger,
}
}
func (c *NexusEndpointClient) Create(
chasmEngine chasm.Engine,
clientBean client.Bean,
return &outboundQueueStandbyTaskExecutor{
stateMachineEnvironment: stateMachineEnvironment{
shardContext: shardCtx,
cache: workflowCache,
logger: logger,
metricsHandler: metricsHandler.WithTags(
metrics.OperationTag(metrics.OperationOutboundQueueProcessorScope),
),
},
config: shardCtx.GetConfig(),
clusterName: clusterName,
chasmEngine: chasmEngine,
clientBean: clientBean,
}
}
func (e *outboundQueueStandbyTaskExecutor) Execute(
chasmEngine chasm.Engine,
clientBean client.Bean,
return &transferQueueStandbyTaskExecutor{
transferQueueTaskExecutorBase: newTransferQueueTaskExecutorBase(
shard,
workflowCache,
logger,
metricProvider,
historyRawClient,
matchingRawClient,
visibilityManager,
chasmEngine,
),
clusterName: clusterName,
clientBean: clientBean,
}
}
func (t *transferQueueStandbyTaskExecutor) Execute(
// exit on their own (possibly never).
// NOTE: Errors returned by the supplied function are ignored.
g.initOnce.Do(g.init)
g.wg.Go(func() {
_ = f(g.ctx)
})
}
// Cancel cancels the `context.Context` that was passed to all goroutines
// spawned via `Go` on this `Group`.
g.initOnce.Do(g.init)
g.cancel()
}
// Wait blocks waiting for all goroutines spawned via `Go` on this `Group`
// instance to complete. If `Go` has not been called then this function returns
// immediately.
g.wg.Wait()
}
g.ctx, g.cancel = context.WithCancel(context.Background())
}
}
return &colName{Name: name}
}
func newSAColName(
fieldName string,
valueType enumspb.IndexedValueType,
return &saColName{
dbColName: newColName(dbColName),
alias: alias,
fieldName: fieldName,
valueType: valueType,
}
}
func newFuncExpr(name string, exprs ...sqlparser.Expr) *sqlparser.FuncExpr {
}
t, _ := time.Parse(time.RFC3339, "9999-12-31T23:59:59Z")
return t
}
// formatComparisonExprStringForError formats comparison expression after
namespaceRegistry namespace.Registry,
req any,
namespaceName, err := GetNamespaceName(namespaceRegistry, req)
if err != nil {
}
}
namespaceRegistry namespace.Registry,
req any,
switch request := req.(type) {
case *workflowservice.RegisterNamespaceRequest:
// For namespace registration requests, we don't expect to find namespace so skip checking caches
// to avoid caching a NotFound error from persistence readthrough
return namespace.Name(request.GetNamespace()), nil
namespaceName := namespace.Name(request.GetNamespace())
_, err := namespaceRegistry.GetNamespace(namespaceName)
if err != nil {
return namespace.EmptyName, err
}
case NamespaceIDGetter:
return namespaceName, nil
return namespace.EmptyName, serviceerror.NewInternalf("unable to extract namespace info from request of type %T", req)
}
}
policy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &RetryableInterceptor{
policy: policy,
isRetryable: isRetryable,
}
}
func (i *RetryableInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
var response any
op := func(ctx context.Context) error {
var err error
response, err = handler(ctx, req)
return err
}
return response, err
}
metricsHandler metrics.Handler,
logger log.Logger,
return &Validator{
searchAttributesProvider: searchAttributesProvider,
searchAttributesMapperProvider: searchAttributesMapperProvider,
searchAttributesNumberOfKeysLimit: searchAttributesNumberOfKeysLimit,
searchAttributesSizeOfValueLimit: searchAttributesSizeOfValueLimit,
searchAttributesTotalSizeLimit: searchAttributesTotalSizeLimit,
visibilityManager: visibilityManager,
allowList: allowList,
suppressErrorSetSystemSearchAttribute: suppressErrorSetSystemSearchAttribute,
metricsHandler: metricsHandler,
logger: logger,
}
}
// Validate search attributes are valid for writing.
shard historyi.ShardContext,
logger log.Logger,
return &StateRebuilderImpl{
shard: shard,
namespaceRegistry: shard.GetNamespaceRegistry(),
eventsCache: shard.GetEventsCache(),
clusterMetadata: shard.GetClusterMetadata(),
executionMgr: shard.GetExecutionManager(),
taskRefresher: workflow.NewTaskRefresher(shard),
rebuiltHistorySize: 0,
rebuiltExternalPayloadSize: 0,
rebuiltExternalPayloadCount: 0,
logger: logger,
}
}
func (r *StateRebuilderImpl) Rebuild(
category Category,
visibilityTimestamp time.Time,
return &FakeTask{
WorkflowKey: workflowKey,
TaskID: common.EmptyEventTaskID,
Version: common.EmptyVersion,
VisibilityTimestamp: visibilityTimestamp,
Category: category,
}
}
func (f *FakeTask) GetKey() Key {
}
f.TaskID = id
}
return f.VisibilityTimestamp
}
func (f *FakeTask) SetVisibilityTime(t time.Time) {
matchingRawClient resource.MatchingRawClient,
chasmEngine chasm.Engine,
return &timerQueueActiveTaskExecutor{
timerQueueTaskExecutorBase: newTimerQueueTaskExecutorBase(
shard,
workflowCache,
workflowDeleteManager,
matchingRawClient,
chasmEngine,
logger,
metricProvider,
config,
true,
),
}
}
func (t *timerQueueActiveTaskExecutor) Execute(
visibilityManager manager.VisibilityManager,
logger log.Logger,
return &Activities{
visibilityManager: visibilityManager,
logger: logger,
}
}
func NewLocalActivities(
namespaceCacheRefreshInterval dynamicconfig.DurationPropertyFn,
logger log.Logger,
return &LocalActivities{
visibilityManager: visibilityManager,
metadataManager: metadataManager,
logger: logger,
namespaceCacheRefreshInterval: namespaceCacheRefreshInterval,
}
}
func (a *LocalActivities) IsAdvancedVisibilityActivity(_ context.Context, _ namespace.Name) (bool, error) {
}
func newInvocationTaskHandler(opts invocationTaskHandlerOptions) *invocationTaskHandler {
tasks.go
return &invocationTaskHandler{
config: opts.Config,
namespaceRegistry: opts.NamespaceRegistry,
metricsHandler: opts.MetricsHandler,
logger: opts.Logger,
httpCallerProvider: opts.HTTPCallerProvider,
httpTraceProvider: opts.HTTPTraceProvider,
historyClient: opts.HistoryClient,
}
}
func (h *invocationTaskHandler) Validate(ctx chasm.Context, cb *Callback, attrs chasm.TaskInvocation, task *callbackspb.InvocationTask) (bool, error) {
}
return &backoffTaskHandler{}
}
// Execute toggles the callback status from BACKING_OFF to SCHEDULED to trigger a new invocation attempt.
}
func NewSchedulerIdleTaskHandler(opts SchedulerIdleTaskHandlerOptions) *SchedulerIdleTaskHandler {
scheduler_tasks.go
return &SchedulerIdleTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
baseLogger: opts.BaseLogger,
}
}
func (r *SchedulerIdleTaskHandler) Execute(
}
func NewSchedulerCallbacksTaskHandler(opts SchedulerCallbacksTaskHandlerOptions) *SchedulerCallbacksTaskHandler {
scheduler_tasks.go
return &SchedulerCallbacksTaskHandler{
config: opts.Config,
historyClient: opts.HistoryClient,
frontendClient: opts.FrontendClient,
}
}
// watchResult holds the outcome of watchRunningStart for a single BufferedStart.
}
return nil
}
// RegisterServices Registers the gRPC calls to the handlers of the library.
}
return nil
}
return nil
}
func (UnimplementedLibrary) mustEmbedUnimplementedLibrary() {}
// tasks within the CHASM framework.
// The format of the returned FQN is: "libName.name"
return libName + "." + name
}
)
buildInfo, ok := debug.ReadBuildInfo()
if !ok {
return
}
InfoData.GoVersion = buildInfo.GoVersion
for _, setting := range buildInfo.Settings {
switch setting.Key {
case "GOARCH":
InfoData.GoArch = setting.Value
case "GOOS":
InfoData.GoOs = setting.Value
case "CGO_ENABLED":
InfoData.CgoEnabled = setting.Value == "1"
case "vcs.revision":
InfoData.GitRevision = setting.Value
)
c := cfg.Services[string(svcName)].RPC
return &c
}
return &cfg.Global.Membership
}
servicePortMap := make(ServicePortMap)
for sn, sc := range cfg.Services {
servicePortMap[primitives.ServiceName(sn)] = sc.RPC.GRPCPort
}
}
searchAttributesMapperProvider searchattribute.MapperProvider,
chasmRegistry *chasm.Registry,
return &visibilityManagerImpl{
store: store,
logger: logger,
searchAttributesMapperProvider: searchAttributesMapperProvider,
chasmRegistry: chasmRegistry,
}
}
p.store.Close()
}
func (p *visibilityManagerImpl) GetReadStoreName(_ namespace.Name) string {
}
return []string{p.store.GetName()}
}
func (p *visibilityManagerImpl) HasStoreName(stName string) bool {
// Otherwise, the type name of the first non-wrapper error in the depth-first traversal of err's tree is returned.
// We consider errors wrapped via [fmt.Errorf], [errors.Join] and some pkg/errors functions to be wrapper errors.
// If any error in the tree has an explicit type name, use it, preferring the first one in the DFS traversal.
var typedErr typedError
if errors.As(err, &typedErr) {
return typedErr.ErrorTypeName()
}
// Special case for context.Cancel error. It is of type errorString, which is not very useful.
return "context.Canceled"
}
// Special case for context.DeadlineExceeded error. It is of unexported type deadlineExceededError.
return "context.DeadlineExceeded"
}
// Otherwise, do a DFS traversal of the error tree, ignoring wrapper errors.
for len(q) > 0 {
err = q[len(q)-1]
q = q[:len(q)-1]
errType := fmt.Sprintf("%T", err)
if !wrapperErrorTypes[errType] {
return strings.TrimPrefix(errType, "*")
}
// The error could implement zero or one of the unary or multi-error wrapper interfaces. It's impossible to
// implement both because they have the same method name. As a result, this is still deterministic.
timeSource clock.TimeSource,
config *Config,
return &namespaceHandler{
logger: logger,
metadataMgr: metadataMgr,
namespaceRegistry: namespaceRegistry,
clusterMetadata: clusterMetadata,
namespaceReplicator: namespaceReplicator,
namespaceAttrValidator: nsmanager.NewValidator(clusterMetadata),
archivalMetadata: archivalMetadata,
archiverProvider: archiverProvider,
timeSource: timeSource,
config: config,
}
}
// RegisterNamespace register a new namespace
paginationFnProvider PaginationFnProvider,
r Range,
return &IteratorImpl{
paginationFnProvider: paginationFnProvider,
remainingRange: r,
// lazy initialized to prevent task pre-fetching on creating the iterator
pagingIterator: nil,
}
}
if i.pagingIterator == nil {
i.pagingIterator = collection.NewPagingIterator(i.paginationFnProvider(i.remainingRange))
}
}
relocateAttributesMinBlobSize dynamicconfig.IntPropertyFnWithNamespaceFilter,
externalPayloadsEnabled dynamicconfig.BoolPropertyFnWithNamespaceFilter,
return &visibilityQueueTaskExecutor{
shardContext: shardContext,
cache: workflowCache,
logger: logger,
metricProvider: metricProvider,
visibilityMgr: visibilityMgr,
ensureCloseBeforeDelete: ensureCloseBeforeDelete,
enableCloseWorkflowCleanup: enableCloseWorkflowCleanup,
relocateAttributesMinBlobSize: relocateAttributesMinBlobSize,
externalPayloadsEnabled: externalPayloadsEnabled,
}
}
func (t *visibilityQueueTaskExecutor) Execute(
var Module = fx.Options(fx.Provide(NewResult))
return fxResult{
Component: &workerComponent{},
}
}
func (c *workerComponent) Register(registry sdkworker.Registry, ns *namespace.Namespace, _ workercommon.RegistrationDetails) func() {
fx.go
registry.RegisterWorkflowWithOptions(DummyWorkflow, workflow.RegisterOptions{Name: DummyWFTypeName})
return nil
}
func (c *workerComponent) DedicatedWorkerOptions(ns *namespace.Namespace) *workercommon.PerNSDedicatedWorkerOptions {
fx.go
return &workercommon.PerNSDedicatedWorkerOptions{
Enabled: true,
}
}
)
// lookup localhost and favor the first ipv4 address
// unless there are only ipv6 addresses available
ips, err := net.LookupIP(domain)
if err != nil || len(ips) == 0 {
// fallback to default instead of error
return localhostIPDefault
}
if ip4 := ip.To4(); ip4 != nil {
}
}
return ips[len(ips)-1].String()
// GetLocalhostIP returns the ip address of the localhost domain
localhostIP := os.Getenv(localhostIPEnv)
ip := net.ParseIP(localhostIP)
if ip != nil {
// if localhost is an ip return it
return ip.String()
}
// otherwise, ignore the value and lookup `localhost`
}
saMapperProvider searchattribute.MapperProvider,
saValidator *searchattribute.Validator,
return &frontendHandler{
callbackValidator: callbackValidator,
linkValidator: linkValidator,
client: client,
config: config,
logger: logger,
metricsHandler: metricsHandler,
namespaceRegistry: namespaceRegistry,
saMapperProvider: saMapperProvider,
saValidator: saValidator,
}
}
// IsStandaloneActivityEnabled checks if standalone activities are enabled for the given namespace
}
return nexusTaskHandlerBase{
config: o.Config,
namespaceRegistry: o.NamespaceRegistry,
metricsHandler: o.MetricsHandler,
logger: o.Logger,
clientProvider: o.ClientProvider,
endpointRegistry: o.EndpointRegistry,
httpTraceProvider: o.HTTPTraceProvider,
historyClient: o.HistoryClient,
chasmRegistry: o.ChasmRegistry,
}
}
// nexusTaskHandlerBase contains common dependencies shared by the invocation and cancellation task handlers.
logger log.Logger,
metricsHandler metrics.Handler,
return &archiverProvider{
historyArchiverConfigs: historyArchiverConfigs,
visibilityArchiverConfigs: visibilityArchiverConfigs,
executionManager: executionManager,
logger: logger,
metricsHandler: metricsHandler,
customHistoryArchiverFactory: customHistoryArchiverFactory,
customVisibilityArchiverFactory: customVisibilityArchiverFactory,
historyArchivers: make(map[string]archiver.HistoryArchiver),
visibilityArchivers: make(map[string]archiver.VisibilityArchiver),
}
}
func (p *archiverProvider) GetHistoryArchiver(scheme string) (historyArchiver archiver.HistoryArchiver, err error) {
// NewRealTimeSource returns a timeSource that uses the real wall timeSource time.
return RealTimeSource{}
}
// Now returns the current time, with the location set to UTC.
return time.Now().UTC()
}
// Since returns the time elapsed since t
// AfterFunc is a pass-through to time.AfterFunc.
return time.AfterFunc(d, f)
}
// NewTimer is a pass-through to time.NewTimer.
t := time.NewTimer(d)
return t.C, t
}
metadata Metadata,
tlsProvider tlsConfigProvider,
cache := &FrontendHTTPClientCache{
metadata: metadata,
tlsProvider: tlsProvider,
}
cache.clients = collection.NewFallibleOnceMap(cache.newClientForCluster)
metadata.RegisterMetadataChangeCallback(cache, cache.evictionCallback)
return cache
}
// Get returns a cached HttpClient if available, or constructs a new one for the given cluster name.
// It invalidates clients which are either no longer present or have had their HTTP address changed.
// It is assumed that TLS information has not changed for clusters that are unmodified.
func (c *FrontendHTTPClientCache) evictionCallback(oldClusterMetadata map[string]*ClusterInformation, newClusterMetadata map[string]*ClusterInformation) {
frontend_http_client.go
for oldClusterName, oldClusterInfo := range oldClusterMetadata {
if oldClusterName == c.metadata.GetCurrentClusterName() || oldClusterInfo == nil {
continue
}
)
f, err := newFactory(params)
if err != nil {
return nil, err
}
return f, nil
}
m := f.getMonitor()
lc.Append(fx.StopHook(m.Stop))
return m
}
func provideHostInfoProvider(lc fx.Lifecycle, f *factory) (membership.HostInfoProvider, error) {
fx.go
return f.getHostInfoProvider()
}
}
d := metricDefinition{
name: name,
description: "",
unit: "",
}
for _, opt := range opts {
opt.apply(&d)
}
return d
}
return md.name
}
func (md metricDefinition) Unit() MetricUnit {
type mutationFunc func(*Namespace)
f(ns)
}
// WithActiveCluster assigns the active cluster to a Namespace during a Clone
// WithGlobalFlag sets whether or not this Namespace is global.
return mutationFunc(
func(ns *Namespace) {
ns.replicationResolver.SetGlobalFlag(b)
})
}
// WithNotificationVersion assigns a notification version to the Namespace.
return mutationFunc(
func(ns *Namespace) {
ns.notificationVersion = v
})
}
// NewCompletionHTTPHandler constructs an [http.Handler] from given options for handling operation completion requests.
if options.Logger == nil {
options.Logger = slog.Default()
}
options.Serializer = nexus.DefaultSerializer()
}
}
options: options,
BaseHTTPHandler: BaseHTTPHandler{
Logger: options.Logger,
FailureConverter: options.FailureConverter,
},
}
}
writeMaxQPS dynamicconfig.IntPropertyFn,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return &visibilityManagerRateLimited{
delegate: delegate,
readRateLimiter: newPriorityRateLimiter(readMaxQPS, operatorRPSRatio),
writeRateLimiter: newPriorityRateLimiter(writeMaxQPS, operatorRPSRatio),
}
}
m.delegate.Close()
}
func (m *visibilityManagerRateLimited) GetReadStoreName(nsName namespace.Name) string {
}
func (m *visibilityManagerRateLimited) GetStoreNames() []string {
visibility_manager_rate_limited.go
return m.delegate.GetStoreNames()
}
func (m *visibilityManagerRateLimited) HasStoreName(stName string) bool {
mh metrics.Handler,
logger log.Logger,
l := log.With(
logger,
tag.String("service", "client"),
tag.String("address", address),
)
return &dialTracer{
address: address,
metricsHandler: mh,
logger: l,
}
}
func (d *dialTracer) beginNetworkDial(ctx context.Context) (context.Context, *networkDialTrace) {
func NewFrontendServiceErrorInterceptor(
logger log.Logger,
return func(
ctx context.Context,
req any,
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (any, error) {
resp, err := handler(ctx, req)
if err == nil {
}
case *serviceerrors.ShardOwnershipLost:
err = serviceerror.NewUnavailable("shard unavailable, please backoff and retry")
pollWaitForToken dynamicconfig.BoolPropertyFnWithNamespaceFilter,
metricsHandler metrics.Handler,
return &NamespaceRateLimitInterceptorImpl{
namespaceRegistry: namespaceRegistry,
rateLimiter: rateLimiter,
tokens: tokens,
pollMethods: pollMethods,
pollWaitForToken: pollWaitForToken,
metricsHandler: metricsHandler,
}
}
func (ni *NamespaceRateLimitInterceptorImpl) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
if ns := MustGetNamespaceName(ni.namespaceRegistry, req); ns != namespace.EmptyName {
method := info.FullMethod
if IsLongPollGetWorkflowExecutionHistoryRequest(req) {
func SpanExportersFromEnv(
envVars envVarLookup,
exporters := map[SpanExporterType]otelsdktrace.SpanExporter{}
exporterTypes, ok := envVars(OtelTracesExporterTypesEnvKey)
if !ok {
}
for exporterType := range strings.SplitSeq(exporterTypes, ",") {
rsn primitives.ServiceName,
envVars envVarLookup,
// map "internal-frontend" to "frontend" for the purpose of tracing
if rsn == primitives.InternalFrontendService {
rsn = primitives.FrontendService
}
// allow custom prefix via env vars
if customServicePrefix, found := envVars(OtelServiceNameEnvKey); found {
serviceNamePrefix = customServicePrefix
}
}
clusterMetadataManager persistence.ClusterMetadataManager,
sdkVersionRecorder *interceptor.SDKVersionInterceptor,
return &VersionChecker{
config: config,
shutdownChan: make(chan struct{}),
metricsHandler: metricsHandler.WithTags(metrics.OperationTag(metrics.VersionCheckScope)),
clusterMetadataManager: clusterMetadataManager,
sdkVersionRecorder: sdkVersionRecorder,
}
}
if vc.config.EnableServerVersionCheck() {
vc.startOnce.Do(func() {
// TODO: specify a timeout for the context
}
if vc.config.EnableServerVersionCheck() {
vc.stopOnce.Do(func() {
close(vc.shutdownChan)
logger log.Logger,
config *configs.Config,
return &resendHandlerImpl{
namespaceRegistry: namespaceRegistry,
clientBean: clientBean,
serializer: serializer,
engineProvider: historyEngineProvider,
remoteHistoryFetcher: remoteHistoryFetcher,
eventImporter: importer,
logger: logger,
clusterMetadata: clusterMetadata,
config: config,
}
}
// ResendHistoryEvents is used to retrieve history events from remote and apply to current(passive) cluster. Mostly handle 3 cases:
// NewDefaultHandoverTrackerFactory returns a factory that creates the default OSS HandoverTracker.
return func(params HandoverTrackerParams) HandoverTracker {
handoverNamespaces: make(map[namespace.Name]*namespaceHandOverInfo),
clusterMetadata: params.ClusterMetadata,
getMaxReplicationTaskID: params.GetMaxReplicationTaskID,
errorByStateFn: params.ErrorByStateFn,
notifyReplicationFn: params.NotifyReplicationFn,
logger: params.Logger,
}
}
}
}
func (t *defaultHandoverTracker) ResolvePendingTaskIDs(maxReplicationTaskID int64) {
handover_tracker.go
for _, handoverInfo := range t.handoverNamespaces {
if handoverInfo.MaxReplicationTaskID == PendingMaxReplicationTaskID {
handoverInfo.MaxReplicationTaskID = maxReplicationTaskID
}
func newCancellationInvocationTaskHandler(opts cancellationInvocationTaskHandlerOptions) *cancellationInvocationTaskHandler {
cancellation_tasks.go
return &cancellationInvocationTaskHandler{
nexusTaskHandlerBase: opts.toBase(),
}
}
func (h *cancellationInvocationTaskHandler) Validate(
}
func newCancellationBackoffTaskHandler(opts commonTaskHandlerOptions) *cancellationBackoffTaskHandler {
cancellation_tasks.go
return &cancellationBackoffTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
logger: opts.Logger,
}
}
func (h *cancellationBackoffTaskHandler) Validate(
// contextValues builds the CHASM context values exposed to scheduler components.
var tweakables dynamicconfig.TypedPropertyFnWithNamespaceFilter[Tweakables]
if c != nil {
}
}
)
return &Config{
Tweakables: CurrentTweakables.Get(dc),
ServiceCallTimeout: ServiceCallTimeout.Get(dc),
EncodeInternalTokenWithEnvelope: callback.EncodeInternalTokenWithEnvelope.Get(dc),
RetryPolicy: func() backoff.RetryPolicy {
return backoff.NewExponentialRetryPolicy(
RetryPolicyInitialInterval.Get(dc)(),
logger log.Logger,
throttledLogger log.Logger,
return &metricClient{
client: client,
metricsHandler: metricsHandler,
logger: logger,
throttledLogger: throttledLogger,
}
}
if s, ok := c.client.(interface{ Stop() }); ok {
s.Stop()
}
}
// NewDefaultAuthorizer creates a default authorizer
return &defaultAuthorizer{}
}
var resultAllow = Result{Decision: DecisionAllow}
// Namespace Writer is allowed to access non admin APIs on their namespaces.
// Namespace Reader is allowed to access non admin readonly APIs on their namespaces.
func (a *defaultAuthorizer) Authorize(_ context.Context, claims *Claims, target *CallTarget) (Result, error) {
default_authorizer.go
// APIs that are essentially read-only health checks with no sensitive information are
// always allowed
if IsHealthCheckAPI(target.APIName) {
return resultAllow, nil
}
if claims == nil {
}
metadata := api.GetMethodMetadata(target.APIName)
// FullJitter return random number from 0 to input, inclusive, exclusive
return T(rand.Float64() * float64(input))
}
// Jitter return random number from (1-coefficient)*input to (1+coefficient)*input, inclusive, exclusive
func Jitter[T ~int64 | ~int | ~int32 | ~float64 | ~float32](input T, coefficient float64) T {
jitter.go
validateCoefficient(coefficient)
if coefficient == 0 {
return input
}
addon := rand.Float64() * 2 * (float64(input) - base)
return T(base + addon)
}
if coefficient < 0 || coefficient > 1 {
panic("coefficient cannot be < 0 or > 1")
}
fx.Invoke(MetadataLifetimeHooks),
fx.Provide(fx.Annotate(
fx.ResultTags(`group:"deadlockDetectorRoots"`),
)),
lc fx.Lifecycle,
clusterMetadata Metadata,
lc.Append(
fx.Hook{
OnStart: func(context.Context) error {
clusterMetadata.Start()
return nil
},
clusterMetadata.Stop()
return nil
},
},
)
// NewOnceMap creates a [OnceMap] from a given construct function.
// construct should be kept light as it is called while holding a lock on the entire map.
return &OnceMap[K, T]{
construct: construct,
inner: make(map[K]T, 0),
}
}
func (m *OnceMap[K, T]) Get(key K) T {
// NewFallibleOnceMap creates a [FallibleOnceMap] from a given construct function.
// construct should be kept light as it is called while holding a lock on the entire map.
func NewFallibleOnceMap[K comparable, T any](construct func(K) (T, error)) *FallibleOnceMap[K, T] {
oncemap.go
return &FallibleOnceMap[K, T]{
construct: construct,
inner: make(map[K]T, 0),
}
}
func (p *FallibleOnceMap[K, T]) Get(key K) (T, error) {
// Validate validates the archival config
if !isArchivalConfigValid(a.History.State, a.History.EnableRead, namespaceDefaults.History.State, namespaceDefaults.History.URI, a.History.Provider != nil) {
return errors.New("invalid history archival config")
}
if !isArchivalConfigValid(a.Visibility.State, a.Visibility.EnableRead, namespaceDefaults.Visibility.State, namespaceDefaults.Visibility.URI, a.Visibility.Provider != nil) {
archival.go
return errors.New("invalid visibility archival config")
}
}
domianDefaultURI string,
specifiedProvider bool,
archivalEnabled := clusterStatus == ArchivalEnabled
URISet := len(domianDefaultURI) != 0
validEnable := archivalEnabled && URISet && specifiedProvider
validDisabled := !archivalEnabled && !enableRead && namespaceDefaultStatus != ArchivalEnabled && !URISet && !specifiedProvider
return validEnable || validDisabled
}
}
return Int64ToString(int64(v))
}
func Uint64ToString(v uint64) string {
}
return strconv.FormatInt(v, 10)
}
return Int64ToString(int64(v))
}
return strconv.FormatUint(uint64(v), 10)
}
func Int64SetToSlice(
)
if globalRegistry.queried.Load() {
panic("dynamicconfig.New*Setting must only be called from static initializers")
}
globalRegistry.settings = make(map[Key]GenericSetting)
}
if globalRegistry.settings[s.Key()] != nil {
// nolint:forbidigo // only called during static initialization
panic(fmt.Sprintf("duplicate registration of dynamic config key: %q", s.Key().String()))
}
}
if !globalRegistry.queried.Load() {
globalRegistry.queried.Store(true)
}
return globalRegistry.settings[k]
}
}
ret := make([]elastic.Sorter, 0, len(defaultSorterFields))
for _, item := range defaultSorterFields {
fs := elastic.NewFieldSort(item.name)
if item.desc {
fs.Desc()
}
if item.missing_first {
fs.Missing("_first")
} else {
fs.Missing("_last")
}
}
}()
ttl time.Duration,
logger log.Logger,
return &XDCCacheImpl{
cache: cache.New(
max(xdcMinCacheSize, maxBytes),
&cache.Options{
TTL: ttl,
Pin: false,
},
),
logger: logger,
}
}
func (e *XDCCacheImpl) Put(
}
return durationpb.New(td)
}
func MinDurationPtr(d1 *durationpb.Duration, d2 *durationpb.Duration) *durationpb.Duration {
}
return durationMultipleOf(h, time.Hour)
}
return durationMultipleOf(int64(d), time.Hour*24)
}
return DurationPtr(time.Duration(amt) * mult)
}
// ValidateAndCapProtoDuration validates protobuf durations for two conditions:
decidingReservation Reservation,
otherReservations []Reservation,
return &PriorityReservationImpl{
decidingReservation: decidingReservation,
otherReservations: otherReservations,
}
}
// OK returns whether the limiter can provide the requested number of tokens
return r.decidingReservation.OK()
}
// Cancel indicates that the reservation holder will not perform the reserved action
// DelayFrom returns the duration for which the reservation holder must wait
// before taking the reserved action. Zero duration means act immediately.
func (r *PriorityReservationImpl) DelayFrom(now time.Time) time.Duration {
priority_reservation_impl.go
return r.decidingReservation.DelayFrom(now)
}
// NewHealthInterceptor returns a new HealthInterceptor. It starts with state not healthy.
return &HealthInterceptor{}
}
func (i *HealthInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
// only enforce health check on WorkflowService and OperatorService
if strings.HasPrefix(info.FullMethod, api.WorkflowServicePrefix) ||
strings.HasPrefix(info.FullMethod, api.OperatorServicePrefix) {
if !i.healthy.Load() {
return nil, notHealthyErr
}
}
}
i.healthy.Store(healthy)
}
// Requires the context to be pre-wrapped with contextutil.WithMetadataContext() before the RPC call.
// This is typically done by server-side interceptors (e.g., ContextMetadataInterceptor).
func TrailerToContextMetadataInterceptor(logger log.Logger) grpc.UnaryClientInterceptor {
trailer_to_context_metadata_interceptor.go
throttledLogger := log.NewThrottledLogger(logger, func() float64 {
return 1.0 / 30.0 // 1 log per 30 seconds
})
return func(
ctx context.Context,
method string,
req, reply any,
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
var trailer metadata.MD
opts = append(opts, grpc.Trailer(&trailer))
}
_, isNoop := tp.(otelnoop.TracerProvider)
return !isNoop
}
timeSource clock.TimeSource,
visibilityManager manager.VisibilityManager,
deleteManager := &DeleteManagerImpl{
shardContext: shardContext,
workflowCache: cache,
metricsHandler: shardContext.GetMetricsHandler(),
config: config,
timeSource: timeSource,
visibilityManager: visibilityManager,
}
return deleteManager
}
func (m *DeleteManagerImpl) AddDeleteExecutionTask(
workflowCache wcache.Cache,
logger log.Logger,
return &workflowResetterImpl{
shardContext: shardContext,
namespaceRegistry: shardContext.GetNamespaceRegistry(),
clusterMetadata: shardContext.GetClusterMetadata(),
executionMgr: shardContext.GetExecutionManager(),
workflowCache: workflowCache,
stateRebuilder: NewStateRebuilder(shardContext, logger),
transaction: workflow.NewTransaction(shardContext),
logger: logger,
}
}
// ResetWorkflow resets the given base run and creates a new run that would start after baseNextEventID. It additionally does the following
maxReaderCount dynamicconfig.IntPropertyFn,
grouper Grouper,
return &mitigatorImpl{
readerGroup: readerGroup,
monitor: monitor,
logger: logger,
metricsHandler: metricsHandler,
maxReaderCount: maxReaderCount,
actionRunner: runAction,
grouper: grouper,
}
}
func (m *mitigatorImpl) Mitigate(alert Alert) {
)
func NewPriorityAssigner(nsRegistry namespace.Registry, currentClusterName string) PriorityAssigner {
priority_assigner.go
return &priorityAssignerImpl{
nsRegistry: nsRegistry,
currentClusterName: currentClusterName,
}
}
func (a *priorityAssignerImpl) Assign(executable Executable) tasks.Priority {
}
return NewStaticPriorityAssigner(tasks.PriorityHigh)
}
func NewStaticPriorityAssigner(priority tasks.Priority) PriorityAssigner {
priority_assigner.go
return staticPriorityAssigner{priority: priority}
}
func (a staticPriorityAssigner) Assign(_ Executable) tasks.Priority {
registry *chasm.Registry,
library *Library,
return registry.Register(library)
}
// httpCallerProviderProvider provides an HTTPCallerProvider for CHASM callbacks.
httpClientCache *cluster.FrontendHTTPClientCache,
logger log.Logger,
localClient, err := rpcFactory.CreateLocalFrontendHTTPClient()
if err != nil {
return nil, fmt.Errorf("cannot create local frontend HTTP client: %w", err)
}
callbackTokenGenerator := commonnexus.NewCallbackTokenGenerator()
m := collection.NewOnceMap(func(queuescommon.NamespaceIDAndDestination) HTTPCaller {
return func(r *http.Request) (*http.Response, error) {
return routeRequest(r,
saMapperProvider searchattribute.MapperProvider,
saValidator *searchattribute.Validator,
return &frontendHandler{
client: client,
config: config,
logger: logger,
namespaceRegistry: namespaceRegistry,
endpointRegistry: endpointRegistry,
saMapperProvider: saMapperProvider,
saValidator: saValidator,
}
}
func (h *frontendHandler) StartNexusOperationExecution(
// NewNoopLogger return a noopLogger
return &noopLogger{}
}
func (n *noopLogger) DPanic(string, ...tag.Tag) {}
func (n *noopLogger) Panic(string, ...tag.Tag) {}
return n
}
// NewSlogLogger creates an slog.Logger from a given logger.
// Try extracting and underlying slog logger (e.g. for Temporal CLI).
if sl, ok := logger.(SLogWrapper); ok {
return sl.SLog()
}
return slog.New(&handler{logger: logger, zapLogger: extractZapLogger(logger), group: "", tags: nil})
}
}
switch l := logger.(type) {
case *zapLogger:
return l.zl
return extractZapLogger(l.logger)
}
}
// withIncreasedSkip increases the skip level for the given logger if it embeds a zapLogger.
switch l := logger.(type) {
case *zapLogger:
return l.Skip(skip)
}
// Default to not increasing the skip, it's better to have a logger than not having one.
}
func NewRequestRateLimiterAdapter(
rateLimiter RateLimiter,
return &RequestRateLimiterAdapterImpl{
rateLimiter: rateLimiter,
}
}
func (r *RequestRateLimiterAdapterImpl) Allow(
now time.Time,
request Request,
return r.rateLimiter.AllowN(now, request.Token)
}
func (r *RequestRateLimiterAdapterImpl) Reserve(
now time.Time,
request Request,
return r.rateLimiter.ReserveN(now, request.Token)
}
func (r *RequestRateLimiterAdapterImpl) Wait(
var _ grpc.UnaryServerInterceptor = (*NamespaceLogInterceptor)(nil).Intercept
func NewNamespaceLogInterceptor(namespaceRegistry namespace.Registry, logger log.Logger) *NamespaceLogInterceptor {
namespace_logger.go
return &NamespaceLogInterceptor{
namespaceRegistry: namespaceRegistry,
logger: logger,
}
}
func (nli *NamespaceLogInterceptor) Intercept(
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
if nli.logger != nil {
methodName := api.MethodName(info.FullMethod)
namespace := MustGetNamespaceName(nli.namespaceRegistry, req)
}
func (ec *ExportConfig) SpanExporters() (map[SpanExporterType]otelsdktrace.SpanExporter, error) {
config.go
return ec.inner.SpanExporters()
}
func (ec *ExportConfig) MetricExporters() ([]metric.Exporter, error) {
// unmarshalled into this ExportConfig object. The returned SpanExporters have
// not been started.
func (ec *exportConfig) SpanExporters() (map[SpanExporterType]otelsdktrace.SpanExporter, error) {
config.go
out := make(map[SpanExporterType]otelsdktrace.SpanExporter, len(ec.Exporters))
for _, expcfg := range ec.Exporters {
if !strings.HasPrefix(expcfg.Kind.Signal, "trace") {
continue
}
_, isNoop := t.(otelnoop.Tracer)
return !isNoop
}
rateFn quotas.RateFn,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewPriorityRateLimiterHelper(
quotas.NewDefaultIncomingRateBurst(rateFn),
operatorRPSRatio,
RequestToPriority,
APIPrioritiesOrdered,
)
}
func NewNamespaceRateLimiter(
namespaceRateFn quotas.NamespaceRateFn,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewNamespaceRequestRateLimiter(
func(req quotas.Request) quotas.RequestRateLimiter {
return quotas.NewPriorityRateLimiterHelper(
quotas.NewNamespaceRateBurst(
replicationTaskExecutor nsreplication.TaskExecutor,
currentCluster string,
return &eagerNamespaceRefresherImpl{
metadataManager: metadataManager,
namespaceRegistry: namespaceRegistry,
logger: logger,
clientBean: clientBean,
replicationTaskExecutor: replicationTaskExecutor,
currentCluster: currentCluster,
metricsHandler: metricsHandler,
}
}
func (e *eagerNamespaceRefresherImpl) SyncNamespaceFromSourceCluster(
)
// Experiment with no op rate limiter
return quotas.NoopRequestRateLimiter
}
// Experiment with no op rate limiter
return quotas.NoopRequestRateLimiter
}
return quotas.NoopRequestRateLimiter
}
rateFn quotas.RateFn,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewPriorityRateLimiterHelper(
quotas.NewDefaultIncomingRateBurst(rateFn),
operatorRPSRatio,
RequestToPriority,
APIPrioritiesOrdered,
)
}
func NewNamespaceRateLimiter(
namespaceRateFn quotas.NamespaceRateFn,
operatorRPSRatio dynamicconfig.FloatPropertyFn,
return quotas.NewNamespaceRequestRateLimiter(
func(req quotas.Request) quotas.RequestRateLimiter {
return quotas.NewPriorityRateLimiterHelper(
quotas.NewNamespaceRateBurst(
allowDeleteNamespaceIfNexusEndpointTarget dynamicconfig.BoolPropertyFn,
nexusEndpointListDefaultPageSize dynamicconfig.IntPropertyFn,
return &localActivities{
metadataManager: metadataManager,
clusterMetadata: clusterMetadata,
nexusEndpointManager: nexusEndpointManager,
logger: logger,
protectedNamespaces: protectedNamespaces,
allowDeleteNamespaceIfNexusEndpointTarget: allowDeleteNamespaceIfNexusEndpointTarget,
nexusEndpointListDefaultPageSize: nexusEndpointListDefaultPageSize,
}
}
func (a *localActivities) GetNamespaceInfoActivity(ctx context.Context, nsID namespace.ID, nsName namespace.Name) (getNamespaceInfoResult, error) {
// NewSpecBuilder takes the compute-limit getters directly (rather than a *dynamicconfig.Collection)
// so the dynamic-config plumbing stays in the wiring layer, per the common codebase pattern.
func NewSpecBuilder(warnIterations, maxIterations dynamicconfig.IntPropertyFn) *SpecBuilder {
spec.go
return &SpecBuilder{
warnIterations: warnIterations,
maxIterations: maxIterations,
locationCache: cache.New(1000,
&cache.Options{
TTL: 24 * time.Hour,
},
),
}
}
func (b *SpecBuilder) NewCompiledSpec(spec *schedulepb.ScheduleSpec) (*CompiledSpec, error) {
logger log.Logger,
namespaceRegistry namespace.Registry,
return &handler{
config: config,
historyHandler: historyHandler,
linkValidator: linkValidator,
logger: logger,
metricsHandler: metricsHandler,
namespaceRegistry: namespaceRegistry,
}
}
// StartActivityExecution schedules an activity execution. Note that while external callers refer to
registry *chasm.Registry,
library *Library,
return registry.Register(library)
}
var Module = fx.Module(
"chasm.lib.scheduler",
fx.Provide(ConfigProvider),
return legacyscheduler.NewSpecBuilder(
dynamicconfig.SchedulerSpecWarnIterations.Get(dc),
dynamicconfig.SchedulerSpecMaxIterations.Get(dc),
)
}),
fx.Provide(NewSpecProcessor),
fx.Provide(newHandler),
fx.Provide(NewSchedulerIdleTaskHandler),
})
service := nexus.NewService("TestService")
service.MustRegister(TestOperation)
return service
}
type testOperationProcessor struct {
}
sp := chasm.NewNexusServiceProcessor("TestService")
sp.MustRegisterOperation("TestOperation", chasm.NewRegisterableNexusOperationProcessor(testOperationProcessor{}))
return sp
}
dc *dynamicconfig.Collection,
testHooks testhooks.TestHooks,
lb := &defaultLoadBalancer{
namespaceIDToName: namespaceIDToName,
nReadPartitions: dynamicconfig.MatchingNumTaskqueueReadPartitions.Get(dc),
nWritePartitions: dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(dc),
testHooks: testHooks,
taskQueueLBs: make(map[tqid.TaskQueue]*tqLoadBalancer),
}
return lb
}
func (lb *defaultLoadBalancer) PickWritePartition(
logger log.Logger,
testHooks testhooks.TestHooks,
return &taskExecutorImpl{
currentCluster: currentCluster,
metadataManager: metadataManagerV2,
dataMerger: dataMerger,
admitter: admitter,
logger: logger,
testHooks: testHooks,
}
}
// Execute handles receiving of the namespace replication task
var _ sqlplugin.Plugin = (*plugin)(nil)
sql.RegisterPlugin(PluginName, &plugin{
driver: &driver.PQDriver{},
queryConverter: &queryConverter{},
})
sql.RegisterPlugin(PluginNamePGX, &plugin{
driver: &driver.PGXDriver{},
queryConverter: &queryConverter{},
})
}
func (p *plugin) GetVisibilityQueryConverter() sqlplugin.VisibilityQueryConverter {
)
items := make([]string, len(fields))
for i, field := range fields {
items[i] = fmt.Sprintf("%s = excluded.%s", field, field)
}
return fmt.Sprintf(
// The WHERE clause ensures that no update occurs if the version is behind the saved version.
"ON CONFLICT (namespace_id, run_id) DO UPDATE SET %s WHERE executions_visibility.%s < EXCLUDED.%s",
strings.Join(items, ", "), sqlplugin.VersionColumnName, sqlplugin.VersionColumnName,
)
}
// ToSQLiteDateTime converts to time to SQLite datetime
if t.IsZero() {
return minSQLiteDateTime
}
}
// FromSQLiteDateTime converts SQLite datetime and returns go time
if t.Equal(minSQLiteDateTime) {
return time.Time{}.UTC()
}
}
t, err := time.Parse(time.RFC3339, "1000-01-01T00:00:00Z")
if err != nil {
return time.Unix(0, 0).UTC()
}
}
)
items := make([]string, len(fields))
for i, field := range fields {
items[i] = fmt.Sprintf("%s = excluded.%s", field, field)
}
return fmt.Sprintf(
// The WHERE clause ensures that no update occurs if the version is behind the saved version.
"ON CONFLICT (namespace_id, run_id) DO UPDATE SET %s WHERE executions_visibility.%s < EXCLUDED.%s",
strings.Join(items, ", "), sqlplugin.VersionColumnName, sqlplugin.VersionColumnName,
)
}
}
t := reflect.TypeFor[VisibilityRow]()
dbFields := make([]string, t.NumField())
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
dbFields[i] = f.Tag.Get("db")
if dbFields[i] == "" {
dbFields[i] = strcase.ToSnake(f.Name)
}
}
}
nsRegistry namespace.Registry,
visibilityMgr manager.VisibilityManager,
return &ChasmVisibilityManager{
registry: registry,
nsRegistry: nsRegistry,
visibilityMgr: visibilityMgr,
}
}
func ChasmVisibilityManagerProvider(
nsRegistry namespace.Registry,
visibilityMgr manager.VisibilityManager,
return NewChasmVisibilityManager(registry, nsRegistry, visibilityMgr)
}
// ListExecutions implements the Engine interface for visibility queries.
}
return &UnsafeSQLString{Val: val}
}
func NewColName(name string) *ColumnName {
}
func NewSAColumn(alias string, fieldName string, valueType enumspb.IndexedValueType) *SAColumn {
util.go
return &SAColumn{
Alias: alias,
FieldName: fieldName,
ValueType: valueType,
}
}
func NamespaceDivisionSAColumn() *SAColumn {
delay time.Duration,
timeSource clock.TimeSource,
if delay < 0 {
return nil, fmt.Errorf("%w: %v", ErrNegativeDelay, delay)
}
delegator.SetRateLimiter(NoopRequestRateLimiter)
timer := timeSource.AfterFunc(delay, func() {
delegator.SetRateLimiter(rl)
})
RequestRateLimiter: &delegator,
timer: timer,
}, nil
}
callerSegment int32,
initiation string,
return Request{
API: api,
Token: token,
Caller: caller,
CallerType: callerType,
CallerSegment: callerSegment,
Initiation: initiation,
}
}
registry *hsm.Registry,
executorOptions TaskExecutorOptions,
exec := taskExecutor{executorOptions}
if err := hsm.RegisterImmediateExecutor(
registry,
exec.executeInvocationTask,
); err != nil {
return err
}
registry,
exec.executeBackoffTask,
)
}
}
return TaskTypeInvocation
}
func (t InvocationTask) Destination() string {
var _ hsm.Task = BackoffTask{}
return TaskTypeBackoff
}
func (t BackoffTask) Deadline() time.Time {
}
if err := reg.RegisterTaskSerializer(TaskTypeInvocation, InvocationTaskSerializer{}); err != nil {
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeBackoff, BackoffTaskSerializer{}); err != nil { // nolint:revive
tasks.go
return err
}
}
healthCheckFn func(ctx context.Context, hostAddress string) (*historyservice.DeepHealthCheckResponse, error),
logger log.Logger,
return &healthCheckerImpl{
serviceName: serviceName,
membershipMonitor: membershipMonitor,
hostFailurePercentage: hostFailurePercentage,
hostDeclinedServingProportion: hostDeclinedServingProportion,
healthCheckFn: healthCheckFn,
logger: logger,
}
}
func (h *healthCheckerImpl) Check(ctx context.Context) (HealthCheckResult, error) {
searchAttributeProvider searchattribute.Provider,
visibilityManger manager.VisibilityManager,
return &archiver{
archiverProvider: archiverProvider,
metricsHandler: metricsHandler.WithTags(metrics.OperationTag(metrics.ArchiverClientScope)),
logger: logger,
rateLimiter: rateLimiter,
searchAttributeProvider: searchAttributeProvider,
visibilityManager: visibilityManger,
}
}
func (a *archiver) Archive(ctx context.Context, request *Request) (res *Response, err error) {
)
var defaultProvider TaskGeneratorProvider = new(taskGeneratorProviderImpl)
populateTaskGeneratorProvider(defaultProvider)
}
func populateTaskGeneratorProvider(provider TaskGeneratorProvider) {
task_generator_provider.go
_taskGeneratorProvider.Store(&provider)
}
return *_taskGeneratorProvider.Load()
}
func (p *taskGeneratorProviderImpl) NewTaskGenerator(
)
func NewGeneratorTaskHandler(opts GeneratorTaskHandlerOptions) *GeneratorTaskHandler {
generator_tasks.go
return &GeneratorTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
baseLogger: opts.BaseLogger,
SpecProcessor: opts.SpecProcessor,
specBuilder: opts.SpecBuilder,
}
}
func (g *GeneratorTaskHandler) Execute(
func NewSchedulerMigrateToWorkflowTaskHandler(
opts SchedulerMigrateToWorkflowTaskHandlerOptions,
return &SchedulerMigrateToWorkflowTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
baseLogger: opts.BaseLogger,
historyClient: opts.HistoryClient,
saMapperProvider: opts.SaMapperProvider,
}
}
func (h *SchedulerMigrateToWorkflowTaskHandler) Validate(
}
return Config{
maxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
defaultWorkflowRetrySettings: dynamicconfig.DefaultWorkflowRetryPolicy.Get(dc),
maxLinksPerRequest: dynamicconfig.FrontendMaxLinksPerRequest.Get(dc),
linkMaxSize: dynamicconfig.FrontendLinkMaxSize.Get(dc),
enableSignalWithStartFromWorkflow: dynamicconfig.EnableSignalWithStartFromWorkflow.Get(dc),
}
}
// newVisibilitySearchAttributesMapper returns a mapper with all maps initialized.
return &VisibilitySearchAttributesMapper{
aliasToField: make(map[string]string),
fieldToAlias: make(map[string]string),
saTypeMap: make(map[string]enumspb.IndexedValueType),
systemAliasToField: make(map[string]string),
overriddenSystemFields: make(map[string]enumspb.IndexedValueType),
}
}
// Alias returns the alias for a given field.
connections connectionPool[C],
historyServiceResolver membership.ServiceResolver,
return &BasicRedirector[C]{
connections: connections,
historyServiceResolver: historyServiceResolver,
}
}
r.connections.Close()
}
func (r *BasicRedirector[C]) clientForShardID(shardID int32) (C, error) {
request *matchingservice.ListNexusEndpointsRequest,
opts ...grpc.CallOption,
var resp *matchingservice.ListNexusEndpointsResponse
op := func(ctx context.Context) error {
var err error
resp, err = c.client.ListNexusEndpoints(ctx, request, opts...)
return err
}
err := backoff.ThrottleRetryContext(ctx, op, c.policy, c.isRetryable)
return resp, err
}
// GetMethodMetadata gets metadata for a given API method in one of the services exported by
// frontend (WorkflowService, OperatorService, AdminService).
switch {
case strings.HasPrefix(fullApiName, WorkflowServicePrefix):
return workflowServiceMetadata[MethodName(fullApiName)]
case strings.HasPrefix(fullApiName, OperatorServicePrefix):
return operatorServiceMetadata[MethodName(fullApiName)]
// MethodName returns just the method name from a fully qualified name.
index := strings.LastIndex(fullApiName, "/")
if index > -1 {
}
return fullApiName
}
// NewHostInfoFromAddress creates a new HostInfo instance from a socket address.
return hostAddress(address)
}
// hostAddress is a HostInfo implementation that uses a string as the address and identity.
// GetAddress returns the value of the hostAddress.
return string(a)
}
// Identity returns the value of the hostAddress.
return string(a)
}
)
// WithTags creates a new MetricProvder with provided []Tag
// Gauge obtains a gauge for the given name.
return NoopGaugeMetricFunc
}
// Timer obtains a timer for the given name.
return NoopTimerMetricFunc
}
// Histogram obtains a histogram for the given name.
var NoopCounterMetricFunc = CounterFunc(func(i int64, t ...Tag) {})
var NoopHistogramMetricFunc = HistogramFunc(func(i int64, t ...Tag) {})
)
out := make([]string, len(fields))
for i, field := range fields {
out[i] = prefix + field
}
return out
}
return strings.Join(appendPrefix(":", fields), ", ")
}
store TaskStore,
serializer serialization.Serializer,
return &taskManagerImpl{
taskStore: store,
serializer: serializer,
}
}
m.taskStore.Close()
}
func (m *taskManagerImpl) GetName() string {
func NewRoutingRateLimiter(
apiToRateLimiter map[string]RequestRateLimiter,
return &RoutingRateLimiterImpl{
apiToRateLimiter: apiToRateLimiter,
}
}
// Allow attempts to allow a request to go through. The method returns
now time.Time,
request Request,
rateLimiter, ok := r.apiToRateLimiter[request.API]
if !ok {
return true
}
}
)
if value := c.Context.Value(key); value != nil {
return value
}
}
// CopyContextValues copies values in source Context to destination Context.
return &valueCopyCtx{
Context: dst,
valueCtx: src,
}
}
// ResetContextTimeout creates new context with specified timeout and copies values from source Context.
namespaceRegistry namespace.Registry,
policy config.DCRedirectionPolicy,
switch policy.Policy {
case DCRedirectionPolicyDefault:
// default policy, noop
return NewNoopRedirectionPolicy(clusterMetadata.GetCurrentClusterName())
return NewNoopRedirectionPolicy(clusterMetadata.GetCurrentClusterName())
case DCRedirectionPolicySelectedAPIsForwarding:
currentClusterName := clusterMetadata.GetCurrentClusterName()
// NewNoopRedirectionPolicy is DC redirection policy which does nothing
func NewNoopRedirectionPolicy(currentClusterName string) *NoopRedirectionPolicy {
dc_redirection_policy.go
return &NoopRedirectionPolicy{
currentClusterName: currentClusterName,
}
}
// WithNamespaceIDRedirect redirect the API call based on namespace ID
type operationMachineDefinition struct{}
return OperationMachineType
}
func (operationMachineDefinition) Deserialize(d []byte) (any, error) {
}
return CancelationMachineType
}
// CompareState compares the progress of two Cancelation state machines to determine whether to sync machine state while
)
if err := r.RegisterMachine(operationMachineDefinition{}); err != nil {
return err
}
}
metricsHandler metrics.Handler,
logger log.Logger,
return &EventsReapplierImpl{
stateMachineRegistry: stateMachineRegistry,
chasmWorkflowRegistry: chasmWorkflowRegistry,
metricsHandler: metricsHandler,
logger: logger,
}
}
func (r *EventsReapplierImpl) ReapplyEvents(
standbyExecutor Executor,
logger log.Logger,
return &activeStandbyExecutor{
currentClusterName: currentClusterName,
registry: registry,
activeExecutor: activeExecutor,
standbyExecutor: standbyExecutor,
logger: logger,
}
}
func (e *activeStandbyExecutor) Execute(
r namespace.Registry,
cr *chasm.Registry,
return &DLQWriter{
dlqWriter: w,
metricsHandler: h,
logger: l,
namespaceRegistry: r,
chasmRegistry: cr,
}
}
// WriteTaskToDLQ writes a task to the DLQ, creating the underlying queue if it doesn't already exist.
logger log.Logger,
handler metrics.Handler,
maxSize := config.ReplicationProgressCacheMaxSize()
opts := &cache.Options{
TTL: config.ReplicationProgressCacheTTL(),
}
return &progressCacheImpl{
cache: cache.NewWithMetrics(maxSize, opts, handler.WithTags(metrics.CacheTypeTag(metrics.ReplicationProgressCacheTypeTagValue))),
}
}
func (c *progressCacheImpl) Get(
eventBlobCache persistence.XDCCache,
logger log.Logger,
return &SyncStateRetrieverImpl{
shardContext: shardContext,
workflowCache: workflowCache,
workflowConsistencyChecker: workflowConsistencyChecker,
eventBlobCache: eventBlobCache,
logger: logger,
}
}
func (s *SyncStateRetrieverImpl) GetSyncWorkflowStateArtifact(
}
return c.id
}
return c.name
}
return c.cType
}
func (c Category) MarshalText() (text []byte, err error) {
// NewCommandHandlerRegistry creates a new [CommandHandlerRegistry].
return &CommandHandlerRegistry{
handlers: make(map[enumspb.CommandType]CommandHandler),
}
}
// Register registers a [CommandHandler] for a given command type.
// Returns an [ErrDuplicateRegistration] if a handler for the given command is already registered.
// All registration is expected to happen in a single thread on process initialization.
func (r *CommandHandlerRegistry) Register(t enumspb.CommandType, handler CommandHandler) error {
command_handler.go
if existing, ok := r.handlers[t]; ok {
return fmt.Errorf("%w: command handler for %v: %v", ErrDuplicateRegistration, t, existing)
}
return nil
}
type applyFunc func(*TestServer)
// WithT directs all worker and client logs to the test logger.
// If this option is specified, then server will automatically be stopped when the
// test completes.
return applyFunc(func(server *TestServer) {
server.t = t
})
}
// WithBaseServerOptions enables configuring additional server options not directly exposed via temporaltest.
return applyFunc(func(server *TestServer) {
server.serverOptions = append(server.serverOptions, options...)
})
}
newLibrary,
),
return registry.Register(l)
}),
)
fx.Provide(resource.SearchAttributeValidatorProvider),
fx.Provide(newComponentOnlyLibrary),
// Frontend needs to register the component in order to serialize ComponentRefs, but doesn't
// need task handlers.
return registry.Register(l)
}),
)
headerMaxSize dynamicconfig.IntPropertyFnWithNamespaceFilter,
endpointRules dynamicconfig.TypedPropertyFnWithNamespaceFilter[AddressMatchRules],
return &validator{
maxCallbacksPerExecution: maxCallbacksPerExecution,
urlMaxLength: urlMaxLength,
headerMaxSize: headerMaxSize,
endpointRules: endpointRules,
}
}
// Validate validates completion callbacks: count, URL length, endpoint allowlist, header size, and normalizes header
)
func NewBackfillerTaskHandler(opts BackfillerTaskHandlerOptions) *BackfillerTaskHandler {
backfiller_tasks.go
return &BackfillerTaskHandler{
config: opts.Config,
metricsHandler: opts.MetricsHandler,
baseLogger: opts.BaseLogger,
specProcessor: opts.SpecProcessor,
}
}
// BackfillerTask invalidation reasons. Limited cardinality for ReasonTag.
logger log.Logger,
specBuilder *legacyscheduler.SpecBuilder,
return &SpecProcessorImpl{
config: config,
metricsHandler: metricsHandler,
logger: logger,
specBuilder: specBuilder,
}
}
func (s *SpecProcessorImpl) ProcessTimeRange(
// history service. Only include this in services that provide
// historyservice.HistoryServiceServer (the history service).
var HistoryHandlerModule = fx.Invoke(func(library *library, historyHandler historyservice.HistoryServiceServer) {
fx.go
library.workflowServiceNexusHandler.setHistoryHandler(historyHandler)
})
request *matchingservice.ListNexusEndpointsRequest,
opts ...grpc.CallOption,
p, err := tqid.NormalPartitionFromRpcName("not-applicable", "not-applicable", enumspb.TASK_QUEUE_TYPE_UNSPECIFIED)
if err != nil {
return nil, err
}
if err != nil {
}
ctx, cancel := c.createLongPollContext(ctx)
defer cancel()
pollPolicy backoff.RetryPolicy,
isRetryable backoff.IsRetryable,
return &retryableClient{
client: client,
policy: policy,
pollPolicy: pollPolicy,
isRetryable: isRetryable,
}
}
func (c *retryableClient) Route(p tqid.Partition) (string, error) {
var _ ClaimMapperWithAuthInfoRequired = (*noopClaimMapper)(nil)
return &noopClaimMapper{}
}
func (*noopClaimMapper) GetClaims(_ *AuthInfo) (*Claims, error) {
}
func GetClaimMapperFromConfig(config *config.Authorization, logger log.Logger) (ClaimMapper, error) {
claim_mapper.go
switch strings.ToLower(config.ClaimMapper) {
return NewNoopClaimMapper(), nil
case "default":
return NewDefaultJWTClaimMapper(NewDefaultTokenKeyProvider(config, logger), config, logger), nil
)
return &hostInfoProvider{
hostInfo: hostInfo,
}
}
return hip.hostInfo
}
queue QueueV2,
serializer serialization.Serializer,
return &HistoryTaskQueueManagerImpl{
queue: queue,
serializer: serializer,
}
}
func (m *HistoryTaskQueueManagerImpl) EnqueueTask(
}
}
// combineUnique combines the given strings into a single string by hashing the length of each string and the string
)
items := make([]string, len(fields))
for i, field := range fields {
// This line is to ensure that no update occurs (for any column) if the version is behind the saved version.
items[i] = fmt.Sprintf("%v = IF(%v < VALUES(%v), VALUES(%v), %v)",
field, sqlplugin.VersionColumnName, sqlplugin.VersionColumnName, field, field)
}
return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(items, ", "))
}
// Exec executes a sql statement
_, err := mdb.db.Exec(stmt, args...)
return err
}
// ListTables returns a list of tables in this database
// CreateDatabase creates a database if it doesn't exist
// SQLite does not need to create database
return nil
}
// DropDatabase drops a database
searchAttributesProvider Provider,
fallbackIndexName string,
return &mapperProviderImpl{
customMapper: customMapper,
namespaceRegistry: namespaceRegistry,
searchAttributesProvider: searchAttributesProvider,
fallbackIndexName: fallbackIndexName,
}
}
func (m *mapperProviderImpl) GetMapper(nsName namespace.Name) (Mapper, error) {
// So, if you want 300 seconds of history on an event that records 1k counts/sec, 3 10-second windows
// is fine.
if cfg.WindowCount <= 0 {
return nil, errors.New("windowCount must be non-negative")
}
return nil, errors.New("probable misconfiguration detected: windowSize is too small, consider increasing it to at least 50ms")
}
windows: make([]timedWindow, cfg.WindowCount),
cfg: cfg,
// mu and head both empty
}, nil
}
)
s, ok := PriorityName[p]
if ok {
return s
}
return strconv.Itoa(int(p))
}
func getPriority(
class, subClass Priority,
return class | subClass
}
metricsHandler metrics.Handler,
logger log.Logger,
return &Dispatcher{
matchingClient: matchingClient,
config: config,
metricsHandler: metricsHandler,
logger: logger,
}
}
func (d *Dispatcher) Execute(
httpClientCache *cluster.FrontendHTTPClientCache,
logger log.Logger,
localClient, err := rpcFactory.CreateLocalFrontendHTTPClient()
if err != nil {
return nil, fmt.Errorf("cannot create local frontend HTTP client: %w", err)
}
callbackTokenGenerator := commonnexus.NewCallbackTokenGenerator()
m := collection.NewOnceMap(func(queuescommon.NamespaceIDAndDestination) HTTPCaller {
return func(r *http.Request) (*http.Response, error) {
return routeRequest(r,
endpointRegistry commonnexus.EndpointRegistry,
config *nexusoperations.Config,
h := commandHandler{
config: config,
endpointRegistry: endpointRegistry,
nexusProcessor: chasmRegistry.NexusEndpointProcessor,
}
if err := reg.Register(enumspb.COMMAND_TYPE_SCHEDULE_NEXUS_OPERATION, h.HandleScheduleCommand); err != nil {
return err
}
return reg.Register(enumspb.COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION, h.HandleCancelCommand)
commands.go
}
serializer serialization.Serializer,
logger log.Logger,
return &eventImporterImpl{
historyFetcher: historyFetcher,
engineProvider: engineProvider,
serializer: serializer,
logger: logger,
}
}
//nolint:revive // cognitive complexity 30 (> max enabled 25)
shardController shard.Controller,
logger log.Logger,
return &historyEventsHandlerImpl{
clusterMetadata: clusterMetadata,
eventImporter: eventImporter,
shardController: shardController,
logger: logger,
}
}
func (h *historyEventsHandlerImpl) HandleHistoryEvents(
serializer serialization.Serializer,
logger log.Logger,
return &HistoryPaginatedFetcherImpl{
NamespaceRegistry: namespaceRegistry,
ClientBean: clientBean,
Serializer: serializer,
Logger: logger,
}
}
func (n *HistoryPaginatedFetcherImpl) GetSingleWorkflowHistoryPaginatedIteratorInclusive(
workflowCache wcache.Cache,
logger log.Logger,
return &workflowRebuilderImpl{
shard: shard,
workflowConsistencyChecker: api.NewWorkflowConsistencyChecker(shard, workflowCache),
transaction: workflow.NewTransaction(shard),
logger: logger,
}
}
func (r *workflowRebuilderImpl) rebuild(
reachabilityCacheOpenWFExecutionTTL,
reachabilityCacheClosedWFExecutionTTL time.Duration,
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 based on the query-string key.
sdkClientFactory sdk.ClientFactory,
numWorkflows int,
return &clientImpl{
metricsHandler: metricsHandler,
logger: logger,
sdkClientFactory: sdkClientFactory,
numWorkflows: numWorkflows,
}
}
func (c *clientImpl) SendParentClosePolicyRequest(ctx context.Context, request Request) error {
maxLinksPerComponent dynamicconfig.IntPropertyFnWithNamespaceFilter,
linkMaxSize dynamicconfig.IntPropertyFnWithNamespaceFilter,
return &linkValidator{
maxLinksPerRequest: maxLinksPerRequest,
maxLinksPerComponent: maxLinksPerComponent,
linkMaxSize: linkMaxSize,
}
}
// ValidateRequest checks count, per-link size, and variant shape for the links
saMapperProvider searchattribute.MapperProvider,
saValidator *searchattribute.Validator,
return &RequestValidator{
config: config,
saMapperProvider: saMapperProvider,
saValidator: saValidator,
}
}
func (v *RequestValidator) ValidateWorkflowID(
// The apply function is called after verifying the transition is possible but before setting the destination state,
// so it can inspect the current (source) state.
func NewTransition[S comparable, SM StateMachine[S], E any](src []S, dst S, apply func(SM, MutableContext, E) error) Transition[S, SM, E] {
statemachine.go
return Transition[S, SM, E]{
Sources: src,
Destination: dst,
apply: apply,
}
}
// Possible returns a boolean indicating whether the transition is possible for the current state.
largeTimeout time.Duration,
client adminservice.AdminServiceClient,
return &clientImpl{
timeout: timeout,
largeTimeout: largeTimeout,
client: client,
}
}
func (c *clientImpl) createContext(parent context.Context) (context.Context, context.CancelFunc) {
metricsHandler metrics.Handler,
throttledLogger log.Logger,
return &metricClient{
client: client,
metricsHandler: metricsHandler,
throttledLogger: throttledLogger,
}
}
func (c *metricClient) startMetricsRecording(
// NewRetryableClient creates a new instance of adminservice.AdminServiceClient with retry policy
func NewRetryableClient(client adminservice.AdminServiceClient, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) adminservice.AdminServiceClient {
retryable_client.go
return &retryableClient{
client: client,
policy: policy,
isRetryable: isRetryable,
}
}
func (c *retryableClient) StreamWorkflowReplicationMessages(
longPollTimeout time.Duration,
client workflowservice.WorkflowServiceClient,
return &clientImpl{
timeout: timeout,
longPollTimeout: longPollTimeout,
client: client,
}
}
func (c *clientImpl) createContext(parent context.Context) (context.Context, context.CancelFunc) {
metricsHandler metrics.Handler,
throttledLogger log.Logger,
return &metricClient{
client: client,
metricsHandler: metricsHandler,
throttledLogger: throttledLogger,
}
}
func (c *metricClient) startMetricsRecording(
// NewRetryableClient creates a new instance of workflowservice.WorkflowServiceClient with retry policy
func NewRetryableClient(client workflowservice.WorkflowServiceClient, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) workflowservice.WorkflowServiceClient {
retryable_client.go
return &retryableClient{
client: client,
policy: policy,
isRetryable: isRetryable,
}
}
// NewRetryableClient creates a new instance of historyservice.HistoryServiceClient with retry policy
func NewRetryableClient(client historyservice.HistoryServiceClient, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) historyservice.HistoryServiceClient {
retryable_client.go
return &retryableClient{
client: client,
policy: policy,
isRetryable: isRetryable,
}
}
func (c *retryableClient) StreamWorkflowReplicationMessages(
request *matchingservice.ListNexusEndpointsRequest,
opts ...grpc.CallOption,
metricsHandler, startTime := c.startMetricsRecording(ctx, "MatchingClientListNexusEndpoints")
defer func() {
c.finishMetricsRecording(metricsHandler, startTime, retError)
}()
}
// WithMetadataContext adds a metadata context to the given context.
metadataCtx := &metadataContext{
Metadata: make(map[string]any),
MarkedActivityIDs: make(map[string]struct{}),
}
return context.WithValue(ctx, metadataCtxKey, metadataCtx)
}
// ContextHasMetadata returns true if the context has metadata support.
workflowID string,
runID string,
return WorkflowKey{
NamespaceID: namespaceID,
WorkflowID: workflowID,
RunID: runID,
}
}
func (k *WorkflowKey) GetNamespaceID() string {
namespaceReplicationQueue persistence.NamespaceReplicationQueue,
logger log.Logger,
return &dlqMessageHandlerImpl{
replicationHandler: replicationHandler,
namespaceReplicationQueue: namespaceReplicationQueue,
logger: logger,
}
}
// Read reads namespace replication DLQ messages
r resolver.ServiceResolver,
logger log.Logger,
return checkMainKeyspace(cfg, r, logger)
}
func checkMainKeyspace(
r resolver.ServiceResolver,
logger log.Logger,
ds, ok := cfg.DataStores[cfg.DefaultStore]
if ok && ds.Cassandra != nil {
return CheckCompatibleVersion(*ds.Cassandra, r, cassandraschema.Version, logger)
}
}
// NewDataBlob returns a new DataBlob.
// TODO: return an UnknowEncodingType error with the actual type string when encodingTypeStr is invalid
encodingType, err := enumspb.EncodingTypeFromString(encodingTypeStr)
if err != nil {
// encodingTypeStr not valid, an error will be returned on deserialization
encodingType = enumspb.ENCODING_TYPE_UNSPECIFIED
}
Data: data,
EncodingType: encodingType,
}
}
logger log.Logger,
serializer serialization.Serializer,
return &sqlTaskManagerV1{
SqlStore: NewSQLStore(db, logger, serializer),
userDataStore: uds,
taskQueueStore: tqs,
}, nil
}
func (m *sqlTaskManagerV1) CreateTasks(
logger log.Logger,
serializer serialization.Serializer,
return &sqlTaskManagerV2{
SqlStore: NewSQLStore(db, logger, serializer),
userDataStore: uds,
taskQueueStore: tqs,
}, nil
}
func (m *sqlTaskManagerV2) CreateTasks(
logger log.Logger,
logAllReqErrors dynamicconfig.BoolPropertyFnWithNamespaceFilter,
return &RequestErrorHandler{
logger: logger,
workflowTags: logtags.NewWorkflowTags(tasktoken.NewSerializer(), logger),
logAllReqErrors: logAllReqErrors,
}
}
// HandleError handles error recording and logging
// NewRoutingInfoCache wraps the provided cache with a typed API and metrics.
func NewRoutingInfoCache(c cache.Cache, metricsHandler metrics.Handler) RoutingInfoCache {
routing_info_cache.go
h := metricsHandler.WithTags(metrics.CacheTypeTag(metrics.RoutingInfoCacheTypeTagValue))
return &RoutingInfoCacheImpl{
Cache: c,
metricsHandler: h,
}
}
func (c *RoutingInfoCacheImpl) Get(
// NewVersionMembershipAndReactivationStatusCache wraps the provided cache with a typed API and metrics.
func NewVersionMembershipAndReactivationStatusCache(c cache.Cache, metricsHandler metrics.Handler) VersionMembershipAndReactivationStatusCache {
version_membership_cache.go
h := metricsHandler.WithTags(metrics.CacheTypeTag(metrics.VersionMembershipCacheTypeTagValue))
return &VersionMembershipAndReactivationStatusCacheImpl{
Cache: c,
metricsHandler: h,
}
}
func (c *VersionMembershipAndReactivationStatusCacheImpl) Get(
// NewTransition creates a new [Transition] from the given source states to a destination state for a given event.
// The apply function is called after verifying the transition is possible and setting the destination state.
func NewTransition[S comparable, SM StateMachine[S], E any](src []S, dst S, apply func(SM, E) (TransitionOutput, error)) Transition[S, SM, E] {
sm.go
return Transition[S, SM, E]{
Sources: src,
Destination: dst,
apply: apply,
}
}
// Possible returns a boolean indicating whether the transition is possible for the current state.
)
return &executableTracker{
pendingExecutables: make(map[tasks.Key]Executable),
grouper: grouper,
pendingPerKey: make(map[any]int, 0),
}
}
func (t *executableTracker) split(
}
func NewAdminServiceClient(cc grpc.ClientConnInterface) AdminServiceClient {
service_grpc.pb.go
return &adminServiceClient{cc}
}
func (c *adminServiceClient) RebuildMutableState(ctx context.Context, in *RebuildMutableStateRequest, opts ...grpc.CallOption) (*RebuildMutableStateResponse, error) {
}
func RegisterAdminServiceServer(s grpc.ServiceRegistrar, srv AdminServiceServer) {
service_grpc.pb.go
s.RegisterService(&AdminService_ServiceDesc, srv)
}
func _AdminService_RebuildMutableState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
}
return &handler{
config: config,
logger: logger,
}
}
// StartNexusOperation creates a new standalone Nexus operation execution via CHASM.
}
func newHandler(logger log.Logger, specBuilder *legacyscheduler.SpecBuilder) *handler {
handler.go
return &handler{
logger: logger,
specBuilder: specBuilder,
}
}
func (h *handler) CreateSchedule(ctx context.Context, req *schedulerpb.CreateScheduleRequest) (resp *schedulerpb.CreateScheduleResponse, err error) {
// NewJSONPBEncoder creates a new JSONPBEncoder.
return JSONPBEncoder{}
}
// NewJSONPBIndentEncoder creates a new JSONPBEncoder with indent.
// Encode protobuf struct to bytes.
return e.marshaler.Marshal(pb)
}
// Decode bytes to protobuf struct.
//
// The hash function to use for sharding
func NewShardedConcurrentTxMap(initialCap int, hashfn HashFunc) ConcurrentTxMap {
concurrent_tx_map.go
cmap := new(ShardedConcurrentTxMap)
cmap.hashfn = hashfn
cmap.initialCap = max(nShards, initialCap/nShards)
return cmap
}
// Get returns the value corresponding to the key, if it exist
}
return SyncMap[K, V]{
RWMutex: &sync.RWMutex{},
contents: make(map[K]V),
}
}
func (m *SyncMap[K, V]) Get(key K) (value V, ok bool) {
var Module = fx.Options(
col := NewCollection(client, logger)
lc.Append(fx.StartStopHook(col.Start, col.Stop))
return col
}),
fx.Provide(fx.Annotate(
fx.ResultTags(`group:"deadlockDetectorRoots"`),
)),
type WithDescription string
m.description = string(h)
}
// WithUnit sets the unit of a metric. See NewBytesHistogramDef for an example.
type WithUnit MetricUnit
m.unit = MetricUnit(h)
}
namespaceReplicationQueue persistence.NamespaceReplicationQueue,
logger log.Logger,
return &replicator{
namespaceReplicationQueue: namespaceReplicationQueue,
logger: logger,
}
}
// HandleTransmissionTask handle transmission of the namespace replication task
// GetOrUseDefaultActiveCluster return the current cluster name or use the input if valid
func GetOrUseDefaultActiveCluster(currentClusterName string, activeClusterName string) string {
cluster_metadata.go
if len(activeClusterName) == 0 {
return currentClusterName
}
}
// GetOrUseDefaultClusters return the current cluster or use the input if valid
func GetOrUseDefaultClusters(currentClusterName string, clusters []string) []string {
cluster_metadata.go
if len(clusters) == 0 {
return []string{currentClusterName}
}
}
// UnixMilliseconds returns t as a Unix time, the number of milliseconds elapsed since January 1, 1970 UTC.
// It should be used for all CQL timestamp.
// Handling zero time separately because UnixNano is undefined for zero times.
if t.IsZero() {
return 0
}
if unixNano < 0 {
// Time is before January 1, 1970 UTC
return 0
}
}
)
RegisterTranslator(fixedTranslatorName, NewFixedAddressTranslatorPlugin())
}
type FixedAddressTranslatorPlugin struct {
}
return &FixedAddressTranslatorPlugin{}
}
// GetTranslator What gocql driver does is that it will connect to the first node in the list in configuration
logger log.Logger,
serializer serialization.Serializer,
return &sqlExecutionStore{
SqlStore: NewSQLStore(db, logger, serializer),
HistoryBranchUtil: p.NewHistoryBranchUtil(serializer),
}, nil
}
// txExecuteShardLocked executes f under transaction and with read lock on shard row
}
func (mdb *db) GetNexusEndpointsTableVersion(ctx context.Context) (int64, error) {
nexus_endpoints.go
var version int64
err := mdb.conn.GetContext(ctx, &version, getEndpointsTableVersionQry)
if errors.Is(err, sql.ErrNoRows) {
}
return version, err
}
storeNames []string,
allowList dynamicconfig.BoolPropertyFnWithNamespaceFilter,
if len(storeNames) == 0 {
return dynamicconfig.GetBoolPropertyFnFilteredByNamespace(false)
}
case mysql.PluginName, postgresql.PluginName, postgresql.PluginNamePGX, sqlite.PluginName:
defs.go
// Advanced visibility with SQL DB don't support list of values
return dynamicconfig.GetBoolPropertyFnFilteredByNamespace(false)
default:
// Otherwise (ES), check dynamic config
lc fx.Lifecycle,
pprof *PProfInitializerImpl,
lc.Append(
fx.Hook{
OnStart: func(context.Context) error {
return pprof.Start()
},
// todo: refactor pprof to gracefully shutdown http server
// OnStop: func(ctx context.Context) error {
)
return &UniversalImpl[T]{}
}
func (a *UniversalImpl[T]) Test(t T) bool {
}
return EmptyPredicateProtoSize
}
// limit is used if and only if it is configured to a value greater than zero and the number of instances that
// the memberCounter reports is greater than zero. Otherwise, the per-instance limit is used.
func getQuota(memberCounter MemberCounter, instanceLimit, clusterLimit int) float64 {
cluster_aware_quota_calculator.go
if clusterLimit > 0 && memberCounter != nil {
if clusterSize := memberCounter.AvailableMemberCount(); clusterSize > 0 {
return float64(clusterLimit) / float64(clusterSize)
}
}
return getQuota(l.MemberCounter, l.PerInstanceQuota(), l.GlobalQuota())
}
func (l ClusterAwareNamespaceQuotaCalculator) GetQuota(namespace string) float64 {
// OK returns whether the limiter can provide the requested number of tokens
return true
}
// Cancel indicates that the reservation holder will not perform the reserved action
// DelayFrom returns the duration for which the reservation holder must wait
// before taking the reserved action. Zero duration means act immediately.
return time.Duration(0) // no delay
}
refreshInterval time.Duration,
logger log.Logger,
providerMap := &localStorePerHostCertProviderMap{}
if overrides == nil {
return providerMap
}
providerMap.certProviderCache = make(map[string]CertProvider, len(overrides))
serializer *tasktoken.Serializer,
logger log.Logger,
return &WorkflowTags{
serializer: serializer,
logger: logger,
}
}
func (wt *WorkflowTags) Extract(req any, fullMethod string) []tag.Tag {
// from a WorkflowService request using field paths declared in the
// temporal.api.protometa.v1.request_header proto annotation.
func workflowServiceRequestRoutingKey(req any) namespace.RoutingKey {
routing_key_extractor_gen.go
switch r := req.(type) {
case *workflowservice.CreateScheduleRequest:
return namespace.RoutingKey{ID: r.GetScheduleId()}
case *workflowservice.StartBatchOperationRequest:
return namespace.RoutingKey{ID: r.GetJobId()}
return namespace.RoutingKey{ID: r.GetWorkflowId()}
case *workflowservice.StopBatchOperationRequest:
return namespace.RoutingKey{ID: r.GetJobId()}
case *workflowservice.UpdateWorkflowExecutionRequest:
return namespace.RoutingKey{ID: r.GetWorkflowExecution().GetWorkflowId()}
return namespace.RoutingKey{}
}
}
// as an instrumentation-scope attribute (OTEL semconv service.name) so every emitted event carries
// it, replacing per-event common-tag plumbing.
return lp.Logger(
instrumentationName,
log.WithInstrumentationAttributes(attribute.String("service.name", serviceName)),
)
}
// NoopLogger returns a logger that discards all events. Safe default for tests and for
type stateMachineDefinition struct{}
return StateMachineType
}
func (stateMachineDefinition) Deserialize(d []byte) (any, error) {
}
return r.RegisterMachine(stateMachineDefinition{})
}
// EventScheduled is triggered when the callback is meant to be scheduled for the first time - when its Trigger
shardContext historyi.ShardContext,
workflowCache wcache.Cache,
return &WorkflowConsistencyCheckerImpl{
shardContext: shardContext,
workflowCache: workflowCache,
}
}
func (c *WorkflowConsistencyCheckerImpl) GetWorkflowCache() wcache.Cache {
func ConvertWeightsToDynamicConfigValue(
weights map[tasks.Priority]int,
weightsForDC := make(map[string]any)
for priority, weight := range weights {
weightsForDC[priority.String()] = weight
}
return weightsForDC
}
currentShardId int32,
clusterMetadata cluster.Metadata,
return &pollerManagerImpl{
currentShardId: currentShardId,
clusterMetadata: clusterMetadata,
}
}
func (p pollerManagerImpl) getSourceClusterShardIDs(sourceClusterName string) ([]int32, error) {
config *configs.Config,
visibilityManager manager.VisibilityManager,
return &relocatableAttributesFetcher{
visibilityManager: visibilityManager,
disableFetchFromVisibility: config.DisableFetchRelocatableAttributesFromVisibility,
}
}
// RelocatableAttributes contains workflow attributes that can be moved from the mutable state to the persistence
}
return StateMachineType
}
return reg.RegisterMachine(stateMachineDefinition{})
}
func NewTaskRefresher(
shard historyi.ShardContext,
return &TaskRefresherImpl{
shard: shard,
taskGeneratorProvider: GetTaskGeneratorProvider(),
}
}
func (r *TaskRefresherImpl) Refresh(
func NewTransaction(
shardContext historyi.ShardContext,
return &TransactionImpl{
shard: shardContext,
logger: shardContext.GetLogger(),
}
}
func (t *TransactionImpl) CreateWorkflowExecution(
// serializeConflictToken serializes a conflict token as a byte slice.
token := make([]byte, 8)
binary.LittleEndian.PutUint64(token, uint64(conflictToken))
return token
}
// newTaggedLogger returns a logger tagged with the Scheduler's attributes.
}
switch strings.ToLower(config.Authorizer) {
return NewNoopAuthorizer(), nil
case "default":
return NewDefaultAuthorizer(), nil
func NewPriorityQueue[T any](
compareLess func(this T, other T) bool,
return &priorityQueueImpl[T]{
compareLess: compareLess,
}
}
// NewPriorityQueueWithItems creats a new priority queue
// register adds a metric definition to the list of pending metric definitions. This method is thread-safe.
c.Lock()
defer c.Unlock()
c.definitions = append(c.definitions, d)
}
// buildCatalog builds a catalog from the list of pending metric definitions. It is safe to call this method multiple
func NewValidator(
clusterMetadata cluster.Metadata,
return &Validator{
clusterMetadata: clusterMetadata,
}
}
func (d *Validator) ValidateNamespaceConfig(config *persistencespb.NamespaceConfig) error {
}
func NewLoggedHTTPClientTraceProvider(dc *dynamicconfig.Collection) HTTPClientTraceProvider {
trace.go
return &LoggedHTTPClientTraceProvider{
Config: HTTPTraceConfig.Get(dc),
}
}
func (p *LoggedHTTPClientTraceProvider) NewTrace(attempt int32, logger log.Logger) *httptrace.ClientTrace {
)
func NewHistoryBranchUtil(serializer serialization.Serializer) *HistoryBranchUtilImpl {
history_branch_util.go
return &HistoryBranchUtilImpl{
serializer: serializer,
}
}
func (u *HistoryBranchUtilImpl) NewHistoryBranch(
logger log.Logger,
serializer serialization.Serializer,
return &queueV2{
SqlStore: NewSQLStore(db, logger, serializer),
}
}
func (q *queueV2) EnqueueMessage(
var _ sqlplugin.Plugin = (*plugin)(nil)
sql.RegisterPlugin(PluginName, &plugin{
queryConverter: &queryConverter{},
})
}
func (p *plugin) GetVisibilityQueryConverter() sqlplugin.VisibilityQueryConverter {
}
return &Overrides{
minTypeScriptEagerActivitySupportedVersion: semver.MustParse("1.4.4"),
}
}
func (o *Overrides) shouldForceDisableEagerDispatch(sdkName, sdkVersion string) bool {
// NewChasmNotifier creates a new instance of ChasmNotifier.
return &ChasmNotifier{
executions: make(map[chasm.ExecutionKey]*subscriptionTracker),
}
}
// Subscribe returns a channel that will be closed when there is a notification relating to the
func NewCircuitBreakerPool[K comparable](
constructor func(key K) circuitbreaker.TwoStepCircuitBreaker,
return &CircuitBreakerPool[K]{
m: collection.NewOnceMap(constructor),
}
}
func NewExecutableTaskConverter(
processToolBox ProcessToolBox,
return &executableTaskConverterImpl{
processToolBox: processToolBox,
}
}
func (e *executableTaskConverterImpl) Convert(
// NewTaskQueueRateLimitFractionProvider wraps inner and enforces [0.0, 1.0] on every call.
func NewTaskQueueRateLimitFractionProvider(inner TaskQueueRateLimitFractionProvider) TaskQueueRateLimitFractionProvider {
rate_limit_fraction_provider.go
return TaskQueueRateLimitFractionProviderFunc(func(nsName namespace.Name, tqName string, tqType enumspb.TaskQueueType) float64 {
return max(min(inner.GetRateLimitFraction(nsName, tqName, tqType), maxRateLimitFraction), minRateLimitFraction)
})
var defaultTaskQueueRateLimitFractionProvider = NewTaskQueueRateLimitFractionProvider(&unitRateLimitFractionProvider{})
func taskQueueRateLimitFractionProviderProvider() TaskQueueRateLimitFractionProvider {
rate_limit_fraction_provider.go
return defaultTaskQueueRateLimitFractionProvider
}
}
return &Config{
RequestTimeout: RequestTimeout.Get(dc),
RetryPolicy: func() backoff.RetryPolicy {
return backoff.NewExponentialRetryPolicy(
RetryPolicyInitialInterval.Get(dc)(),
}
_, found := healthCheckAPI[fullApi]
return found
}
// With returns Logger instance that prepend every log entry with tags. If logger implements
// WithLogger it is used, otherwise every log call will be intercepted.
if wl, ok := logger.(WithLogger); ok {
}
return newWithLogger(logger, tags...)
}
)
)
func newNoopSignalAggregator() *noopSignalAggregator { return &noopSignalAggregator{} }
noop_health_signal_aggregator.go
func (a *noopSignalAggregator) Record(_ int32, _ time.Duration, _ error) {}
noop_health_signal_aggregator.go
func (a *noopSignalAggregator) AverageLatency() float64 {
}
t, err := time.Parse(time.RFC3339, "1000-01-01T00:00:00Z")
if err != nil {
return time.Unix(0, 0).UTC()
}
}
}
t, err := time.Parse(time.RFC3339, "1000-01-01T00:00:00Z")
if err != nil {
return time.Unix(0, 0).UTC()
}
}
}
return &Config{
RequestTimeout: RequestTimeout.Get(dc),
RetryPolicy: func() backoff.RetryPolicy {
return backoff.NewExponentialRetryPolicy(
RetryPolicyInitialInterval.Get(dc)(),
namespaceRegistry namespace.Registry,
config *configs.Config,
return &OutboundQueueCircuitBreakerPool{
CircuitBreakerPool: NewCircuitBreakerPool(
func(key tasks.TaskGroupNamespaceIDAndDestination) circuitbreaker.TwoStepCircuitBreaker {
// This is intentionally not failing the function in case of error. The circuit breaker is
// agnostic to Task implementation, and thus the settings function is not expected to return
}
func RegisterHistoryServiceServer(s grpc.ServiceRegistrar, srv HistoryServiceServer) {
service_grpc.pb.go
s.RegisterService(&HistoryService_ServiceDesc, srv)
}
func _HistoryService_StartWorkflowExecution_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
}
func RegisterMatchingServiceServer(s grpc.ServiceRegistrar, srv MatchingServiceServer) {
service_grpc.pb.go
s.RegisterService(&MatchingService_ServiceDesc, srv)
}
func _MatchingService_PollWorkflowTaskQueue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
// Size returns the size of the object, in bytes, once serialized
return proto.Size(val)
}
// Equal returns whether two Predicate values are equivalent by recursively
"chasm",
fx.Provide(NewRegistry),
return registry.Register(&CoreLibrary{})
}),
)
var Module = fx.Module(
"chasm.lib.tests",
return registry.Register(Library)
}),
)
ctx context.Context,
engine VisibilityManager,
return context.WithValue(ctx, visibilityManagerCtxKey, engine)
}
func visibilityManagerFromContext(
// NewNoopAuthorizer creates a no-op authorizer
return &noopAuthorizer{}
}
func (a *noopAuthorizer) Authorize(_ context.Context, _ *Claims, _ *CallTarget) (Result, error) {
var Module = fx.Options(
fx.Provide(NewDeadlockDetector),
lc.Append(fx.StartStopHook(dd.Start, dd.Stop))
}),
)
)
return Key{handle: unique.Make(strings.ToLower(s))}
}
func (k Key) String() string {
)
if v, ok := s[key]; ok {
if cvs, ok := v.([]ConstrainedValue); ok {
return cvs
lc fx.Lifecycle,
registry namespace.Registry,
lc.Append(fx.StartStopHook(registry.Start, registry.Stop))
}
// NewNoopDataMerger creates a new NoopDataMerger.
return &NoopDataMerger{}
}
// MergeData returns taskData directly without any merging.
// NewDefaultAdmitter creates the default NamespaceReplicationAdmitter.
return &DefaultAdmitter{}
}
// Admit returns true iff currentCluster appears in the task's replication
}
return &CallbackTokenGenerator{}
}
func (g *CallbackTokenGenerator) Tokenize(completion *tokenspb.NexusOperationCompletion) (string, error) {
// [Failure] instances are converted to [FailureError] to allow access to the full failure metadata and details if
// available.
return defaultFailureConverter
}
func retryBehaviorAsOptionalBool(e *nexus.HandlerError) *bool {
}
return defaultDataConverter.ToPayload(value)
}
func Decode(p *commonpb.Payload, valuePtr any) error {
// newJSONHistoryTokenSerializer creates a new instance of TaskTokenSerializer
func newJSONHistoryTokenSerializer() *jsonHistoryTokenSerializer {
json_history_token_serializer.go
return &jsonHistoryTokenSerializer{}
}
func (t *historyPagingToken) SetRangeIndexes(
// RegisterPlugin adds an auth plugin to the plugin registry
// it is only safe to use from a package init function
translators[name] = plugin
}
func LookupTranslator(name string) (TranslatorPlugin, error) {
_ time.Time,
_ Request,
return true
}
func (r *NoopRequestRateLimiterImpl) Reserve(
// SetRateLimiter sets the rate limiter to delegate to.
func (d *RequestRateLimiterDelegator) SetRateLimiter(rl RequestRateLimiter) {
request_rate_limiter_delegator.go
d.delegate.Store(monomorphicRequestRateLimiter{rl})
}
// loadDelegate returns the rate limiter that this rate limiter delegates to.
)
return &NoopResolver{}
}
func (c *NoopResolver) Resolve(service string) []string {
// NewStalePartitionCounts returns new StalePartitionCounts error.
return &StalePartitionCounts{Message: message}
}
// Error returns string message.
// NewSerializer creates a new instance of Serializer
return &Serializer{}
}
func (s *Serializer) Serialize(taskToken *tokenspb.Task) ([]byte, error) {
// NewCompletionHandler returns a CompletionHandler. Wired via fx; see Module.
func NewCompletionHandler(metricsHandler metrics.Handler, config *Config) *CompletionHandler {
completion.go
return &CompletionHandler{metricsHandler: metricsHandler, config: config}
}
// Handle resolves an async Nexus operation completion.
fx.Invoke(RegisterExecutor),
// Bridge CHASM ClientProvider to HSM ClientProvider type.
return ClientProvider(cp)
}),
)
var Module = fx.Options(
fx.Provide(NewArchiver),
return quotas.NewDefaultOutgoingRateLimiter(quotas.RateFn(config.ArchivalBackendMaxRPS))
}),
)
var Module = fx.Options(
fx.Provide(func(executionManager persistence.ExecutionManager, config *configs.Config, handler metrics.Handler, logger log.Logger) Cache {
fx.go
return NewHostLevelEventsCache(executionManager, config, handler, logger, false)
}),
)
// NewUnprocessableTaskError returns a new UnprocessableTaskError from given message.
return &UnprocessableTaskError{Message: message}
}
func (e UnprocessableTaskError) Error() string {
}
func newSimplePartitionScalerFactory(cfg scalerFactoryCfg) *simplePartitionScalerFactory {
simple_partition_scaler.go
return &simplePartitionScalerFactory{cfg: cfg}
}
func (s *simplePartitionScalerFactory) New(
// AnnotateWorkerComponentProvider converts a WorkerComponent factory function into an fx provider which will add the
// WorkerComponentTag to the result.
return fx.Provide(fx.Annotate(f, fx.ResultTags(WorkerComponentTag)))
}
// NewServer returns a new instance of server that serves one or many services.
return NewServerFx(TopLevelModule, opts...)
}
// We have to use pointer is because in golang: "recover return nil if was not called directly by a deferred function."
// And we have to set the returned error otherwise our handler will return nil as error which is incorrect
if panicObj := recover(); panicObj != nil {
err, ok := panicObj.(error)
if !ok {
)
func newNoopMovingWindowAverage() *noopMovingWindowAverage { return &noopMovingWindowAverage{} }
noop_moving_window_average.go
func (a *noopMovingWindowAverage) Record(_ int64) {}