github.com/okex/exchain@v1.8.0/libs/tendermint/state/execution_dds.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "github.com/okex/exchain/libs/system/trace" 6 "sync/atomic" 7 "time" 8 9 "github.com/okex/exchain/libs/iavl" 10 "github.com/okex/exchain/libs/system" 11 "github.com/okex/exchain/libs/tendermint/delta" 12 redis_cgi "github.com/okex/exchain/libs/tendermint/delta/redis-cgi" 13 "github.com/okex/exchain/libs/tendermint/libs/log" 14 "github.com/spf13/viper" 15 16 "github.com/okex/exchain/libs/tendermint/types" 17 ) 18 19 type identityMapType map[string]int64 20 21 func (m identityMapType) String() string { 22 var output string 23 var comma string 24 for k, v := range m { 25 output += fmt.Sprintf("%s%s=%d", comma, k, v) 26 comma = "," 27 } 28 return output 29 } 30 31 func (m identityMapType) increase(from string, num int64) { 32 if len(from) > 0 { 33 m[from] += num 34 } 35 } 36 37 var EmptyGenerateWatchDataF = func() ([]byte, error) { return nil, nil } 38 39 type WatchDataManager interface { 40 CreateWatchDataGenerator() func() ([]byte, error) 41 UnmarshalWatchData([]byte) (interface{}, error) 42 ApplyWatchData(interface{}) 43 } 44 45 type EmptyWatchDataManager struct{} 46 47 func (e EmptyWatchDataManager) CreateWatchDataGenerator() func() ([]byte, error) { 48 return EmptyGenerateWatchDataF 49 } 50 func (e EmptyWatchDataManager) UnmarshalWatchData([]byte) (interface{}, error) { return nil, nil } 51 func (e EmptyWatchDataManager) ApplyWatchData(interface{}) {} 52 53 var ( 54 evmWatchDataManager WatchDataManager = EmptyWatchDataManager{} 55 wasmWatchDataManager WatchDataManager = EmptyWatchDataManager{} 56 ) 57 58 func SetEvmWatchDataManager(manager WatchDataManager) { 59 evmWatchDataManager = manager 60 } 61 62 func SetWasmWatchDataManager(manager WatchDataManager) { 63 wasmWatchDataManager = manager 64 } 65 66 type DeltaContext struct { 67 deltaBroker delta.DeltaBroker 68 lastFetchedHeight int64 69 dataMap *deltaMap 70 71 downloadDelta bool 72 uploadDelta bool 73 hit float64 74 missed float64 75 logger log.Logger 76 compressType int 77 compressFlag int 78 bufferSize int 79 80 idMap identityMapType 81 identity string 82 } 83 84 func newDeltaContext(l log.Logger) *DeltaContext { 85 dp := &DeltaContext{ 86 dataMap: newDataMap(), 87 missed: 1, 88 downloadDelta: types.DownloadDelta, 89 uploadDelta: types.UploadDelta, 90 idMap: make(identityMapType), 91 logger: l, 92 } 93 94 if dp.uploadDelta && dp.downloadDelta { 95 panic("download delta is not allowed if upload delta enabled") 96 } 97 98 if dp.uploadDelta { 99 dp.compressType = viper.GetInt(types.FlagDDSCompressType) 100 dp.compressFlag = viper.GetInt(types.FlagDDSCompressFlag) 101 dp.setIdentity() 102 } 103 return dp 104 } 105 106 func (dc *DeltaContext) init() { 107 108 if dc.uploadDelta || dc.downloadDelta { 109 dc.bufferSize = viper.GetInt(types.FlagBufferSize) 110 if dc.bufferSize < 5 { 111 dc.bufferSize = 5 112 } 113 url := viper.GetString(types.FlagRedisUrl) 114 auth := viper.GetString(types.FlagRedisAuth) 115 expire := time.Duration(viper.GetInt(types.FlagRedisExpire)) * time.Second 116 dbNum := viper.GetInt(types.FlagRedisDB) 117 if dbNum < 0 || dbNum > 15 { 118 panic("delta-redis-db only support 0~15") 119 } 120 dc.deltaBroker = redis_cgi.NewRedisClient(url, auth, expire, dbNum, dc.logger) 121 dc.logger.Info("Init delta broker", "url", url) 122 } 123 124 // control if iavl produce delta or not 125 iavl.SetProduceDelta(dc.uploadDelta) 126 127 if dc.downloadDelta { 128 go dc.downloadRoutine() 129 } 130 131 dc.logger.Info("DeltaContext init", 132 "uploadDelta", dc.uploadDelta, 133 "downloadDelta", dc.downloadDelta, 134 "buffer-size", dc.bufferSize, 135 ) 136 137 } 138 139 func (dc *DeltaContext) setIdentity() { 140 141 var err error 142 dc.identity, err = system.GetIpAddr(viper.GetBool(types.FlagAppendPid)) 143 144 if err != nil { 145 dc.logger.Error("Failed to set identity", "err", err) 146 return 147 } 148 149 dc.logger.Info("Set identity", "identity", dc.identity) 150 } 151 152 func (dc *DeltaContext) hitRatio() float64 { 153 return dc.hit / (dc.hit + dc.missed) 154 } 155 156 func (dc *DeltaContext) statistic(applied bool, txnum int, delta *DeltaInfo) { 157 if applied { 158 dc.hit += float64(txnum) 159 dc.idMap.increase(delta.from, int64(txnum)) 160 } else { 161 dc.missed += float64(txnum) 162 } 163 } 164 165 func (dc *DeltaContext) postApplyBlock(height int64, deltaInfo *DeltaInfo, 166 abciResponses *ABCIResponses, deltaMap interface{}, isFastSync bool) { 167 168 // delta consumer 169 if dc.downloadDelta { 170 171 applied := false 172 deltaLen := 0 173 if deltaInfo != nil { 174 applied = true 175 deltaLen = deltaInfo.deltaLen 176 } 177 178 dc.statistic(applied, len(abciResponses.DeliverTxs), deltaInfo) 179 180 trace.GetElapsedInfo().AddInfo(trace.Delta, 181 fmt.Sprintf("applied<%t>, ratio<%.2f>, from<%s>", 182 applied, dc.hitRatio(), dc.idMap)) 183 184 dc.logger.Info("Post apply block", "height", height, "delta-applied", applied, 185 "applied-ratio", dc.hitRatio(), "delta-length", deltaLen) 186 187 if applied && types.FastQuery { 188 evmWatchDataManager.ApplyWatchData(deltaInfo.watchData) 189 wasmWatchDataManager.ApplyWatchData(deltaInfo.wasmWatchData) 190 } 191 } 192 193 // delta producer 194 if dc.uploadDelta && !types.WasmStoreCode { 195 trace.GetElapsedInfo().AddInfo(trace.Delta, fmt.Sprintf("ratio<%.2f>", dc.hitRatio())) 196 197 wdFunc := evmWatchDataManager.CreateWatchDataGenerator() 198 wasmWdFunc := wasmWatchDataManager.CreateWatchDataGenerator() 199 go dc.uploadData(height, abciResponses, deltaMap, wdFunc, wasmWdFunc) 200 } 201 types.WasmStoreCode = false 202 } 203 204 func (dc *DeltaContext) uploadData(height int64, abciResponses *ABCIResponses, deltaMap interface{}, wdFunc, wasmWdFunc func() ([]byte, error)) { 205 if abciResponses == nil || deltaMap == nil { 206 dc.logger.Error("Failed to upload", "height", height, "error", fmt.Errorf("empty data")) 207 return 208 } 209 210 delta4Upload := &types.Deltas{ 211 Height: height, 212 CompressType: dc.compressType, 213 CompressFlag: dc.compressFlag, 214 From: dc.identity, 215 } 216 217 var err error 218 info := DeltaInfo{abciResponses: abciResponses, treeDeltaMap: deltaMap, marshalWatchData: wdFunc, wasmMarshalWatchData: wasmWdFunc} 219 delta4Upload.Payload, err = info.dataInfo2Bytes() 220 if err != nil { 221 dc.logger.Error("Failed convert dataInfo2Bytes", "target-height", height, "error", err) 222 } 223 224 dc.uploadRoutine(delta4Upload, float64(len(abciResponses.DeliverTxs))) 225 } 226 227 func (dc *DeltaContext) uploadRoutine(deltas *types.Deltas, txnum float64) { 228 if deltas == nil { 229 return 230 } 231 dc.missed += txnum 232 locked := dc.deltaBroker.GetLocker() 233 dc.logger.Info("Try to upload delta:", "target-height", deltas.Height, "locked", locked, "delta", deltas) 234 235 if !locked { 236 return 237 } 238 239 defer dc.deltaBroker.ReleaseLocker() 240 241 upload := func(mrh int64) bool { 242 return dc.upload(deltas, txnum, mrh) 243 } 244 reset, mrh, err := dc.deltaBroker.ResetMostRecentHeightAfterUpload(deltas.Height, upload) 245 if !reset { 246 dc.logger.Info("Failed to reset mrh:", 247 "target-height", deltas.Height, 248 "existing-mrh", mrh, 249 "err", err) 250 } 251 } 252 253 func (dc *DeltaContext) upload(deltas *types.Deltas, txnum float64, mrh int64) bool { 254 if deltas == nil { 255 dc.logger.Error("Failed to upload nil delta") 256 return false 257 } 258 259 if deltas.Size() == 0 { 260 dc.logger.Error("Failed to upload empty delta", 261 "target-height", deltas.Height, 262 "mrh", mrh) 263 return false 264 } 265 266 // marshal deltas to bytes 267 deltaBytes, err := deltas.Marshal() 268 if err != nil { 269 dc.logger.Error("Failed to upload delta", 270 "target-height", deltas.Height, 271 "mrh", mrh, 272 "error", err) 273 return false 274 } 275 276 t2 := time.Now() 277 // set into dds 278 if err = dc.deltaBroker.SetDeltas(deltas.Height, deltaBytes); err != nil { 279 dc.logger.Error("Failed to upload delta", "target-height", deltas.Height, 280 "mrh", mrh, "error", err) 281 return false 282 283 } 284 t3 := time.Now() 285 dc.missed -= txnum 286 dc.hit += txnum 287 dc.logger.Info("Uploaded delta successfully", 288 "target-height", deltas.Height, 289 "mrh", mrh, 290 "marshal", deltas.MarshalOrUnmarshalElapsed(), 291 "calcHash", deltas.HashElapsed(), 292 "compress", deltas.CompressOrUncompressElapsed(), 293 "upload", t3.Sub(t2), 294 "missed", dc.missed, 295 "uploaded", dc.hit, 296 "deltas", deltas) 297 return true 298 } 299 300 // get delta from dds 301 func (dc *DeltaContext) prepareStateDelta(height int64) *DeltaInfo { 302 if !dc.downloadDelta { 303 return nil 304 } 305 306 deltaInfo, mrh := dc.dataMap.fetch(height) 307 308 atomic.StoreInt64(&dc.lastFetchedHeight, height) 309 310 var succeed bool 311 if deltaInfo != nil { 312 if deltaInfo.deltaHeight != height { 313 dc.logger.Error("Prepared an invalid delta!!!", 314 "expected-height", height, 315 "mrh", mrh, 316 "delta-height", deltaInfo.deltaHeight) 317 return nil 318 } 319 succeed = true 320 } 321 322 dc.logger.Info("Prepare delta", "expected-height", height, 323 "mrh", mrh, "succeed", succeed) 324 return deltaInfo 325 } 326 327 type downloadInfo struct { 328 lastTarget int64 329 firstErrorMap map[int64]error 330 lastErrorMap map[int64]error 331 mrhWhen1stErrHappens map[int64]int64 332 mrhWhenlastErrHappens map[int64]int64 333 retried map[int64]int64 334 logger log.Logger 335 } 336 337 func (dc *DeltaContext) downloadRoutine() { 338 var targetHeight int64 339 var lastRemoved int64 340 buffer := int64(dc.bufferSize) 341 info := &downloadInfo{ 342 firstErrorMap: make(map[int64]error), 343 lastErrorMap: make(map[int64]error), 344 mrhWhen1stErrHappens: make(map[int64]int64), 345 mrhWhenlastErrHappens: make(map[int64]int64), 346 retried: make(map[int64]int64), 347 logger: dc.logger, 348 } 349 350 ticker := time.NewTicker(50 * time.Millisecond) 351 352 for range ticker.C { 353 lastFetchedHeight := atomic.LoadInt64(&dc.lastFetchedHeight) 354 if targetHeight <= lastFetchedHeight { 355 // move ahead to lastFetchedHeight + 1 356 targetHeight = lastFetchedHeight + 1 357 358 // git rid of all deltas before <targetHeight> 359 removed, left := dc.dataMap.remove(lastFetchedHeight) 360 dc.logger.Info("Reset target height", 361 "target-height", targetHeight, 362 "last-fetched", lastFetchedHeight, 363 "removed", removed, 364 "left", left, 365 ) 366 } else { 367 if targetHeight%10 == 0 && lastRemoved != lastFetchedHeight { 368 removed, left := dc.dataMap.remove(lastFetchedHeight) 369 dc.logger.Info("Remove stale deltas", 370 "target-height", targetHeight, 371 "last-fetched", lastFetchedHeight, 372 "removed", removed, 373 "left", left, 374 ) 375 lastRemoved = lastFetchedHeight 376 } 377 } 378 379 lastFetchedHeight = atomic.LoadInt64(&dc.lastFetchedHeight) 380 if targetHeight > lastFetchedHeight+buffer { 381 continue 382 } 383 384 err, delta, mrh := dc.download(targetHeight) 385 info.statistics(targetHeight, err, mrh) 386 if err != nil { 387 continue 388 } 389 390 // unmarshal delta bytes to delta info 391 deltaInfo := &DeltaInfo{ 392 from: delta.From, 393 deltaLen: delta.Size(), 394 deltaHeight: delta.Height, 395 } 396 err = deltaInfo.bytes2DeltaInfo(&delta.Payload) 397 if err == nil { 398 dc.dataMap.insert(targetHeight, deltaInfo, mrh) 399 targetHeight++ 400 } 401 } 402 } 403 404 func (info *downloadInfo) clear(height int64) { 405 delete(info.firstErrorMap, height) 406 delete(info.lastErrorMap, height) 407 delete(info.retried, height) 408 delete(info.mrhWhenlastErrHappens, height) 409 delete(info.mrhWhen1stErrHappens, height) 410 } 411 412 func (info *downloadInfo) dump(msg string, target int64) { 413 info.logger.Info(msg, 414 "target-height", target, 415 "retried", info.retried[target], 416 "1st-err", info.firstErrorMap[target], 417 "mrh-when-1st-err", info.mrhWhen1stErrHappens[target], 418 "last-err", info.lastErrorMap[target], 419 "mrh-when-last-err", info.mrhWhenlastErrHappens[target], 420 "map-size", len(info.retried), 421 ) 422 info.clear(target) 423 } 424 425 func (info *downloadInfo) statistics(height int64, err error, mrh int64) { 426 if err != nil { 427 if _, ok := info.firstErrorMap[height]; !ok { 428 info.firstErrorMap[height] = err 429 info.mrhWhen1stErrHappens[height] = mrh 430 } 431 info.lastErrorMap[height] = err 432 info.retried[height]++ 433 info.mrhWhenlastErrHappens[height] = mrh 434 } else { 435 info.dump("Download info", height) 436 } 437 438 if info.lastTarget != height { 439 if _, ok := info.retried[info.lastTarget]; ok { 440 info.dump("Failed to download", info.lastTarget) 441 } 442 info.lastTarget = height 443 } 444 } 445 446 func (dc *DeltaContext) download(height int64) (error, *types.Deltas, int64) { 447 dc.logger.Debug("Download delta started:", "target-height", height) 448 449 t0 := time.Now() 450 deltaBytes, err, latestHeight := dc.deltaBroker.GetDeltas(height) 451 if err != nil { 452 return err, nil, latestHeight 453 } 454 t1 := time.Now() 455 456 // unmarshal 457 delta := &types.Deltas{} 458 459 err = delta.Unmarshal(deltaBytes) 460 if err != nil { 461 dc.logger.Error("Downloaded an invalid delta:", "target-height", height, "err", err) 462 return err, nil, latestHeight 463 } 464 465 cacheMap, cacheList := dc.dataMap.info() 466 dc.logger.Info("Downloaded delta successfully:", 467 "target-height", height, 468 "cacheMap", cacheMap, 469 "cacheList", cacheList, 470 "download", t1.Sub(t0), 471 "calcHash", delta.HashElapsed(), 472 "uncompress", delta.CompressOrUncompressElapsed(), 473 "unmarshal", delta.MarshalOrUnmarshalElapsed(), 474 "delta", delta) 475 476 return nil, delta, latestHeight 477 }