mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
1264350566
The streamID.marshalString() is executed in hot path if the query selects _stream_id field. Command to run the benchmark: go test ./lib/logstorage/ -run=NONE -bench=BenchmarkStreamIDMarshalString -benchtime=5s Results before the commit: BenchmarkStreamIDMarshalString-16 438480714 14.04 ns/op 71.23 MB/s 0 B/op 0 allocs/op Results after the commit: BenchmarkStreamIDMarshalString-16 982459660 6.049 ns/op 165.30 MB/s 0 B/op 0 allocs/op
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
package logstorage
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
|
)
|
|
|
|
// TenantID is an id of a tenant for log streams.
|
|
//
|
|
// Each log stream is associated with a single TenantID.
|
|
type TenantID struct {
|
|
// AccountID is the id of the account for the log stream.
|
|
AccountID uint32
|
|
|
|
// ProjectID is the id of the project for the log stream.
|
|
ProjectID uint32
|
|
}
|
|
|
|
// Reset resets tid.
|
|
func (tid *TenantID) Reset() {
|
|
tid.AccountID = 0
|
|
tid.ProjectID = 0
|
|
}
|
|
|
|
// String returns human-readable representation of tid
|
|
func (tid *TenantID) String() string {
|
|
return fmt.Sprintf("{accountID=%d,projectID=%d}", tid.AccountID, tid.ProjectID)
|
|
}
|
|
|
|
// equal returns true if tid equals to a.
|
|
func (tid *TenantID) equal(a *TenantID) bool {
|
|
return tid.AccountID == a.AccountID && tid.ProjectID == a.ProjectID
|
|
}
|
|
|
|
// less returns true if tid is less than a.
|
|
func (tid *TenantID) less(a *TenantID) bool {
|
|
if tid.AccountID != a.AccountID {
|
|
return tid.AccountID < a.AccountID
|
|
}
|
|
return tid.ProjectID < a.ProjectID
|
|
}
|
|
|
|
func (tid *TenantID) marshalString(dst []byte) []byte {
|
|
n := uint64(tid.AccountID)<<32 | uint64(tid.ProjectID)
|
|
dst = marshalUint64Hex(dst, n)
|
|
return dst
|
|
}
|
|
|
|
// marshal appends the marshaled tid to dst and returns the result
|
|
func (tid *TenantID) marshal(dst []byte) []byte {
|
|
dst = encoding.MarshalUint32(dst, tid.AccountID)
|
|
dst = encoding.MarshalUint32(dst, tid.ProjectID)
|
|
return dst
|
|
}
|
|
|
|
// unmarshal unmarshals tid from src and returns the remaining tail.
|
|
func (tid *TenantID) unmarshal(src []byte) ([]byte, error) {
|
|
if len(src) < 8 {
|
|
return src, fmt.Errorf("cannot unmarshal tenantID from %d bytes; need at least 8 bytes", len(src))
|
|
}
|
|
tid.AccountID = encoding.UnmarshalUint32(src[:4])
|
|
tid.ProjectID = encoding.UnmarshalUint32(src[4:])
|
|
return src[8:], nil
|
|
}
|
|
|
|
// GetTenantIDFromRequest returns tenantID from r.
|
|
func GetTenantIDFromRequest(r *http.Request) (TenantID, error) {
|
|
var tenantID TenantID
|
|
|
|
accountID, err := getUint32FromHeader(r, "AccountID")
|
|
if err != nil {
|
|
return tenantID, err
|
|
}
|
|
projectID, err := getUint32FromHeader(r, "ProjectID")
|
|
if err != nil {
|
|
return tenantID, err
|
|
}
|
|
|
|
tenantID.AccountID = accountID
|
|
tenantID.ProjectID = projectID
|
|
return tenantID, nil
|
|
}
|
|
|
|
// ParseTenantID returns tenantID from s.
|
|
//
|
|
// s is expected in the form of accountID:projectID. If s is empty, then zero tenantID is returned.
|
|
func ParseTenantID(s string) (TenantID, error) {
|
|
var tenantID TenantID
|
|
if s == "" {
|
|
return tenantID, nil
|
|
}
|
|
|
|
n := strings.Index(s, ":")
|
|
if n < 0 {
|
|
account, err := getUint32FromString(s)
|
|
if err != nil {
|
|
return tenantID, fmt.Errorf("cannot parse accountID from %q: %w", s, err)
|
|
}
|
|
tenantID.AccountID = account
|
|
|
|
return tenantID, nil
|
|
}
|
|
|
|
account, err := getUint32FromString(s[:n])
|
|
if err != nil {
|
|
return tenantID, fmt.Errorf("cannot parse accountID part from %q: %w", s, err)
|
|
}
|
|
tenantID.AccountID = account
|
|
|
|
project, err := getUint32FromString(s[n+1:])
|
|
if err != nil {
|
|
return tenantID, fmt.Errorf("cannot parse projectID part from %q: %w", s, err)
|
|
}
|
|
tenantID.ProjectID = project
|
|
|
|
return tenantID, nil
|
|
}
|
|
|
|
func getUint32FromHeader(r *http.Request, headerName string) (uint32, error) {
|
|
s := r.Header.Get(headerName)
|
|
if len(s) == 0 {
|
|
return 0, nil
|
|
}
|
|
return getUint32FromString(s)
|
|
}
|
|
|
|
func getUint32FromString(s string) (uint32, error) {
|
|
if len(s) == 0 {
|
|
return 0, nil
|
|
}
|
|
n, err := strconv.ParseUint(s, 10, 32)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("cannot parse %q as uint32: %w", s, err)
|
|
}
|
|
return uint32(n), nil
|
|
}
|