Files
Alex ShtinandGitHub 8e8f1b0f7c Remove license header from every file (#7689)
## What changed?
<!-- Describe what has changed in this PR -->
Remove license header from every file. Because it is really hard to
follow in this PR here is the summary:
1. License header is removed from all `.go` and `.proto` files
:fireworks::fireworks:🎆.
2. `LICENSE` file in the root directory has only Temporal and Uber
copyrights.
3. 5 other `LICENSE` files added to the packages which have copyrights
different from Temporal and Uber: Datadog, Xargin, "Mat Ryer, Tyler
Bunnell and contributors".
4. `license_file` flag is removed from all code generation tools.
5. `copyright_file` flag is removed from `go:generate mockgen`
directive.
6. All copyright related targets are removed from `Makefile`.
7. Updated Temporal copyright year to 2025 everywhere.

## Why?
<!-- Tell your future self why have you made these changes -->
I double checked with legal department that it is not needed to have
license header in every file. One file per repo is enough. I put all
copyrights to the root `LICENSE` file and removed header from all other
files. Also updated tools and `Makefile`.
2025-05-01 18:50:21 -07:00

328 lines
7.0 KiB
Go

package queues
import (
"sync"
"time"
"go.temporal.io/server/common/clock"
"go.temporal.io/server/common/dynamicconfig"
"go.temporal.io/server/service/history/tasks"
)
var _ Monitor = (*monitorImpl)(nil)
const (
monitorWatermarkPrecision = time.Second
defaultAlertSilenceDuration = 10 * time.Second
alertChSize = 10
)
type (
// Monitor tracks Queue statistics and sends an Alert to the AlertCh
// if any statistics becomes abnormal
Monitor interface {
GetTotalPendingTaskCount() int
GetSlicePendingTaskCount(slice Slice) int
SetSlicePendingTaskCount(slice Slice, count int)
GetReaderWatermark(readerID int64) (tasks.Key, bool)
SetReaderWatermark(readerID int64, watermark tasks.Key)
GetTotalSliceCount() int
GetSliceCount(readerID int64) int
SetSliceCount(readerID int64, count int)
RemoveSlice(slice Slice)
RemoveReader(readerID int64)
ResolveAlert(AlertType)
SilenceAlert(AlertType)
AlertCh() <-chan *Alert
Close()
}
MonitorOptions struct {
PendingTasksCriticalCount dynamicconfig.IntPropertyFn
ReaderStuckCriticalAttempts dynamicconfig.IntPropertyFn
SliceCountCriticalThreshold dynamicconfig.IntPropertyFn
}
monitorImpl struct {
sync.Mutex
totalPendingTaskCount int
totalSliceCount int
readerStats map[int64]readerStats
sliceStats map[Slice]sliceStats
categoryType tasks.CategoryType
timeSource clock.TimeSource
options *MonitorOptions
pendingAlerts map[AlertType]struct{}
silencedAlerts map[AlertType]time.Time // silenced alertType => expiration
alertCh chan *Alert
shutdownCh chan struct{}
}
readerStats struct {
progress readerProgress
sliceCount int
}
readerProgress struct {
watermark tasks.Key
attempts int
}
sliceStats struct {
pendingTaskCount int
}
)
func newMonitor(
categoryType tasks.CategoryType,
timeSource clock.TimeSource,
options *MonitorOptions,
) *monitorImpl {
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{}),
}
}
func (m *monitorImpl) GetTotalPendingTaskCount() int {
m.Lock()
defer m.Unlock()
return m.totalPendingTaskCount
}
func (m *monitorImpl) GetSlicePendingTaskCount(slice Slice) int {
m.Lock()
defer m.Unlock()
if stats, ok := m.sliceStats[slice]; ok {
return stats.pendingTaskCount
}
return 0
}
func (m *monitorImpl) SetSlicePendingTaskCount(slice Slice, count 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,
AlertAttributesQueuePendingTaskCount: &AlertAttributesQueuePendingTaskCount{
CurrentPendingTaskCount: m.totalPendingTaskCount,
CiriticalPendingTaskCount: criticalTotalTasks,
},
})
}
}
func (m *monitorImpl) GetReaderWatermark(readerID int64) (tasks.Key, bool) {
m.Lock()
defer m.Unlock()
stats, ok := m.readerStats[readerID]
if !ok {
return tasks.MinimumKey, false
}
return stats.progress.watermark, true
}
func (m *monitorImpl) SetReaderWatermark(readerID int64, watermark tasks.Key) {
// TODO: currently only tracking default reader progress for scheduled queue
if readerID != DefaultReaderId || m.categoryType != tasks.CategoryTypeScheduled {
return
}
m.Lock()
defer m.Unlock()
watermark.FireTime = watermark.FireTime.Truncate(monitorWatermarkPrecision)
watermark.TaskID = 0
stats := m.readerStats[readerID]
if stats.progress.watermark.CompareTo(watermark) != 0 {
stats.progress = readerProgress{
watermark: watermark,
attempts: 1,
}
m.readerStats[readerID] = stats
return
}
stats.progress.attempts++
m.readerStats[readerID] = stats
criticalAttempts := m.options.ReaderStuckCriticalAttempts()
if criticalAttempts > 0 && stats.progress.attempts >= criticalAttempts {
m.sendAlertLocked(&Alert{
AlertType: AlertTypeReaderStuck,
AlertAttributesReaderStuck: &AlertAttributesReaderStuck{
ReaderID: readerID,
CurrentWatermark: stats.progress.watermark,
},
})
}
}
func (m *monitorImpl) GetTotalSliceCount() int {
m.Lock()
defer m.Unlock()
count := 0
for _, stats := range m.readerStats {
count += stats.sliceCount
}
return count
}
func (m *monitorImpl) GetSliceCount(readerID int64) int {
m.Lock()
defer m.Unlock()
if stats, ok := m.readerStats[readerID]; ok {
return stats.sliceCount
}
return 0
}
func (m *monitorImpl) SetSliceCount(readerID int64, count int) {
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,
AlertAttributesSliceCount: &AlertAttributesSlicesCount{
CurrentSliceCount: m.totalSliceCount,
CriticalSliceCount: criticalSliceCount,
},
})
}
}
func (m *monitorImpl) RemoveSlice(slice Slice) {
m.Lock()
defer m.Unlock()
stats, ok := m.sliceStats[slice]
if !ok {
return
}
m.totalPendingTaskCount -= stats.pendingTaskCount
delete(m.sliceStats, slice)
}
func (m *monitorImpl) RemoveReader(readerID int64) {
m.Lock()
defer m.Unlock()
stats, ok := m.readerStats[readerID]
if !ok {
return
}
m.totalSliceCount -= stats.sliceCount
delete(m.readerStats, readerID)
}
func (m *monitorImpl) ResolveAlert(alertType AlertType) {
m.Lock()
defer m.Unlock()
delete(m.pendingAlerts, alertType)
}
func (m *monitorImpl) SilenceAlert(alertType AlertType) {
m.Lock()
defer m.Unlock()
delete(m.pendingAlerts, alertType)
m.silencedAlerts[alertType] = m.timeSource.Now().Add(defaultAlertSilenceDuration)
}
func (m *monitorImpl) AlertCh() <-chan *Alert {
return m.alertCh
}
func (m *monitorImpl) Close() {
m.Lock()
defer m.Unlock()
close(m.shutdownCh)
for {
select {
case <-m.alertCh:
// drain alertCh
default:
close(m.alertCh)
return
}
}
}
func (m *monitorImpl) sendAlertLocked(alert *Alert) {
if m.isClosed() {
// make sure alert won't be sent to a closed chan
return
}
if m.timeSource.Now().Before(m.silencedAlerts[alert.AlertType]) {
return
}
// dedup alerts, we only need one outstanding alert per alert type
if _, ok := m.pendingAlerts[alert.AlertType]; ok {
return
}
select {
case m.alertCh <- alert:
m.pendingAlerts[alert.AlertType] = struct{}{}
default:
// do not block if alertCh full
}
}
func (m *monitorImpl) isClosed() bool {
select {
case <-m.shutdownCh:
return true
default:
return false
}
}