github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/util/ledger/migrations/fix_broken_data_migration.go (about) 1 package migrations 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 "sync" 8 "time" 9 10 "github.com/onflow/cadence/migrations" 11 "github.com/rs/zerolog" 12 13 "github.com/onflow/atree" 14 15 "github.com/onflow/cadence/runtime/common" 16 17 "github.com/onflow/flow-go/cmd/util/ledger/reporters" 18 "github.com/onflow/flow-go/cmd/util/ledger/util" 19 "github.com/onflow/flow-go/cmd/util/ledger/util/registers" 20 "github.com/onflow/flow-go/ledger" 21 "github.com/onflow/flow-go/ledger/common/convert" 22 "github.com/onflow/flow-go/model/flow" 23 ) 24 25 type FixSlabsWithBrokenReferencesMigration struct { 26 log zerolog.Logger 27 rw reporters.ReportWriter 28 outputDir string 29 accountsToFix map[common.Address]struct{} 30 nWorkers int 31 mutex sync.Mutex 32 brokenPayloads []*ledger.Payload 33 payloadsFile string 34 } 35 36 var _ AccountBasedMigration = &FixSlabsWithBrokenReferencesMigration{} 37 38 const fixSlabsWithBrokenReferencesName = "fix-slabs-with-broken-references" 39 40 func NewFixBrokenReferencesInSlabsMigration( 41 outputDir string, 42 rwf reporters.ReportWriterFactory, 43 accountsToFix map[common.Address]struct{}, 44 ) *FixSlabsWithBrokenReferencesMigration { 45 return &FixSlabsWithBrokenReferencesMigration{ 46 outputDir: outputDir, 47 rw: rwf.ReportWriter(fixSlabsWithBrokenReferencesName), 48 accountsToFix: accountsToFix, 49 brokenPayloads: make([]*ledger.Payload, 0, 10), 50 } 51 } 52 53 func (m *FixSlabsWithBrokenReferencesMigration) InitMigration( 54 log zerolog.Logger, 55 _ *registers.ByAccount, 56 nWorkers int, 57 ) error { 58 m.log = log. 59 With(). 60 Str("migration", fixSlabsWithBrokenReferencesName). 61 Logger() 62 m.nWorkers = nWorkers 63 64 return nil 65 } 66 67 func (m *FixSlabsWithBrokenReferencesMigration) MigrateAccount( 68 _ context.Context, 69 address common.Address, 70 accountRegisters *registers.AccountRegisters, 71 ) error { 72 73 if _, exist := m.accountsToFix[address]; !exist { 74 return nil 75 } 76 77 migrationRuntime := NewBasicMigrationRuntime(accountRegisters) 78 storage := migrationRuntime.Storage 79 80 // Load all atree registers in storage 81 err := loadAtreeSlabsInStorage(storage, accountRegisters, m.nWorkers) 82 if err != nil { 83 return err 84 } 85 86 // Fix broken references 87 fixedStorageIDs, skippedStorageIDs, err := 88 storage.FixLoadedBrokenReferences(migrations.ShouldFixBrokenCompositeKeyedDictionary) 89 if err != nil { 90 return err 91 } 92 93 if len(skippedStorageIDs) > 0 { 94 m.log.Warn(). 95 Str("account", address.HexWithPrefix()). 96 Msgf("skipped slabs with broken references: %v", skippedStorageIDs) 97 } 98 99 if len(fixedStorageIDs) == 0 { 100 m.log.Warn(). 101 Str("account", address.HexWithPrefix()). 102 Msgf("did not fix any slabs with broken references") 103 104 return nil 105 } 106 107 m.log.Log(). 108 Str("account", address.HexWithPrefix()). 109 Msgf("fixed %d slabs with broken references", len(fixedStorageIDs)) 110 111 // Save broken payloads to save to payload file later 112 brokenPayloads, err := getAtreePayloadsByID(accountRegisters, fixedStorageIDs) 113 if err != nil { 114 return err 115 } 116 117 m.mergeBrokenPayloads(brokenPayloads) 118 119 err = storage.NondeterministicFastCommit(m.nWorkers) 120 if err != nil { 121 return fmt.Errorf("failed to commit storage: %w", err) 122 } 123 124 // Finalize the transaction 125 result, err := migrationRuntime.TransactionState.FinalizeMainTransaction() 126 if err != nil { 127 return fmt.Errorf("failed to finalize main transaction: %w", err) 128 } 129 130 // Merge the changes to the original payloads. 131 expectedAddresses := map[flow.Address]struct{}{ 132 flow.Address(address): {}, 133 } 134 135 err = registers.ApplyChanges( 136 accountRegisters, 137 result.WriteSet, 138 expectedAddresses, 139 m.log, 140 ) 141 if err != nil { 142 return fmt.Errorf("failed to apply changes to account registers: %w", err) 143 } 144 145 // Log fixed payloads 146 fixedPayloads, err := getAtreePayloadsByID(accountRegisters, fixedStorageIDs) 147 if err != nil { 148 return err 149 } 150 151 m.rw.Write(fixedSlabsWithBrokenReferences{ 152 Account: address.Hex(), 153 BrokenPayloads: brokenPayloads, 154 FixedPayloads: fixedPayloads, 155 }) 156 157 return nil 158 } 159 160 func (m *FixSlabsWithBrokenReferencesMigration) mergeBrokenPayloads(payloads []*ledger.Payload) { 161 m.mutex.Lock() 162 defer m.mutex.Unlock() 163 164 m.brokenPayloads = append(m.brokenPayloads, payloads...) 165 } 166 167 func (m *FixSlabsWithBrokenReferencesMigration) Close() error { 168 // close the report writer so it flushes to file 169 m.rw.Close() 170 171 err := m.writeBrokenPayloads() 172 if err != nil { 173 return fmt.Errorf("failed to write broken payloads to file: %w", err) 174 } 175 176 return nil 177 } 178 179 func (m *FixSlabsWithBrokenReferencesMigration) writeBrokenPayloads() error { 180 181 m.payloadsFile = path.Join( 182 m.outputDir, 183 fmt.Sprintf("broken_%d.payloads", int32(time.Now().Unix())), 184 ) 185 186 writtenPayloadCount, err := util.CreatePayloadFile( 187 m.log, 188 m.payloadsFile, 189 m.brokenPayloads, 190 nil, 191 true, 192 ) 193 194 if err != nil { 195 return fmt.Errorf("failed to write all broken payloads to file: %w", err) 196 } 197 198 if writtenPayloadCount != len(m.brokenPayloads) { 199 return fmt.Errorf( 200 "failed to write all broken payloads to file: expected %d, got %d", 201 len(m.brokenPayloads), 202 writtenPayloadCount, 203 ) 204 } 205 206 return nil 207 } 208 209 func getAtreePayloadsByID( 210 registers *registers.AccountRegisters, 211 ids map[atree.SlabID][]atree.SlabID, 212 ) ( 213 []*ledger.Payload, 214 error, 215 ) { 216 outputPayloads := make([]*ledger.Payload, 0, len(ids)) 217 218 err := registers.ForEach(func(owner string, key string, value []byte) error { 219 220 if !flow.IsSlabIndexKey(key) { 221 return nil 222 } 223 224 slabID := atree.NewSlabID( 225 atree.Address([]byte(owner)), 226 atree.SlabIndex([]byte(key[1:])), 227 ) 228 229 _, ok := ids[slabID] 230 if !ok { 231 return nil 232 } 233 234 ledgerKey := convert.RegisterIDToLedgerKey(flow.RegisterID{ 235 Owner: owner, 236 Key: key, 237 }) 238 payload := ledger.NewPayload(ledgerKey, value) 239 outputPayloads = append(outputPayloads, payload) 240 241 return nil 242 }) 243 if err != nil { 244 return nil, err 245 } 246 247 return outputPayloads, nil 248 } 249 250 type fixedSlabsWithBrokenReferences struct { 251 Account string `json:"account"` 252 BrokenPayloads []*ledger.Payload `json:"broken_payloads"` 253 FixedPayloads []*ledger.Payload `json:"fixed_payloads"` 254 }