github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/acyclic/bindinfo/cache.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package bindinfo 15 16 import ( 17 "time" 18 "unsafe" 19 20 "github.com/whtcorpsinc/BerolinaSQL" 21 "github.com/whtcorpsinc/milevadb/metrics" 22 "github.com/whtcorpsinc/milevadb/stochastikctx" 23 "github.com/whtcorpsinc/milevadb/types" 24 "github.com/whtcorpsinc/milevadb/soliton/hint" 25 ) 26 27 const ( 28 // Using is the bind info's in use status. 29 Using = "using" 30 // deleted is the bind info's deleted status. 31 deleted = "deleted" 32 // Invalid is the bind info's invalid status. 33 Invalid = "invalid" 34 // PendingVerify means the bind info needs to be verified. 35 PendingVerify = "pending verify" 36 // Rejected means that the bind has been rejected after verify process. 37 // We can retry it after certain time has passed. 38 Rejected = "rejected" 39 // Manual indicates the binding is created by ALLEGROALLEGROSQL like "create binding for ...". 40 Manual = "manual" 41 // Capture indicates the binding is captured by MilevaDB automatically. 42 Capture = "capture" 43 // Evolve indicates the binding is evolved by MilevaDB from old bindings. 44 Evolve = "evolve" 45 ) 46 47 // Binding stores the basic bind hint info. 48 type Binding struct { 49 BindALLEGROSQL string 50 // Status represents the status of the binding. It can only be one of the following values: 51 // 1. deleted: BindRecord is deleted, can not be used anymore. 52 // 2. using: Binding is in the normal active mode. 53 Status string 54 CreateTime types.Time 55 UFIDelateTime types.Time 56 Source string 57 Charset string 58 DefCauslation string 59 // Hint is the parsed hints, it is used to bind hints to stmt node. 60 Hint *hint.HintsSet 61 // ID is the string form of Hint. It would be non-empty only when the status is `Using` or `PendingVerify`. 62 ID string 63 } 64 65 func (b *Binding) isSame(rb *Binding) bool { 66 if b.ID != "" && rb.ID != "" { 67 return b.ID == rb.ID 68 } 69 // Sometimes we cannot construct `ID` because of the changed schemaReplicant, so we need to compare by bind allegrosql. 70 return b.BindALLEGROSQL == rb.BindALLEGROSQL 71 } 72 73 // SinceUFIDelateTime returns the duration since last uFIDelate time. Export for test. 74 func (b *Binding) SinceUFIDelateTime() (time.Duration, error) { 75 uFIDelateTime, err := b.UFIDelateTime.GoTime(time.Local) 76 if err != nil { 77 return 0, err 78 } 79 return time.Since(uFIDelateTime), nil 80 } 81 82 // cache is a k-v map, key is original allegrosql, value is a slice of BindRecord. 83 type cache map[string][]*BindRecord 84 85 // BindRecord represents a allegrosql bind record retrieved from the storage. 86 type BindRecord struct { 87 OriginalALLEGROSQL string 88 EDB string 89 90 Bindings []Binding 91 } 92 93 // HasUsingBinding checks if there are any using bindings in bind record. 94 func (br *BindRecord) HasUsingBinding() bool { 95 for _, binding := range br.Bindings { 96 if binding.Status == Using { 97 return true 98 } 99 } 100 return false 101 } 102 103 // FindBinding find bindings in BindRecord. 104 func (br *BindRecord) FindBinding(hint string) *Binding { 105 for _, binding := range br.Bindings { 106 if binding.ID == hint { 107 return &binding 108 } 109 } 110 return nil 111 } 112 113 // prepareHints builds ID and Hint for BindRecord. If sctx is not nil, we check if 114 // the BindALLEGROSQL is still valid. 115 func (br *BindRecord) prepareHints(sctx stochastikctx.Context) error { 116 p := BerolinaSQL.New() 117 for i, bind := range br.Bindings { 118 if (bind.Hint != nil && bind.ID != "") || bind.Status == deleted { 119 continue 120 } 121 if sctx != nil { 122 _, err := getHintsForALLEGROSQL(sctx, bind.BindALLEGROSQL) 123 if err != nil { 124 return err 125 } 126 } 127 hintsSet, warns, err := hint.ParseHintsSet(p, bind.BindALLEGROSQL, bind.Charset, bind.DefCauslation, br.EDB) 128 if err != nil { 129 return err 130 } 131 hintsStr, err := hintsSet.Restore() 132 if err != nil { 133 return err 134 } 135 // For `create global binding for select * from t using select * from t`, we allow it though hintsStr is empty. 136 // For `create global binding for select * from t using select /*+ non_exist_hint() */ * from t`, 137 // the hint is totally invalid, we escalate warning to error. 138 if hintsStr == "" && len(warns) > 0 { 139 return warns[0] 140 } 141 br.Bindings[i].Hint = hintsSet 142 br.Bindings[i].ID = hintsStr 143 } 144 return nil 145 } 146 147 // `merge` merges two BindRecord. It will replace old bindings with new bindings if there are new uFIDelates. 148 func merge(lBindRecord, rBindRecord *BindRecord) *BindRecord { 149 if lBindRecord == nil { 150 return rBindRecord 151 } 152 if rBindRecord == nil { 153 return lBindRecord 154 } 155 result := lBindRecord.shallowCopy() 156 for _, rbind := range rBindRecord.Bindings { 157 found := false 158 for j, lbind := range lBindRecord.Bindings { 159 if lbind.isSame(&rbind) { 160 found = true 161 if rbind.UFIDelateTime.Compare(lbind.UFIDelateTime) >= 0 { 162 result.Bindings[j] = rbind 163 } 164 break 165 } 166 } 167 if !found { 168 result.Bindings = append(result.Bindings, rbind) 169 } 170 } 171 return result 172 } 173 174 func (br *BindRecord) remove(deleted *BindRecord) *BindRecord { 175 // Delete all bindings. 176 if len(deleted.Bindings) == 0 { 177 return &BindRecord{OriginalALLEGROSQL: br.OriginalALLEGROSQL, EDB: br.EDB} 178 } 179 result := br.shallowCopy() 180 for _, deletedBind := range deleted.Bindings { 181 for i, bind := range result.Bindings { 182 if bind.isSame(&deletedBind) { 183 result.Bindings = append(result.Bindings[:i], result.Bindings[i+1:]...) 184 break 185 } 186 } 187 } 188 return result 189 } 190 191 func (br *BindRecord) removeDeletedBindings() *BindRecord { 192 result := BindRecord{OriginalALLEGROSQL: br.OriginalALLEGROSQL, EDB: br.EDB, Bindings: make([]Binding, 0, len(br.Bindings))} 193 for _, binding := range br.Bindings { 194 if binding.Status != deleted { 195 result.Bindings = append(result.Bindings, binding) 196 } 197 } 198 return &result 199 } 200 201 // shallowCopy shallow copies the BindRecord. 202 func (br *BindRecord) shallowCopy() *BindRecord { 203 result := BindRecord{ 204 OriginalALLEGROSQL: br.OriginalALLEGROSQL, 205 EDB: br.EDB, 206 Bindings: make([]Binding, len(br.Bindings)), 207 } 208 copy(result.Bindings, br.Bindings) 209 return &result 210 } 211 212 func (br *BindRecord) isSame(other *BindRecord) bool { 213 return br.OriginalALLEGROSQL == other.OriginalALLEGROSQL && br.EDB == other.EDB 214 } 215 216 var statusIndex = map[string]int{ 217 Using: 0, 218 deleted: 1, 219 Invalid: 2, 220 } 221 222 func (br *BindRecord) metrics() ([]float64, []int) { 223 sizes := make([]float64, len(statusIndex)) 224 count := make([]int, len(statusIndex)) 225 if br == nil { 226 return sizes, count 227 } 228 commonLength := float64(len(br.OriginalALLEGROSQL) + len(br.EDB)) 229 // We treat it as deleted if there are no bindings. It could only occur in stochastik handles. 230 if len(br.Bindings) == 0 { 231 sizes[statusIndex[deleted]] = commonLength 232 count[statusIndex[deleted]] = 1 233 return sizes, count 234 } 235 // Make the common length counted in the first binding. 236 sizes[statusIndex[br.Bindings[0].Status]] = commonLength 237 for _, binding := range br.Bindings { 238 sizes[statusIndex[binding.Status]] += binding.size() 239 count[statusIndex[binding.Status]]++ 240 } 241 return sizes, count 242 } 243 244 // size calculates the memory size of a bind info. 245 func (b *Binding) size() float64 { 246 res := len(b.BindALLEGROSQL) + len(b.Status) + 2*int(unsafe.Sizeof(b.CreateTime)) + len(b.Charset) + len(b.DefCauslation) 247 return float64(res) 248 } 249 250 func uFIDelateMetrics(scope string, before *BindRecord, after *BindRecord, sizeOnly bool) { 251 beforeSize, beforeCount := before.metrics() 252 afterSize, afterCount := after.metrics() 253 for status, index := range statusIndex { 254 metrics.BindMemoryUsage.WithLabelValues(scope, status).Add(afterSize[index] - beforeSize[index]) 255 if !sizeOnly { 256 metrics.BindTotalGauge.WithLabelValues(scope, status).Add(float64(afterCount[index] - beforeCount[index])) 257 } 258 } 259 }