github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/migration/nv10/top.go (about)

     1  package nv10
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	address "github.com/filecoin-project/go-address"
    11  	"github.com/filecoin-project/go-state-types/abi"
    12  	"github.com/filecoin-project/go-state-types/rt"
    13  	cid "github.com/ipfs/go-cid"
    14  	cbor "github.com/ipfs/go-ipld-cbor"
    15  	"golang.org/x/sync/errgroup"
    16  	"golang.org/x/xerrors"
    17  
    18  	builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
    19  	states2 "github.com/filecoin-project/specs-actors/v2/actors/states"
    20  
    21  	builtin3 "github.com/filecoin-project/specs-actors/v4/actors/builtin"
    22  	states3 "github.com/filecoin-project/specs-actors/v4/actors/states"
    23  	adt3 "github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    24  )
    25  
    26  // Config parameterizes a state tree migration
    27  type Config struct {
    28  	// Number of migration worker goroutines to run.
    29  	// More workers enables higher CPU utilization doing migration computations (including state encoding)
    30  	MaxWorkers uint
    31  	// Capacity of the queue of jobs available to workers (zero for unbuffered).
    32  	// A queue length of hundreds to thousands improves throughput at the cost of memory.
    33  	JobQueueSize uint
    34  	// Capacity of the queue receiving migration results from workers, for persisting (zero for unbuffered).
    35  	// A queue length of tens to hundreds improves throughput at the cost of memory.
    36  	ResultQueueSize uint
    37  	// Time between progress logs to emit.
    38  	// Zero (the default) results in no progress logs.
    39  	ProgressLogPeriod time.Duration
    40  }
    41  
    42  type Logger interface {
    43  	// This is the same logging interface provided by the Runtime.
    44  	Log(level rt.LogLevel, msg string, args ...interface{})
    45  }
    46  
    47  func ActorHeadKey(addr address.Address, head cid.Cid) string {
    48  	return addr.String() + "-h-" + head.String()
    49  }
    50  
    51  func DeadlineKey(dlCid cid.Cid) string {
    52  	return "d-" + dlCid.String()
    53  }
    54  
    55  func SectorsRootKey(sCid cid.Cid) string {
    56  	return "s-" + sCid.String()
    57  }
    58  
    59  // MigrationCache stores and loads cached data. Its implementation must be threadsafe
    60  type MigrationCache interface {
    61  	Write(key string, newCid cid.Cid) error
    62  	Read(key string) (bool, cid.Cid, error)
    63  	Load(key string, loadFunc func() (cid.Cid, error)) (cid.Cid, error)
    64  }
    65  
    66  // Migrates the filecoin state tree starting from the global state tree and upgrading all actor state.
    67  // The store must support concurrent writes (even if the configured worker count is 1).
    68  func MigrateStateTree(ctx context.Context, store cbor.IpldStore, actorsRootIn cid.Cid, priorEpoch abi.ChainEpoch, cfg Config, log Logger, cache MigrationCache) (cid.Cid, error) {
    69  	if cfg.MaxWorkers <= 0 {
    70  		return cid.Undef, xerrors.Errorf("invalid migration config with %d workers", cfg.MaxWorkers)
    71  	}
    72  
    73  	// Maps prior version code CIDs to migration functions.
    74  	var migrations = map[cid.Cid]actorMigration{
    75  		builtin2.AccountActorCodeID:          nilMigrator{builtin3.AccountActorCodeID},
    76  		builtin2.CronActorCodeID:             nilMigrator{builtin3.CronActorCodeID},
    77  		builtin2.InitActorCodeID:             cachedMigration(cache, initMigrator{}),
    78  		builtin2.MultisigActorCodeID:         cachedMigration(cache, multisigMigrator{}),
    79  		builtin2.PaymentChannelActorCodeID:   cachedMigration(cache, paychMigrator{}),
    80  		builtin2.RewardActorCodeID:           nilMigrator{builtin3.RewardActorCodeID},
    81  		builtin2.StorageMarketActorCodeID:    cachedMigration(cache, marketMigrator{}),
    82  		builtin2.StorageMinerActorCodeID:     cachedMigration(cache, minerMigrator{}),
    83  		builtin2.StoragePowerActorCodeID:     cachedMigration(cache, powerMigrator{}),
    84  		builtin2.SystemActorCodeID:           nilMigrator{builtin3.SystemActorCodeID},
    85  		builtin2.VerifiedRegistryActorCodeID: cachedMigration(cache, verifregMigrator{}),
    86  	}
    87  	// Set of prior version code CIDs for actors to defer during iteration, for explicit migration afterwards.
    88  	var deferredCodeIDs = map[cid.Cid]struct{}{
    89  		// None
    90  	}
    91  	if len(migrations)+len(deferredCodeIDs) != 11 {
    92  		panic(fmt.Sprintf("incomplete migration specification with %d code CIDs", len(migrations)))
    93  	}
    94  	startTime := time.Now()
    95  
    96  	// Load input and output state trees
    97  	adtStore := adt3.WrapStore(ctx, store)
    98  	actorsIn, err := states2.LoadTree(adtStore, actorsRootIn)
    99  	if err != nil {
   100  		return cid.Undef, err
   101  	}
   102  	actorsOut, err := states3.NewTree(adtStore)
   103  	if err != nil {
   104  		return cid.Undef, err
   105  	}
   106  
   107  	// Setup synchronization
   108  	grp, ctx := errgroup.WithContext(ctx)
   109  	// Input and output queues for workers.
   110  	jobCh := make(chan *migrationJob, cfg.JobQueueSize)
   111  	jobResultCh := make(chan *migrationJobResult, cfg.ResultQueueSize)
   112  	// Atomically-modified counters for logging progress
   113  	var jobCount uint32
   114  	var doneCount uint32
   115  
   116  	// Iterate all actors in old state root to create migration jobs for each non-deferred actor.
   117  	grp.Go(func() error {
   118  		defer close(jobCh)
   119  		log.Log(rt.INFO, "Creating migration jobs for tree %s", actorsRootIn)
   120  		if err = actorsIn.ForEach(func(addr address.Address, actorIn *states2.Actor) error {
   121  			if _, ok := deferredCodeIDs[actorIn.Code]; ok {
   122  				return nil // Deferred for explicit migration later.
   123  			}
   124  			nextInput := &migrationJob{
   125  				Address:        addr,
   126  				Actor:          *actorIn, // Must take a copy, the pointer is not stable.
   127  				cache:          cache,
   128  				actorMigration: migrations[actorIn.Code],
   129  			}
   130  			select {
   131  			case jobCh <- nextInput:
   132  			case <-ctx.Done():
   133  				return ctx.Err()
   134  			}
   135  			atomic.AddUint32(&jobCount, 1)
   136  			return nil
   137  		}); err != nil {
   138  			return err
   139  		}
   140  		log.Log(rt.INFO, "Done creating %d migration jobs for tree %s after %v", jobCount, actorsRootIn, time.Since(startTime))
   141  		return nil
   142  	})
   143  
   144  	// Worker threads run jobs.
   145  	var workerWg sync.WaitGroup
   146  	for i := uint(0); i < cfg.MaxWorkers; i++ {
   147  		workerWg.Add(1)
   148  		workerId := i
   149  		grp.Go(func() error {
   150  			defer workerWg.Done()
   151  			for job := range jobCh {
   152  				result, err := job.run(ctx, store, priorEpoch)
   153  				if err != nil {
   154  					return err
   155  				}
   156  				select {
   157  				case jobResultCh <- result:
   158  				case <-ctx.Done():
   159  					return ctx.Err()
   160  				}
   161  				atomic.AddUint32(&doneCount, 1)
   162  			}
   163  			log.Log(rt.INFO, "Worker %d done", workerId)
   164  			return nil
   165  		})
   166  	}
   167  	log.Log(rt.INFO, "Started %d workers", cfg.MaxWorkers)
   168  
   169  	// Monitor the job queue. This non-critical goroutine is outside the errgroup and exits when
   170  	// workersFinished is closed, or the context done.
   171  	workersFinished := make(chan struct{}) // Closed when waitgroup is emptied.
   172  	if cfg.ProgressLogPeriod > 0 {
   173  		go func() {
   174  			defer log.Log(rt.DEBUG, "Job queue monitor done")
   175  			for {
   176  				select {
   177  				case <-time.After(cfg.ProgressLogPeriod):
   178  					jobsNow := jobCount // Snapshot values to avoid incorrect-looking arithmetic if they change.
   179  					doneNow := doneCount
   180  					pendingNow := jobsNow - doneNow
   181  					elapsed := time.Since(startTime)
   182  					rate := float64(doneNow) / elapsed.Seconds()
   183  					log.Log(rt.INFO, "%d jobs created, %d done, %d pending after %v (%.0f/s)",
   184  						jobsNow, doneNow, pendingNow, elapsed, rate)
   185  				case <-workersFinished:
   186  					return
   187  				case <-ctx.Done():
   188  					return
   189  				}
   190  			}
   191  		}()
   192  	}
   193  
   194  	// Close result channel when workers are done sending to it.
   195  	grp.Go(func() error {
   196  		workerWg.Wait()
   197  		close(jobResultCh)
   198  		close(workersFinished)
   199  		log.Log(rt.INFO, "All workers done after %v", time.Since(startTime))
   200  		return nil
   201  	})
   202  
   203  	// Insert migrated records in output state tree and accumulators.
   204  	grp.Go(func() error {
   205  		log.Log(rt.INFO, "Result writer started")
   206  		resultCount := 0
   207  		for result := range jobResultCh {
   208  			if err := actorsOut.SetActor(result.Address, &result.Actor); err != nil {
   209  				return err
   210  			}
   211  			resultCount++
   212  		}
   213  		log.Log(rt.INFO, "Result writer wrote %d results to state tree after %v", resultCount, time.Since(startTime))
   214  		return nil
   215  	})
   216  
   217  	if err := grp.Wait(); err != nil {
   218  		return cid.Undef, err
   219  	}
   220  
   221  	// Perform any deferred migrations explicitly here.
   222  	// Deferred migrations might depend on values accumulated through migration of other actors.
   223  
   224  	elapsed := time.Since(startTime)
   225  	rate := float64(doneCount) / elapsed.Seconds()
   226  	log.Log(rt.INFO, "All %d done after %v (%.0f/s). Flushing state tree root.", doneCount, elapsed, rate)
   227  	return actorsOut.Flush()
   228  }
   229  
   230  type actorMigrationInput struct {
   231  	address    address.Address // actor's address
   232  	balance    abi.TokenAmount // actor's balance
   233  	head       cid.Cid         // actor's state head CID
   234  	priorEpoch abi.ChainEpoch  // epoch of last state transition prior to migration
   235  	cache      MigrationCache  // cache of existing cid -> cid migrations for this actor
   236  }
   237  
   238  type actorMigrationResult struct {
   239  	newCodeCID cid.Cid
   240  	newHead    cid.Cid
   241  }
   242  
   243  type actorMigration interface {
   244  	// Loads an actor's state from an input store and writes new state to an output store.
   245  	// Returns the new state head CID.
   246  	migrateState(ctx context.Context, store cbor.IpldStore, input actorMigrationInput) (result *actorMigrationResult, err error)
   247  	migratedCodeCID() cid.Cid
   248  }
   249  
   250  type migrationJob struct {
   251  	address.Address
   252  	states2.Actor
   253  	actorMigration
   254  	cache MigrationCache
   255  }
   256  type migrationJobResult struct {
   257  	address.Address
   258  	states3.Actor
   259  }
   260  
   261  func (job *migrationJob) run(ctx context.Context, store cbor.IpldStore, priorEpoch abi.ChainEpoch) (*migrationJobResult, error) {
   262  	result, err := job.migrateState(ctx, store, actorMigrationInput{
   263  		address:    job.Address,
   264  		balance:    job.Actor.Balance,
   265  		head:       job.Actor.Head,
   266  		priorEpoch: priorEpoch,
   267  		cache:      job.cache,
   268  	})
   269  	if err != nil {
   270  		return nil, xerrors.Errorf("state migration failed for %s actor, addr %s: %w",
   271  			builtin2.ActorNameByCode(job.Actor.Code), job.Address, err)
   272  	}
   273  
   274  	// Set up new actor record with the migrated state.
   275  	return &migrationJobResult{
   276  		job.Address, // Unchanged
   277  		states3.Actor{
   278  			Code:       result.newCodeCID,
   279  			Head:       result.newHead,
   280  			CallSeqNum: job.Actor.CallSeqNum, // Unchanged
   281  			Balance:    job.Actor.Balance,    // Unchanged
   282  		},
   283  	}, nil
   284  }
   285  
   286  // Migrator which preserves the head CID and provides a fixed result code CID.
   287  type nilMigrator struct {
   288  	OutCodeCID cid.Cid
   289  }
   290  
   291  func (n nilMigrator) migrateState(_ context.Context, _ cbor.IpldStore, in actorMigrationInput) (*actorMigrationResult, error) {
   292  	return &actorMigrationResult{
   293  		newCodeCID: n.OutCodeCID,
   294  		newHead:    in.head,
   295  	}, nil
   296  }
   297  
   298  func (n nilMigrator) migratedCodeCID() cid.Cid {
   299  	return n.OutCodeCID
   300  }
   301  
   302  // Migrator that uses cached transformation if it exists
   303  type cachedMigrator struct {
   304  	cache MigrationCache
   305  	actorMigration
   306  }
   307  
   308  func (c cachedMigrator) migrateState(ctx context.Context, store cbor.IpldStore, in actorMigrationInput) (*actorMigrationResult, error) {
   309  	newHead, err := c.cache.Load(ActorHeadKey(in.address, in.head), func() (cid.Cid, error) {
   310  		result, err := c.actorMigration.migrateState(ctx, store, in)
   311  		if err != nil {
   312  			return cid.Undef, err
   313  		}
   314  		return result.newHead, nil
   315  	})
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  	return &actorMigrationResult{
   320  		newCodeCID: c.migratedCodeCID(),
   321  		newHead:    newHead,
   322  	}, nil
   323  }
   324  
   325  func cachedMigration(cache MigrationCache, m actorMigration) actorMigration {
   326  	return cachedMigrator{
   327  		actorMigration: m,
   328  		cache:          cache,
   329  	}
   330  }