github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/util/ledger/reporters/atree_reporter.go (about)

     1  package reporters
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	goRuntime "runtime"
     8  	"sync"
     9  
    10  	"github.com/onflow/atree"
    11  	"github.com/rs/zerolog"
    12  	"github.com/schollz/progressbar/v3"
    13  
    14  	"github.com/onflow/flow-go/ledger"
    15  	"github.com/onflow/flow-go/model/flow"
    16  )
    17  
    18  // AtreeReporter iterates payloads and generates payload and atree stats.
    19  type AtreeReporter struct {
    20  	Log zerolog.Logger
    21  	RWF ReportWriterFactory
    22  }
    23  
    24  var _ ledger.Reporter = &AtreeReporter{}
    25  
    26  func (r *AtreeReporter) Name() string {
    27  	return "Atree Reporter"
    28  }
    29  
    30  func (r *AtreeReporter) Report(payloads []ledger.Payload, commit ledger.State) error {
    31  	rwa := r.RWF.ReportWriter("atree_report")
    32  	defer rwa.Close()
    33  
    34  	progress := progressbar.Default(int64(len(payloads)), "Processing:")
    35  
    36  	workerCount := goRuntime.NumCPU() / 2
    37  	if workerCount == 0 {
    38  		workerCount = 1
    39  	}
    40  
    41  	jobs := make(chan *ledger.Payload, workerCount)
    42  
    43  	results := make(chan payloadStats, workerCount)
    44  	defer close(results)
    45  
    46  	// create multiple workers to process payloads concurrently
    47  	wg := &sync.WaitGroup{}
    48  	wg.Add(workerCount)
    49  
    50  	for i := 0; i < workerCount; i++ {
    51  		go func() {
    52  			defer wg.Done()
    53  
    54  			var stats payloadStats
    55  			for p := range jobs {
    56  				err := stats.process(p)
    57  				if err != nil {
    58  					k, keyErr := p.Key()
    59  					if keyErr != nil {
    60  						r.Log.Err(keyErr).Msg("failed to get payload key")
    61  					} else {
    62  						r.Log.Err(err).Msgf("failed to process payload %s", k.String())
    63  					}
    64  				}
    65  			}
    66  			results <- stats
    67  		}()
    68  	}
    69  
    70  	// produce jobs for workers to process
    71  	for i := 0; i < len(payloads); i++ {
    72  		jobs <- &payloads[i]
    73  
    74  		err := progress.Add(1)
    75  		if err != nil {
    76  			panic(fmt.Errorf("progress.Add(1): %w", err))
    77  		}
    78  	}
    79  	close(jobs)
    80  
    81  	// wait until all jobs are done
    82  	wg.Wait()
    83  
    84  	err := progress.Finish()
    85  	if err != nil {
    86  		panic(fmt.Errorf("progress.Finish(): %w", err))
    87  	}
    88  
    89  	// aggregate all payload stats
    90  	var stats payloadStats
    91  	for i := 0; i < workerCount; i++ {
    92  		r := <-results
    93  		stats.add(&r)
    94  	}
    95  
    96  	rwa.Write(stats)
    97  
    98  	return nil
    99  }
   100  
   101  type payloadType uint
   102  
   103  const (
   104  	unknownPayloadType payloadType = iota
   105  	fvmPayloadType
   106  	storagePayloadType
   107  	slabPayloadType
   108  )
   109  
   110  func getPayloadType(p *ledger.Payload) (payloadType, error) {
   111  	k, err := p.Key()
   112  	if err != nil {
   113  		return unknownPayloadType, err
   114  	}
   115  	if len(k.KeyParts) < 2 {
   116  		return unknownPayloadType, nil
   117  	}
   118  
   119  	id := flow.NewRegisterID(
   120  		flow.BytesToAddress(k.KeyParts[0].Value),
   121  		string(k.KeyParts[1].Value))
   122  	if id.IsInternalState() {
   123  		return fvmPayloadType, nil
   124  	}
   125  
   126  	if bytes.HasPrefix(k.KeyParts[1].Value, []byte(atree.LedgerBaseStorageSlabPrefix)) {
   127  		return slabPayloadType, nil
   128  	}
   129  	return storagePayloadType, nil
   130  }
   131  
   132  type slabPayloadStats struct {
   133  	SlabArrayMetaCount            uint
   134  	SlabArrayDataCount            uint
   135  	SlabMapMetaCount              uint
   136  	SlabMapDataCount              uint
   137  	SlabMapExternalCollisionCount uint
   138  	SlabStorableCount             uint
   139  	SlabMapCollisionGroupCount    uint
   140  
   141  	SlabMapCollisionCounts []uint
   142  }
   143  
   144  type payloadStats struct {
   145  	FVMPayloadCount     uint
   146  	StoragePayloadCount uint
   147  	SlabPayloadCount    uint
   148  	slabPayloadStats
   149  }
   150  
   151  func (s *payloadStats) add(s1 *payloadStats) {
   152  	s.FVMPayloadCount += s1.FVMPayloadCount
   153  	s.StoragePayloadCount += s1.StoragePayloadCount
   154  	s.SlabPayloadCount += s1.SlabPayloadCount
   155  	s.SlabArrayMetaCount += s1.SlabArrayMetaCount
   156  	s.SlabArrayDataCount += s1.SlabArrayDataCount
   157  	s.SlabMapMetaCount += s1.SlabMapMetaCount
   158  	s.SlabMapDataCount += s1.SlabMapDataCount
   159  	s.SlabMapExternalCollisionCount += s1.SlabMapExternalCollisionCount
   160  	s.SlabStorableCount += s1.SlabStorableCount
   161  	s.SlabMapCollisionGroupCount += s1.SlabMapCollisionGroupCount
   162  
   163  	if len(s1.SlabMapCollisionCounts) > 0 {
   164  		s.SlabMapCollisionCounts = append(s.SlabMapCollisionCounts, s1.SlabMapCollisionCounts...)
   165  	}
   166  }
   167  
   168  func (s *payloadStats) process(p *ledger.Payload) error {
   169  	pt, err := getPayloadType(p)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	switch pt {
   174  	case unknownPayloadType:
   175  		return fmt.Errorf("unknown payload: %s", p.String())
   176  	case fvmPayloadType:
   177  		s.FVMPayloadCount++
   178  	case storagePayloadType:
   179  		s.StoragePayloadCount++
   180  	case slabPayloadType:
   181  		s.SlabPayloadCount++
   182  	}
   183  
   184  	if pt != slabPayloadType {
   185  		return nil
   186  	}
   187  
   188  	if len(p.Value()) < versionAndFlagSize {
   189  		return errors.New("data is too short")
   190  	}
   191  
   192  	flag := p.Value()[flagIndex]
   193  
   194  	switch dataType := getSlabType(flag); dataType {
   195  	case slabArray:
   196  		switch arrayDataType := getSlabArrayType(flag); arrayDataType {
   197  		case slabArrayData:
   198  			s.SlabArrayDataCount++
   199  
   200  		case slabArrayMeta:
   201  			s.SlabArrayMetaCount++
   202  
   203  		default:
   204  			return fmt.Errorf("slab array has invalid flag 0x%x", flag)
   205  		}
   206  
   207  	case slabMap:
   208  		switch mapDataType := getSlabMapType(flag); mapDataType {
   209  		case slabMapData:
   210  			_, collisionGroupCount, err := getCollisionGroupCountFromSlabMapData(p.Value())
   211  			if err != nil {
   212  				return err
   213  			}
   214  			if collisionGroupCount > 0 {
   215  				_, inlineCollisionCount, err := getInlineCollisionCountsFromSlabMapData(p.Value())
   216  				if err != nil {
   217  					return err
   218  				}
   219  				if len(inlineCollisionCount) > 0 {
   220  					s.SlabMapCollisionCounts = append(s.SlabMapCollisionCounts, inlineCollisionCount...)
   221  				}
   222  			}
   223  			s.SlabMapCollisionGroupCount += collisionGroupCount
   224  			s.SlabMapDataCount++
   225  
   226  		case slabMapCollisionGroup:
   227  			elements, err := parseSlabMapData(p.Value())
   228  			if err != nil {
   229  				return err
   230  			}
   231  			_, rawElements, err := parseRawElements(elements, decMode)
   232  			if err != nil {
   233  				return err
   234  			}
   235  			if len(rawElements) > 0 {
   236  				s.SlabMapCollisionCounts = append(s.SlabMapCollisionCounts, uint(len(rawElements)))
   237  			}
   238  			s.SlabMapExternalCollisionCount++
   239  
   240  		case slabMapMeta:
   241  			s.SlabMapMetaCount++
   242  
   243  		default:
   244  			return fmt.Errorf("slab map has invalid flag 0x%x", flag)
   245  		}
   246  
   247  	case slabStorable:
   248  		s.SlabStorableCount++
   249  
   250  	default:
   251  		return fmt.Errorf("slab data has invalid flag 0x%x", flag)
   252  	}
   253  
   254  	return nil
   255  }