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  }