Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource_manager: introduce the resource unit model and config #5862

Merged
merged 5 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
defaultWriteBaseCost = 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it too high?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense. Let's keep it a smaller value first. We can adjust it after some tests.

defaultWriteCostPerByte = 10. / 1024 / 1024
defaultWriteCPUMsCost = 1
)

// 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"`
// 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 = 10 WRU by default.
WriteCostPerByte float64 `toml:"write-cost-per-byte" json:"write-cost-per-byte"`
// WriteCPUMsCost is the cost for each millisecond of CPU time taken by a write request.
// It's 1 millisecond = 1 WRU by default.
WriteCPUMsCost float64 `toml:"write-cpu-ms-cost" json:"write-cpu-ms-cost"`
}

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

// 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
WriteBaseCost RequestUnit
WriteBytesCost RequestUnit
WriteCPUMsCost 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),
WriteBaseCost: RequestUnit(ruConfig.WriteBaseCost),
WriteBytesCost: RequestUnit(ruConfig.WriteCostPerByte),
WriteCPUMsCost: RequestUnit(ruConfig.WriteCPUMsCost),
}
return cfg
}
114 changes: 114 additions & 0 deletions pkg/mcs/resource_manager/client/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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, map[rmpb.ResourceType]float64, map[rmpb.RequestUnitType]float64)
// 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(map[rmpb.ResourceType]float64, map[rmpb.RequestUnitType]float64, 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(map[rmpb.ResourceType]float64, map[rmpb.RequestUnitType]float64, RequestInfo, ResponseInfo)
}

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

// 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
writeBytes := float64(req.WriteBytes())
consumption.WriteBytes += writeBytes
consumption.WRU += float64(kc.WriteBaseCost) + float64(kc.WriteBytesCost)*writeBytes
} else {
consumption.KvReadRpcCount += 1
consumption.RRU += float64(kc.ReadBaseCost)
}
}

// AfterKVRequest ...
func (kc *KVCalculator) AfterKVRequest(consumption *rmpb.Consumption, req RequestInfo, res ResponseInfo) {
if req.IsWrite() {
kvCPUMs := float64(res.KVCPUMs())
consumption.TotalCpuTimeMs += kvCPUMs
consumption.WRU += float64(kc.WriteCPUMsCost) * kvCPUMs
} else {
JmPotato marked this conversation as resolved.
Show resolved Hide resolved
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
}

// 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, resource map[rmpb.ResourceType]float64, ru map[rmpb.RequestUnitType]float64) {
JmPotato marked this conversation as resolved.
Show resolved Hide resolved
}

// BeforeKVRequest ...
func (dsc *SQLCalculator) BeforeKVRequest(resource map[rmpb.ResourceType]float64, ru map[rmpb.RequestUnitType]float64, req RequestInfo) {
JmPotato marked this conversation as resolved.
Show resolved Hide resolved
}

// AfterKVRequest ...
func (dsc *SQLCalculator) AfterKVRequest(resource map[rmpb.ResourceType]float64, ru map[rmpb.RequestUnitType]float64, req RequestInfo, res ResponseInfo) {
JmPotato marked this conversation as resolved.
Show resolved Hide resolved
}