github.com/KinWaiYuen/client-go/v2@v2.5.4/util/misc.go (about) 1 // Copyright 2021 TiKV Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/util/misc.go 19 // 20 21 // Copyright 2021 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package util 36 37 import ( 38 "context" 39 "fmt" 40 "strconv" 41 "strings" 42 "time" 43 44 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 45 "github.com/pingcap/errors" 46 "go.uber.org/zap" 47 ) 48 49 // GCTimeFormat is the format that gc_worker used to store times. 50 const GCTimeFormat = "20060102-15:04:05 -0700" 51 52 // CompatibleParseGCTime parses a string with `GCTimeFormat` and returns a time.Time. If `value` can't be parsed as that 53 // format, truncate to last space and try again. This function is only useful when loading times that saved by 54 // gc_worker. We have changed the format that gc_worker saves time (removed the last field), but when loading times it 55 // should be compatible with the old format. 56 func CompatibleParseGCTime(value string) (time.Time, error) { 57 t, err := time.Parse(GCTimeFormat, value) 58 59 if err != nil { 60 // Remove the last field that separated by space 61 parts := strings.Split(value, " ") 62 prefix := strings.Join(parts[:len(parts)-1], " ") 63 t, err = time.Parse(GCTimeFormat, prefix) 64 } 65 66 if err != nil { 67 err = errors.Errorf("string \"%v\" doesn't has a prefix that matches format \"%v\"", value, GCTimeFormat) 68 } 69 return t, err 70 } 71 72 // WithRecovery wraps goroutine startup call with force recovery. 73 // it will dump current goroutine stack into log if catch any recover result. 74 // exec: execute logic function. 75 // recoverFn: handler will be called after recover and before dump stack, passing `nil` means noop. 76 func WithRecovery(exec func(), recoverFn func(r interface{})) { 77 defer func() { 78 r := recover() 79 if recoverFn != nil { 80 recoverFn(r) 81 } 82 if r != nil { 83 logutil.BgLogger().Error("panic in the recoverable goroutine", 84 zap.Reflect("r", r), 85 zap.Stack("stack trace")) 86 } 87 }() 88 exec() 89 } 90 91 type sessionIDCtxKey struct{} 92 93 // SessionID is the context key type to mark a session. 94 var SessionID = sessionIDCtxKey{} 95 96 // SetSessionID sets session id into context 97 func SetSessionID(ctx context.Context, sessionID uint64) context.Context { 98 return context.WithValue(ctx, SessionID, sessionID) 99 } 100 101 const ( 102 byteSizeGB = int64(1 << 30) 103 byteSizeMB = int64(1 << 20) 104 byteSizeKB = int64(1 << 10) 105 byteSizeBB = int64(1) 106 ) 107 108 // FormatBytes uses to format bytes, this function will prune precision before format bytes. 109 func FormatBytes(numBytes int64) string { 110 if numBytes <= byteSizeKB { 111 return BytesToString(numBytes) 112 } 113 unit, unitStr := getByteUnit(numBytes) 114 if unit == byteSizeBB { 115 return BytesToString(numBytes) 116 } 117 v := float64(numBytes) / float64(unit) 118 decimal := 1 119 if numBytes%unit == 0 { 120 decimal = 0 121 } else if v < 10 { 122 decimal = 2 123 } 124 return fmt.Sprintf("%v %s", strconv.FormatFloat(v, 'f', decimal, 64), unitStr) 125 } 126 127 func getByteUnit(b int64) (int64, string) { 128 if b > byteSizeGB { 129 return byteSizeGB, "GB" 130 } else if b > byteSizeMB { 131 return byteSizeMB, "MB" 132 } else if b > byteSizeKB { 133 return byteSizeKB, "KB" 134 } 135 return byteSizeBB, "Bytes" 136 } 137 138 // BytesToString converts the memory consumption to a readable string. 139 func BytesToString(numBytes int64) string { 140 GB := float64(numBytes) / float64(byteSizeGB) 141 if GB > 1 { 142 return fmt.Sprintf("%v GB", GB) 143 } 144 145 MB := float64(numBytes) / float64(byteSizeMB) 146 if MB > 1 { 147 return fmt.Sprintf("%v MB", MB) 148 } 149 150 KB := float64(numBytes) / float64(byteSizeKB) 151 if KB > 1 { 152 return fmt.Sprintf("%v KB", KB) 153 } 154 155 return fmt.Sprintf("%v Bytes", numBytes) 156 }