Skip to content

Commit

Permalink
resource_manager: introduce the resource unit model and config (#5862)
Browse files Browse the repository at this point in the history
ref #5811, ref #5851

Introduce the resource unit (RU) model and related config.

Signed-off-by: JmPotato <ghzpotato@gmail.com>

Co-authored-by: Ti Chi Robot <ti-community-prow-bot@tidb.io>
  • Loading branch information
JmPotato and ti-chi-bot authored Jan 16, 2023
1 parent 5573d67 commit c6ebcec
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 0 deletions.
83 changes: 83 additions & 0 deletions pkg/mcs/resource_manager/client/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2023 TiKV Project Authors.
//
// 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,g
// 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 client

const (
defaultReadBaseCost = 1
defaultReadCostPerByte = 1. / 1024 / 1024
defaultReadCPUMsCost = 1
defaultWriteBaseCost = 3
defaultWriteCostPerByte = 5. / 1024 / 1024
)

// RequestUnitConfig is the configuration of the request units, which determines the coefficients of
// the RRU and WRU cost. This configuration should be modified carefully.
type RequestUnitConfig struct {
// ReadBaseCost is the base cost for a read request. No matter how many bytes read/written or
// the CPU times taken for a request, this cost is inevitable.
ReadBaseCost float64 `toml:"read-base-cost" json:"read-base-cost"`
// ReadCostPerByte is the cost for each byte read. It's 1 MiB = 1 RRU by default.
ReadCostPerByte float64 `toml:"read-cost-per-byte" json:"read-cost-per-byte"`
// ReadCPUMsCost is the cost for each millisecond of CPU time taken by a read request.
// It's 1 millisecond = 1 RRU by default.
ReadCPUMsCost float64 `toml:"read-cpu-ms-cost" json:"read-cpu-ms-cost"`
// WriteBaseCost is the base cost for a write request. No matter how many bytes read/written or
// the CPU times taken for a request, this cost is inevitable.
WriteBaseCost float64 `toml:"write-base-cost" json:"write-base-cost"`
// WriteCostPerByte is the cost for each byte written. It's 1 MiB = 5 WRU by default.
WriteCostPerByte float64 `toml:"write-cost-per-byte" json:"write-cost-per-byte"`
}

// DefaultRequestUnitConfig returns the default request unit configuration.
func DefaultRequestUnitConfig() *RequestUnitConfig {
return &RequestUnitConfig{
ReadBaseCost: defaultReadBaseCost,
ReadCostPerByte: defaultReadCostPerByte,
ReadCPUMsCost: defaultReadCPUMsCost,
WriteBaseCost: defaultWriteBaseCost,
WriteCostPerByte: defaultWriteCostPerByte,
}
}

// Config is the configuration of the resource units, which gives the read/write request
// units or request resource cost standards. It should be calculated by a given `RequestUnitConfig`
// or `RequestResourceConfig`.
type Config struct {
ReadBaseCost RequestUnit
ReadBytesCost RequestUnit
ReadCPUMsCost RequestUnit
WriteBaseCost RequestUnit
WriteBytesCost RequestUnit
// TODO: add SQL computing CPU cost.
}

// DefaultConfig returns the default configuration.
func DefaultConfig() *Config {
cfg := generateConfig(
DefaultRequestUnitConfig(),
)
return cfg
}

func generateConfig(ruConfig *RequestUnitConfig) *Config {
cfg := &Config{
ReadBaseCost: RequestUnit(ruConfig.ReadBaseCost),
ReadBytesCost: RequestUnit(ruConfig.ReadCostPerByte),
ReadCPUMsCost: RequestUnit(ruConfig.ReadCPUMsCost),
WriteBaseCost: RequestUnit(ruConfig.WriteBaseCost),
WriteBytesCost: RequestUnit(ruConfig.WriteCostPerByte),
}
return cfg
}
122 changes: 122 additions & 0 deletions pkg/mcs/resource_manager/client/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2023 TiKV Project Authors.
//
// 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,g
// 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 client

import (
"context"

rmpb "github.com/pingcap/kvproto/pkg/resource_manager"
)

// RequestUnit is the basic unit of the resource request management, which has two types:
// - RRU: read request unit
// - WRU: write request unit
type RequestUnit float64

// RequestInfo is the interface of the request information provider. A request should be
// able tell whether it's a write request and if so, the written bytes would also be provided.
type RequestInfo interface {
IsWrite() bool
WriteBytes() uint64
}

// ResponseInfo is the interface of the response information provider. A response should be
// able tell how many bytes it read and KV CPU cost in milliseconds.
type ResponseInfo interface {
ReadBytes() uint64
KVCPUMs() uint64
}

// ResourceCalculator is used to calculate the resource consumption of a request.
type ResourceCalculator interface {
// Trickle is used to calculate the resource consumption periodically rather than on the request path.
// It's mainly used to calculate like the SQL CPU cost.
Trickle(context.Context, *rmpb.Consumption)
// BeforeKVRequest is used to calculate the resource consumption before the KV request.
// It's mainly used to calculate the base and write request cost.
BeforeKVRequest(*rmpb.Consumption, RequestInfo)
// AfterKVRequest is used to calculate the resource consumption after the KV request.
// It's mainly used to calculate the read request cost and KV CPU cost.
AfterKVRequest(*rmpb.Consumption, RequestInfo, ResponseInfo)
}

// KVCalculator is used to calculate the KV-side consumption.
type KVCalculator struct {
*Config
}

var _ ResourceCalculator = (*KVCalculator)(nil)

// func newKVCalculator(cfg *Config) *KVCalculator {
// return &KVCalculator{Config: cfg}
// }

// Trickle ...
func (kc *KVCalculator) Trickle(ctx context.Context, consumption *rmpb.Consumption) {
}

// BeforeKVRequest ...
func (kc *KVCalculator) BeforeKVRequest(consumption *rmpb.Consumption, req RequestInfo) {
if req.IsWrite() {
consumption.KvWriteRpcCount += 1
// Write bytes are knowable in advance, so we can calculate the WRU cost here.
writeBytes := float64(req.WriteBytes())
consumption.WriteBytes += writeBytes
consumption.WRU += float64(kc.WriteBaseCost) + float64(kc.WriteBytesCost)*writeBytes
} else {
consumption.KvReadRpcCount += 1
// Read bytes could not be known before the request is executed,
// so we only add the base cost here.
consumption.RRU += float64(kc.ReadBaseCost)
}
}

// AfterKVRequest ...
func (kc *KVCalculator) AfterKVRequest(consumption *rmpb.Consumption, req RequestInfo, res ResponseInfo) {
// For now, we can only collect the KV CPU cost for a read request.
if !req.IsWrite() {
kvCPUMs := float64(res.KVCPUMs())
consumption.TotalCpuTimeMs += kvCPUMs
consumption.RRU += float64(kc.ReadCPUMsCost) * kvCPUMs
}
// A write request may also read data, which should be counted into the RRU cost.
readBytes := float64(res.ReadBytes())
consumption.ReadBytes += readBytes
consumption.RRU += float64(kc.ReadBytesCost) * readBytes
}

// SQLCalculator is used to calculate the SQL-side consumption.
type SQLCalculator struct {
*Config
}

var _ ResourceCalculator = (*SQLCalculator)(nil)

// func newSQLCalculator(cfg *Config) *SQLCalculator {
// return &SQLCalculator{Config: cfg}
// }

// Trickle ...
// TODO: calculate the SQL CPU cost and related resource consumption.
func (dsc *SQLCalculator) Trickle(ctx context.Context, consumption *rmpb.Consumption) {
}

// BeforeKVRequest ...
func (dsc *SQLCalculator) BeforeKVRequest(consumption *rmpb.Consumption, req RequestInfo) {
}

// AfterKVRequest ...
func (dsc *SQLCalculator) AfterKVRequest(consumption *rmpb.Consumption, req RequestInfo, res ResponseInfo) {
}

0 comments on commit c6ebcec

Please sign in to comment.