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 }