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

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