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  }