github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/migrate/progress.go (about) 1 // Copyright 2022 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package migrate 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "time" 22 23 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable" 24 "github.com/dolthub/dolt/go/libraries/doltcore/ref" 25 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 26 "github.com/dolthub/dolt/go/store/datas" 27 28 "github.com/dolthub/dolt/go/cmd/dolt/cli" 29 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 30 "github.com/dolthub/dolt/go/store/chunks" 31 "github.com/dolthub/dolt/go/store/hash" 32 "github.com/dolthub/dolt/go/store/pool" 33 "github.com/dolthub/dolt/go/store/prolly" 34 "github.com/dolthub/dolt/go/store/prolly/shim" 35 "github.com/dolthub/dolt/go/store/prolly/tree" 36 "github.com/dolthub/dolt/go/store/types" 37 "github.com/dolthub/dolt/go/store/val" 38 ) 39 40 const ( 41 MigratedCommitsBranch = "dolt_migrated_commits" 42 MigratedCommitsTable = "dolt_commit_mapping" 43 ) 44 45 var ( 46 mappingSchema, _ = schema.SchemaFromCols(schema.NewColCollection( 47 schema.NewColumn("old_commit_hash", 0, types.StringKind, true), 48 schema.NewColumn("new_commit_hash", 1, types.StringKind, false), 49 )) 50 desc = val.NewTupleDescriptor(val.Type{Enc: val.StringEnc, Nullable: false}) 51 ) 52 53 // progress maintains the state of migration. 54 type progress struct { 55 stack []*doltdb.Commit 56 57 // mapping tracks migrated commits 58 // it maps old commit hash to new hash 59 mapping *prolly.MutableMap 60 kb, vb *val.TupleBuilder 61 buffPool pool.BuffPool 62 63 vs *types.ValueStore 64 cs chunks.ChunkStore 65 } 66 67 func newProgress(ctx context.Context, cs chunks.ChunkStore) (*progress, error) { 68 kd := val.NewTupleDescriptor(val.Type{ 69 Enc: val.ByteStringEnc, 70 Nullable: false, 71 }) 72 vd := val.NewTupleDescriptor(val.Type{ 73 Enc: val.ByteStringEnc, 74 Nullable: false, 75 }) 76 77 ns := tree.NewNodeStore(cs) 78 vs := types.NewValueStore(cs) 79 80 mapping, err := prolly.NewMapFromTuples(ctx, ns, kd, vd) 81 if err != nil { 82 return nil, err 83 } 84 85 mut := mapping.Mutate() 86 kb := val.NewTupleBuilder(kd) 87 vb := val.NewTupleBuilder(vd) 88 89 return &progress{ 90 stack: make([]*doltdb.Commit, 0, 128), 91 mapping: mut, 92 kb: kb, 93 vb: vb, 94 buffPool: ns.Pool(), 95 vs: vs, 96 cs: cs, 97 }, nil 98 } 99 100 func (p *progress) Has(ctx context.Context, addr hash.Hash) (ok bool, err error) { 101 p.kb.PutByteString(0, addr[:]) 102 k := p.kb.Build(p.buffPool) 103 return p.mapping.Has(ctx, k) 104 } 105 106 func (p *progress) Get(ctx context.Context, old hash.Hash) (new hash.Hash, err error) { 107 p.kb.PutByteString(0, old[:]) 108 k := p.kb.Build(p.buffPool) 109 err = p.mapping.Get(ctx, k, func(_, v val.Tuple) error { 110 if len(v) > 0 { 111 n, ok := p.vb.Desc.GetBytes(0, v) 112 if !ok { 113 return fmt.Errorf("failed to get string address from commit mapping value") 114 } 115 new = hash.New(n) 116 } 117 return nil 118 }) 119 return 120 } 121 122 func (p *progress) Put(ctx context.Context, old, new hash.Hash) (err error) { 123 p.kb.PutByteString(0, old[:]) 124 k := p.kb.Build(p.buffPool) 125 p.vb.PutByteString(0, new[:]) 126 v := p.vb.Build(p.buffPool) 127 err = p.mapping.Put(ctx, k, v) 128 return 129 } 130 131 func (p *progress) Push(ctx context.Context, cm *doltdb.Commit) (err error) { 132 p.stack = append(p.stack, cm) 133 return 134 } 135 136 func (p *progress) Pop(ctx context.Context) (cm *doltdb.Commit, err error) { 137 if len(p.stack) == 0 { 138 return nil, nil 139 } 140 top := len(p.stack) - 1 141 cm = p.stack[top] 142 p.stack = p.stack[:top] 143 return 144 } 145 146 func (p *progress) Log(ctx context.Context, format string, args ...any) { 147 cli.Println(time.Now().UTC().String() + " " + fmt.Sprintf(format, args...)) 148 } 149 150 func (p *progress) Finalize(ctx context.Context) (prolly.Map, error) { 151 m, err := p.mapping.Map(ctx) 152 if err != nil { 153 return prolly.Map{}, err 154 } 155 v := shim.ValueFromMap(m) 156 ref, err := p.vs.WriteValue(ctx, v) 157 if err != nil { 158 return prolly.Map{}, err 159 } 160 last, err := p.vs.Root(ctx) 161 if err != nil { 162 return prolly.Map{}, err 163 } 164 ok, err := p.vs.Commit(ctx, last, last) 165 if err != nil { 166 return prolly.Map{}, err 167 } else if !ok { 168 return prolly.Map{}, fmt.Errorf("failed to commit, manifest swapped out beneath us") 169 } 170 171 p.Log(ctx, "Wrote commit mapping!! [commit_mapping_ref: %s]", ref.TargetHash().String()) 172 p.Log(ctx, "Commit mapping allow mapping pre-migration commit hashes to post-migration commit hashes, "+ 173 "it is available on branch '%s' in table '%s'", MigratedCommitsBranch, MigratedCommitsTable) 174 return m, nil 175 } 176 177 func persistMigratedCommitMapping(ctx context.Context, ddb *doltdb.DoltDB, mapping prolly.Map) error { 178 // create a new branch to persist the migrated commit mapping 179 init, err := ddb.ResolveCommitRef(ctx, ref.NewInternalRef(doltdb.CreationBranch)) 180 if err != nil { 181 return err 182 } 183 184 br := ref.NewBranchRef(MigratedCommitsBranch) 185 err = ddb.NewBranchAtCommit(ctx, br, init, nil) 186 if err != nil { 187 return err 188 } 189 190 ns, vrw := ddb.NodeStore(), ddb.ValueReadWriter() 191 m, err := prolly.NewMapFromTuples(ctx, ns, desc, desc) 192 if err != nil { 193 return err 194 } 195 196 rows := m.Mutate() 197 bld := val.NewTupleBuilder(desc) 198 199 // convert |mapping| values from hash.Hash to string 200 iter, err := mapping.IterAll(ctx) 201 if err != nil { 202 return err 203 } 204 205 var k, v val.Tuple 206 kd, vd := mapping.Descriptors() 207 for { 208 k, v, err = iter.Next(ctx) 209 if err == io.EOF { 210 break 211 } else if err != nil { 212 return err 213 } 214 215 o, _ := kd.GetBytes(0, k) 216 bld.PutString(0, hash.New(o).String()) 217 key := bld.Build(ddb.NodeStore().Pool()) 218 219 n, _ := vd.GetBytes(0, v) 220 bld.PutString(0, hash.New(n).String()) 221 value := bld.Build(ddb.NodeStore().Pool()) 222 223 if err = rows.Put(ctx, key, value); err != nil { 224 return err 225 } 226 } 227 228 m, err = rows.Map(ctx) 229 if err != nil { 230 return err 231 } 232 idx := durable.IndexFromProllyMap(m) 233 234 tbl, err := doltdb.NewTable(ctx, vrw, ns, mappingSchema, idx, nil, nil) 235 if err != nil { 236 return err 237 } 238 239 root, err := init.GetRootValue(ctx) 240 if err != nil { 241 return err 242 } 243 244 root, err = root.PutTable(ctx, doltdb.TableName{Name: MigratedCommitsTable}, tbl) 245 if err != nil { 246 return err 247 } 248 249 return commitRoot(ctx, ddb, br, root, init) 250 } 251 252 func commitRoot( 253 ctx context.Context, 254 ddb *doltdb.DoltDB, 255 br ref.BranchRef, 256 root doltdb.RootValue, 257 parent *doltdb.Commit, 258 ) error { 259 roots := doltdb.Roots{ 260 Head: root, 261 Working: root, 262 Staged: root, 263 } 264 parents := []*doltdb.Commit{parent} 265 266 meta, err := parent.GetCommitMeta(ctx) 267 if err != nil { 268 return err 269 } 270 271 meta, err = datas.NewCommitMeta(meta.Name, meta.Email, meta.Description) 272 if err != nil { 273 return err 274 } 275 276 pcm, err := ddb.NewPendingCommit(ctx, roots, parents, meta) 277 if err != nil { 278 return err 279 } 280 281 wsr, err := ref.WorkingSetRefForHead(br) 282 if err != nil { 283 return err 284 } 285 286 ws, err := ddb.ResolveWorkingSet(ctx, wsr) 287 if err != nil { 288 return err 289 } 290 291 prev, err := ws.HashOf() 292 if err != nil { 293 return err 294 } 295 ws = ws.WithWorkingRoot(root).WithStagedRoot(root) 296 297 _, err = ddb.CommitWithWorkingSet(ctx, br, wsr, pcm, ws, prev, &datas.WorkingSetMeta{ 298 Name: meta.Name, 299 Email: meta.Email, 300 Timestamp: uint64(time.Now().Unix()), 301 }, nil) 302 return err 303 }