github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/tendermint/state/execution_dds.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "github.com/fibonacci-chain/fbc/libs/system/trace" 6 "sync/atomic" 7 "time" 8 9 "github.com/fibonacci-chain/fbc/libs/iavl" 10 "github.com/fibonacci-chain/fbc/libs/system" 11 "github.com/fibonacci-chain/fbc/libs/tendermint/delta" 12 redis_cgi "github.com/fibonacci-chain/fbc/libs/tendermint/delta/redis-cgi" 13 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 14 "github.com/spf13/viper" 15 16 "github.com/fibonacci-chain/fbc/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 { 195 trace.GetElapsedInfo().AddInfo(trace.Delta, fmt.Sprintf("ratio<%.2f>", dc.hitRatio())) 196 wdFunc := evmWatchDataManager.CreateWatchDataGenerator() 197 wasmWdFunc := wasmWatchDataManager.CreateWatchDataGenerator() 198 go dc.uploadData(height, abciResponses, deltaMap, wdFunc, wasmWdFunc) 199 } 200 } 201 202 func (dc *DeltaContext) uploadData(height int64, abciResponses *ABCIResponses, deltaMap interface{}, wdFunc, wasmWdFunc func() ([]byte, error)) { 203 if abciResponses == nil || deltaMap == nil { 204 dc.logger.Error("Failed to upload", "height", height, "error", fmt.Errorf("empty data")) 205 return 206 } 207 208 delta4Upload := &types.Deltas{ 209 Height: height, 210 CompressType: dc.compressType, 211 CompressFlag: dc.compressFlag, 212 From: dc.identity, 213 } 214 215 var err error 216 info := DeltaInfo{abciResponses: abciResponses, treeDeltaMap: deltaMap, marshalWatchData: wdFunc, wasmMarshalWatchData: wasmWdFunc} 217 delta4Upload.Payload, err = info.dataInfo2Bytes() 218 if err != nil { 219 dc.logger.Error("Failed convert dataInfo2Bytes", "target-height", height, "error", err) 220 } 221 222 dc.uploadRoutine(delta4Upload, float64(len(abciResponses.DeliverTxs))) 223 } 224 225 func (dc *DeltaContext) uploadRoutine(deltas *types.Deltas, txnum float64) { 226 if deltas == nil { 227 return 228 } 229 dc.missed += txnum 230 locked := dc.deltaBroker.GetLocker() 231 dc.logger.Info("Try to upload delta:", "target-height", deltas.Height, "locked", locked, "delta", deltas) 232 233 if !locked { 234 return 235 } 236 237 defer dc.deltaBroker.ReleaseLocker() 238 239 upload := func(mrh int64) bool { 240 return dc.upload(deltas, txnum, mrh) 241 } 242 reset, mrh, err := dc.deltaBroker.ResetMostRecentHeightAfterUpload(deltas.Height, upload) 243 if !reset { 244 dc.logger.Info("Failed to reset mrh:", 245 "target-height", deltas.Height, 246 "existing-mrh", mrh, 247 "err", err) 248 } 249 } 250 251 func (dc *DeltaContext) upload(deltas *types.Deltas, txnum float64, mrh int64) bool { 252 if deltas == nil { 253 dc.logger.Error("Failed to upload nil delta") 254 return false 255 } 256 257 if deltas.Size() == 0 { 258 dc.logger.Error("Failed to upload empty delta", 259 "target-height", deltas.Height, 260 "mrh", mrh) 261 return false 262 } 263 264 // marshal deltas to bytes 265 deltaBytes, err := deltas.Marshal() 266 if err != nil { 267 dc.logger.Error("Failed to upload delta", 268 "target-height", deltas.Height, 269 "mrh", mrh, 270 "error", err) 271 return false 272 } 273 274 t2 := time.Now() 275 // set into dds 276 if err = dc.deltaBroker.SetDeltas(deltas.Height, deltaBytes); err != nil { 277 dc.logger.Error("Failed to upload delta", "target-height", deltas.Height, 278 "mrh", mrh, "error", err) 279 return false 280 281 } 282 t3 := time.Now() 283 dc.missed -= txnum 284 dc.hit += txnum 285 dc.logger.Info("Uploaded delta successfully", 286 "target-height", deltas.Height, 287 "mrh", mrh, 288 "marshal", deltas.MarshalOrUnmarshalElapsed(), 289 "calcHash", deltas.HashElapsed(), 290 "compress", deltas.CompressOrUncompressElapsed(), 291 "upload", t3.Sub(t2), 292 "missed", dc.missed, 293 "uploaded", dc.hit, 294 "deltas", deltas) 295 return true 296 } 297 298 // get delta from dds 299 func (dc *DeltaContext) prepareStateDelta(height int64) *DeltaInfo { 300 if !dc.downloadDelta { 301 return nil 302 } 303 304 deltaInfo, mrh := dc.dataMap.fetch(height) 305 306 atomic.StoreInt64(&dc.lastFetchedHeight, height) 307 308 var succeed bool 309 if deltaInfo != nil { 310 if deltaInfo.deltaHeight != height { 311 dc.logger.Error("Prepared an invalid delta!!!", 312 "expected-height", height, 313 "mrh", mrh, 314 "delta-height", deltaInfo.deltaHeight) 315 return nil 316 } 317 succeed = true 318 } 319 320 dc.logger.Info("Prepare delta", "expected-height", height, 321 "mrh", mrh, "succeed", succeed) 322 return deltaInfo 323 } 324 325 type downloadInfo struct { 326 lastTarget int64 327 firstErrorMap map[int64]error 328 lastErrorMap map[int64]error 329 mrhWhen1stErrHappens map[int64]int64 330 mrhWhenlastErrHappens map[int64]int64 331 retried map[int64]int64 332 logger log.Logger 333 } 334 335 func (dc *DeltaContext) downloadRoutine() { 336 var targetHeight int64 337 var lastRemoved int64 338 buffer := int64(dc.bufferSize) 339 info := &downloadInfo{ 340 firstErrorMap: make(map[int64]error), 341 lastErrorMap: make(map[int64]error), 342 mrhWhen1stErrHappens: make(map[int64]int64), 343 mrhWhenlastErrHappens: make(map[int64]int64), 344 retried: make(map[int64]int64), 345 logger: dc.logger, 346 } 347 348 ticker := time.NewTicker(50 * time.Millisecond) 349 350 for range ticker.C { 351 lastFetchedHeight := atomic.LoadInt64(&dc.lastFetchedHeight) 352 if targetHeight <= lastFetchedHeight { 353 // move ahead to lastFetchedHeight + 1 354 targetHeight = lastFetchedHeight + 1 355 356 // git rid of all deltas before <targetHeight> 357 removed, left := dc.dataMap.remove(lastFetchedHeight) 358 dc.logger.Info("Reset target height", 359 "target-height", targetHeight, 360 "last-fetched", lastFetchedHeight, 361 "removed", removed, 362 "left", left, 363 ) 364 } else { 365 if targetHeight%10 == 0 && lastRemoved != lastFetchedHeight { 366 removed, left := dc.dataMap.remove(lastFetchedHeight) 367 dc.logger.Info("Remove stale deltas", 368 "target-height", targetHeight, 369 "last-fetched", lastFetchedHeight, 370 "removed", removed, 371 "left", left, 372 ) 373 lastRemoved = lastFetchedHeight 374 } 375 } 376 377 lastFetchedHeight = atomic.LoadInt64(&dc.lastFetchedHeight) 378 if targetHeight > lastFetchedHeight+buffer { 379 continue 380 } 381 382 err, delta, mrh := dc.download(targetHeight) 383 info.statistics(targetHeight, err, mrh) 384 if err != nil { 385 continue 386 } 387 388 // unmarshal delta bytes to delta info 389 deltaInfo := &DeltaInfo{ 390 from: delta.From, 391 deltaLen: delta.Size(), 392 deltaHeight: delta.Height, 393 } 394 err = deltaInfo.bytes2DeltaInfo(&delta.Payload) 395 if err == nil { 396 dc.dataMap.insert(targetHeight, deltaInfo, mrh) 397 targetHeight++ 398 } 399 } 400 } 401 402 func (info *downloadInfo) clear(height int64) { 403 delete(info.firstErrorMap, height) 404 delete(info.lastErrorMap, height) 405 delete(info.retried, height) 406 delete(info.mrhWhenlastErrHappens, height) 407 delete(info.mrhWhen1stErrHappens, height) 408 } 409 410 func (info *downloadInfo) dump(msg string, target int64) { 411 info.logger.Info(msg, 412 "target-height", target, 413 "retried", info.retried[target], 414 "1st-err", info.firstErrorMap[target], 415 "mrh-when-1st-err", info.mrhWhen1stErrHappens[target], 416 "last-err", info.lastErrorMap[target], 417 "mrh-when-last-err", info.mrhWhenlastErrHappens[target], 418 "map-size", len(info.retried), 419 ) 420 info.clear(target) 421 } 422 423 func (info *downloadInfo) statistics(height int64, err error, mrh int64) { 424 if err != nil { 425 if _, ok := info.firstErrorMap[height]; !ok { 426 info.firstErrorMap[height] = err 427 info.mrhWhen1stErrHappens[height] = mrh 428 } 429 info.lastErrorMap[height] = err 430 info.retried[height]++ 431 info.mrhWhenlastErrHappens[height] = mrh 432 } else { 433 info.dump("Download info", height) 434 } 435 436 if info.lastTarget != height { 437 if _, ok := info.retried[info.lastTarget]; ok { 438 info.dump("Failed to download", info.lastTarget) 439 } 440 info.lastTarget = height 441 } 442 } 443 444 func (dc *DeltaContext) download(height int64) (error, *types.Deltas, int64) { 445 dc.logger.Debug("Download delta started:", "target-height", height) 446 447 t0 := time.Now() 448 deltaBytes, err, latestHeight := dc.deltaBroker.GetDeltas(height) 449 if err != nil { 450 return err, nil, latestHeight 451 } 452 t1 := time.Now() 453 454 // unmarshal 455 delta := &types.Deltas{} 456 457 err = delta.Unmarshal(deltaBytes) 458 if err != nil { 459 dc.logger.Error("Downloaded an invalid delta:", "target-height", height, "err", err) 460 return err, nil, latestHeight 461 } 462 463 cacheMap, cacheList := dc.dataMap.info() 464 dc.logger.Info("Downloaded delta successfully:", 465 "target-height", height, 466 "cacheMap", cacheMap, 467 "cacheList", cacheList, 468 "download", t1.Sub(t0), 469 "calcHash", delta.HashElapsed(), 470 "uncompress", delta.CompressOrUncompressElapsed(), 471 "unmarshal", delta.MarshalOrUnmarshalElapsed(), 472 "delta", delta) 473 474 return nil, delta, latestHeight 475 }