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  }