Files
tidb/timer/api/store.go

208 lines
5.3 KiB
Go

// Copyright 2023 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"reflect"
"strings"
"time"
"unsafe"
)
type optionalVal interface {
optionalVal()
Present() bool
Clear()
}
// OptionalVal is used by `TimerCond` and `TimerUpdate` to indicate
// whether some field's condition has been set or whether is field should be update
type OptionalVal[T any] struct {
v T
present bool
}
// NewOptionalVal creates a new OptionalVal
func NewOptionalVal[T any](val T) (o OptionalVal[T]) {
o.Set(val)
return
}
func (*OptionalVal[T]) optionalVal() {}
// Present indicates whether the field value is set.
func (o *OptionalVal[T]) internalPresent() bool {
return o.present
}
// Present indicates whether the field value is set.
func (o *OptionalVal[T]) Present() bool {
return o.present
}
// Get returns the current value, if the second return value is false, this means it is not set.
func (o *OptionalVal[T]) Get() (v T, present bool) {
return o.v, o.present
}
// Set sets the value
func (o *OptionalVal[T]) Set(v T) {
o.v, o.present = v, true
}
// Clear clears the value. The `Present()` will return false after `Clear` is called.
func (o *OptionalVal[T]) Clear() {
var v T
o.v, o.present = v, false
}
func iterOptionalFields(v reflect.Value, excludes []unsafe.Pointer, fn func(name string, val optionalVal) bool) {
tp := v.Type()
loop:
for i := 0; i < v.NumField(); i++ {
fieldVal := v.Field(i).Addr()
optVal, ok := fieldVal.Interface().(optionalVal)
if !ok {
continue
}
for _, ex := range excludes {
if fieldVal.UnsafePointer() == ex {
continue loop
}
}
if !fn(tp.Field(i).Name, optVal) {
break loop
}
}
}
// TimerCond is the condition to filter a timer record
type TimerCond struct {
// ID indicates to filter the timer record with ID
ID OptionalVal[string]
// Namespace indicates to filter the timer by Namespace
Namespace OptionalVal[string]
// Key indicates to filter the timer record with ID
// The filter behavior is defined by `KeyPrefix`
Key OptionalVal[string]
// KeyPrefix indicates how to filter with timer's key if `Key` is set
// If `KeyPrefix is` true, it will check whether the timer's key is prefixed with `TimerCond.Key`.
// Otherwise, it will check whether the timer's key equals `TimerCond.Key`.
KeyPrefix bool
}
// Match will return whether the condition match the timer record
func (c *TimerCond) Match(t *TimerRecord) bool {
if val, ok := c.ID.Get(); ok && t.ID != val {
return false
}
if val, ok := c.Namespace.Get(); ok && t.Namespace != val {
return false
}
if val, ok := c.Key.Get(); ok {
if c.KeyPrefix && !strings.HasPrefix(t.Key, val) {
return false
}
if !c.KeyPrefix && t.Key != val {
return false
}
}
return true
}
// FieldsSet returns all fields that has been set exclude excludes
func (c *TimerCond) FieldsSet(excludes ...unsafe.Pointer) (fields []string) {
iterOptionalFields(reflect.ValueOf(c).Elem(), excludes, func(name string, val optionalVal) bool {
if val.Present() {
fields = append(fields, name)
}
return true
})
return
}
// Clear clears all fields
func (c *TimerCond) Clear() {
iterOptionalFields(reflect.ValueOf(c).Elem(), nil, func(name string, val optionalVal) bool {
val.Clear()
return true
})
c.KeyPrefix = false
}
// TimerUpdate indicates how to update a timer
type TimerUpdate struct {
// Enable indicates to set the timer's `Enable` field
Enable OptionalVal[bool]
// SchedPolicyType indicates to set the timer's `SchedPolicyType` field
SchedPolicyType OptionalVal[SchedPolicyType]
// SchedPolicyExpr indicates to set the timer's `SchedPolicyExpr` field
SchedPolicyExpr OptionalVal[string]
// Watermark indicates to set the timer's `Watermark` field
Watermark OptionalVal[time.Time]
// SummaryData indicates to set the timer's `Summary` field
SummaryData OptionalVal[[]byte]
}
// Apply applies the update to a timer
func (u *TimerUpdate) Apply(record *TimerRecord) error {
if v, ok := u.Enable.Get(); ok {
record.Enable = v
}
if v, ok := u.SchedPolicyType.Get(); ok {
record.SchedPolicyType = v
}
if v, ok := u.SchedPolicyExpr.Get(); ok {
record.SchedPolicyExpr = v
}
if v, ok := u.Watermark.Get(); ok {
record.Watermark = v
}
if v, ok := u.SummaryData.Get(); ok {
record.SummaryData = v
}
return nil
}
// FieldsSet returns all fields that has been set exclude excludes
func (u *TimerUpdate) FieldsSet(excludes ...unsafe.Pointer) (fields []string) {
iterOptionalFields(reflect.ValueOf(u).Elem(), excludes, func(name string, val optionalVal) bool {
if val.Present() {
fields = append(fields, name)
}
return true
})
return
}
// Clear clears all fields
func (u *TimerUpdate) Clear() {
iterOptionalFields(reflect.ValueOf(u).Elem(), nil, func(name string, val optionalVal) bool {
val.Clear()
return true
})
}