Optimize commit iterator (#9751)

This commit is contained in:
James Cor
2025-08-29 17:13:57 -07:00
committed by GitHub
parent c4c8b19649
commit 5fa69b5a30
20 changed files with 236 additions and 102 deletions

View File

@@ -250,7 +250,7 @@ func historicalFuzzyMatching(ctx context.Context, heads hash.HashSet, groupings
return err
}
for {
h, _, err := iterator.Next(ctx)
h, _, _, _, err := iterator.Next(ctx)
if err != nil {
if err == io.EOF {
break

View File

@@ -28,9 +28,11 @@ import (
// CommitItr is an interface for iterating over a set of unique commits
type CommitItr[C Context] interface {
// Next returns the hash of the next commit, and a pointer to that commit. Implementations of Next must handle
// making sure the list of commits returned are unique. When complete Next will return hash.Hash{}, nil, io.EOF
Next(ctx C) (hash.Hash, *OptionalCommit, error)
// Next returns the next commit's hash, pointer, metadata, and height.
// Implementations of Next must handle making sure the list of commits returned are unique.
// When complete Next will return hash.Hash{}, nil, nil, 0, io.EOF
// Note: Some implementations may always return nil for the metadata and height return values.
Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error)
// Reset the commit iterator back to the start
Reset(ctx context.Context) error
@@ -88,25 +90,27 @@ func (cmItr *commitItr[C]) Reset(ctx context.Context) error {
return nil
}
// Next returns the hash of the next commit, and a pointer to that commit. It handles making sure the list of commits
// returned are unique. When complete Next will return hash.Hash{}, nil, io.EOF
func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) {
// Next returns the hash of the next commit, and a pointer to that commit.
// It handles making sure the list of commits returned are unique.
// When complete Next will return hash.Hash{}, nil, nil, 0, io.EOF.
// Note: This implementation always returns nil for the metadata and height return values.
func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) {
for cmItr.curr == nil {
if cmItr.currentRoot >= len(cmItr.rootCommits) {
return hash.Hash{}, nil, io.EOF
return hash.Hash{}, nil, nil, 0, io.EOF
}
cm := cmItr.rootCommits[cmItr.currentRoot]
h, err := cm.HashOf()
if err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
if !cmItr.added[h] {
cmItr.added[h] = true
cmItr.curr = cm
return h, &OptionalCommit{cmItr.curr, h}, nil
return h, &OptionalCommit{cmItr.curr, h}, nil, 0, nil
}
cmItr.currentRoot++
@@ -115,7 +119,7 @@ func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) {
parents, err := cmItr.curr.ParentHashes(ctx)
if err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
for _, h := range parents {
@@ -137,13 +141,13 @@ func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) {
cmItr.unprocessed = cmItr.unprocessed[:numUnprocessed-1]
cmItr.curr, err = HashToCommit(ctx, cmItr.ddb.ValueReadWriter(), cmItr.ddb.ns, next)
if err != nil && err != ErrGhostCommitEncountered {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
if err == ErrGhostCommitEncountered {
cmItr.curr = nil
}
return next, &OptionalCommit{cmItr.curr, next}, nil
return next, &OptionalCommit{cmItr.curr, next}, nil, 0, nil
}
func HashToCommit(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, h hash.Hash) (*Commit, error) {
@@ -183,19 +187,18 @@ func NewFilteringCommitItr[C Context](itr CommitItr[C], filter CommitFilter[C])
// Next returns the hash of the next commit, and a pointer to that commit. Implementations of Next must handle
// making sure the list of commits returned are unique. When complete Next will return hash.Hash{}, nil, io.EOF
func (itr FilteringCommitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) {
func (itr FilteringCommitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) {
// iteration will terminate on io.EOF or a commit that is !filteredOut
for {
h, cm, err := itr.itr.Next(ctx)
h, cm, meta, height, err := itr.itr.Next(ctx)
if err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
if filterOut, err := itr.filter(ctx, h, cm); err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
} else if !filterOut {
return h, cm, nil
return h, cm, meta, height, nil
}
}
}
@@ -217,12 +220,14 @@ type CommitSliceIter[C Context] struct {
var _ CommitItr[context.Context] = (*CommitSliceIter[context.Context])(nil)
func (i *CommitSliceIter[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) {
// Next implements the CommitItr interface.
// Note: This implementation always returns nil for the metadata and height return values.
func (i *CommitSliceIter[C]) Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) {
if i.i >= len(i.h) {
return hash.Hash{}, nil, io.EOF
return hash.Hash{}, nil, nil, 0, io.EOF
}
i.i++
return i.h[i.i-1], &OptionalCommit{i.cm[i.i-1], i.h[i.i-1]}, nil
return i.h[i.i-1], &OptionalCommit{i.cm[i.i-1], i.h[i.i-1]}, nil, 0, nil
}
@@ -232,7 +237,7 @@ func (i *CommitSliceIter[C]) Reset(ctx context.Context) error {
}
func NewOneCommitIter[C Context](cm *Commit, h hash.Hash, meta *datas.CommitMeta) *OneCommitIter[C] {
return &OneCommitIter[C]{cm: &OptionalCommit{cm, h}, h: h}
return &OneCommitIter[C]{cm: &OptionalCommit{cm, h}, h: h, m: meta}
}
type OneCommitIter[C Context] struct {
@@ -244,13 +249,14 @@ type OneCommitIter[C Context] struct {
var _ CommitItr[context.Context] = (*OneCommitIter[context.Context])(nil)
func (i *OneCommitIter[C]) Next(_ C) (hash.Hash, *OptionalCommit, error) {
// Next implements the CommitItr interface.
// Note: This implementation always returns nil for the metadata and height return values.
func (i *OneCommitIter[C]) Next(_ C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) {
if i.done {
return hash.Hash{}, nil, io.EOF
return hash.Hash{}, nil, nil, 0, io.EOF
}
i.done = true
return i.h, i.cm, nil
return i.h, i.cm, i.m, 0, nil
}
func (i *OneCommitIter[C]) Reset(_ context.Context) error {

View File

@@ -474,6 +474,23 @@ func (ddb *DoltDB) Resolve(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef)
return commit.GetAncestor(ctx, cs.aSpec)
}
// ResolveHash takes a hash and returns an OptionalCommit directly.
// This assumes no ancestor spec resolution and no current working branch (cwb) needed.
func (ddb *DoltDB) ResolveHash(ctx context.Context, hash hash.Hash) (*OptionalCommit, error) {
commitValue, err := datas.LoadCommitAddr(ctx, ddb.vrw, hash)
if err != nil {
return nil, err
}
if commitValue.IsGhost() {
return &OptionalCommit{nil, hash}, nil
}
commit, err := NewCommit(ctx, ddb.vrw, ddb.ns, commitValue)
if err != nil {
return nil, err
}
return &OptionalCommit{commit, hash}, nil
}
// BootstrapShallowResolve is a special case of Resolve that is used to resolve a commit prior to pulling it's history
// in a shallow clone. In general, application code should call Resolve and get an OptionalCommit. This is a special case
// where we need to get the head commit for the commit closure used to determine what commits should skipped.

View File

@@ -69,23 +69,25 @@ func (q *q) Swap(i, j int) {
// the commit with the newer timestamp is "less". Finally if both commits are ghost commits, we don't really have enough
// information to compare on, so we just compare the hashes to ensure that the results are stable.
func (q *q) Less(i, j int) bool {
_, okI := q.pending[i].commit.ToCommit()
_, okJ := q.pending[i].commit.ToCommit()
cI := q.pending[i]
cJ := q.pending[j]
_, okI := cI.commit.ToCommit()
_, okJ := cJ.commit.ToCommit()
if !okI && okJ {
return true
} else if okI && !okJ {
return false
} else if !okI && !okJ {
return q.pending[i].hash.String() < q.pending[j].hash.String()
return cI.hash.String() < cJ.hash.String()
}
if q.pending[i].height > q.pending[j].height {
if cI.height > cJ.height {
return true
}
if q.pending[i].height == q.pending[j].height {
return q.pending[i].meta.UserTimestamp > q.pending[j].meta.UserTimestamp
if cI.height == cJ.height {
return cI.meta.UserTimestamp > cJ.meta.UserTimestamp
}
return false
}
@@ -128,15 +130,11 @@ func (q *q) SetInvisible(ctx context.Context, ddb *doltdb.DoltDB, id hash.Hash)
}
func load(ctx context.Context, ddb *doltdb.DoltDB, h hash.Hash) (*doltdb.OptionalCommit, error) {
cs, err := doltdb.NewCommitSpec(h.String())
oc, err := ddb.ResolveHash(ctx, h)
if err != nil {
return nil, err
}
c, err := ddb.Resolve(ctx, cs, nil)
if err != nil {
return nil, err
}
return c, nil
return oc, nil
}
func (q *q) Get(ctx context.Context, ddb *doltdb.DoltDB, id hash.Hash) (*c, error) {
@@ -158,14 +156,21 @@ func (q *q) Get(ctx context.Context, ddb *doltdb.DoltDB, id hash.Hash) (*c, erro
if err != nil {
return nil, err
}
meta, err := commit.GetCommitMeta(ctx)
if err != nil {
return nil, err
}
c := &c{ddb: ddb, commit: &doltdb.OptionalCommit{Commit: commit, Addr: id}, meta: meta, height: h, hash: id}
q.loaded[id] = c
return c, nil
lc := &c{
ddb: ddb,
commit: &doltdb.OptionalCommit{Commit: commit, Addr: id},
meta: meta,
height: h,
hash: id,
}
q.loaded[id] = lc
return lc, nil
}
func newQueue() *q {
@@ -190,7 +195,7 @@ func GetDotDotRevisions(ctx context.Context, includedDB *doltdb.DoltDB, included
var commitList []*doltdb.OptionalCommit
for num < 0 || len(commitList) < num {
_, commit, err := itr.Next(ctx)
_, commit, _, _, err := itr.Next(ctx)
if err == io.EOF {
break
} else if err != nil {
@@ -234,43 +239,35 @@ func newCommiterator[C doltdb.Context](ctx context.Context, ddb *doltdb.DoltDB,
}
// Next implements doltdb.CommitItr
func (iter *commiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, error) {
func (iter *commiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, *datas.CommitMeta, uint64, error) {
if iter.q.NumVisiblePending() > 0 {
nextC := iter.q.PopPending()
var err error
parents := []hash.Hash{}
commit, ok := nextC.commit.ToCommit()
if ok {
parents, err = commit.ParentHashes(ctx)
if err != nil {
return hash.Hash{}, nil, err
}
}
for _, parentID := range parents {
if err := iter.q.AddPendingIfUnseen(ctx, nextC.ddb, parentID); err != nil {
return hash.Hash{}, nil, err
for _, parentCommit := range commit.DatasParents() {
if err := iter.q.AddPendingIfUnseen(ctx, nextC.ddb, parentCommit.Addr()); err != nil {
return hash.Hash{}, nil, nil, 0, err
}
}
}
var err error
matches := true
if iter.matchFn != nil {
matches, err = iter.matchFn(nextC.commit)
if err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
}
if matches {
return nextC.hash, &doltdb.OptionalCommit{Commit: commit, Addr: nextC.hash}, nil
return nextC.hash, &doltdb.OptionalCommit{Commit: commit, Addr: nextC.hash}, nextC.meta, nextC.height, nil
}
return iter.Next(ctx)
}
return hash.Hash{}, nil, io.EOF
return hash.Hash{}, nil, nil, 0, io.EOF
}
// Reset implements doltdb.CommitItr
@@ -301,7 +298,7 @@ func GetTopNTopoOrderedCommitsMatching(ctx context.Context, ddb *doltdb.DoltDB,
var commitList []*doltdb.Commit
for n < 0 || len(commitList) < n {
_, optCmt, err := itr.Next(ctx)
_, optCmt, _, _, err := itr.Next(ctx)
if err == io.EOF {
break
} else if err != nil {
@@ -345,28 +342,28 @@ func newDotDotCommiterator[C doltdb.Context](ctx context.Context, includedDdb *d
}
// Next implements doltdb.CommitItr
func (i *dotDotCommiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, error) {
func (i *dotDotCommiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, *datas.CommitMeta, uint64, error) {
if i.q.NumVisiblePending() > 0 {
nextC := i.q.PopPending()
commit, ok := nextC.commit.ToCommit()
if !ok {
return nextC.hash, nextC.commit, nil
return nextC.hash, nextC.commit, nextC.meta, nextC.height, nil
}
parents, err := commit.ParentHashes(ctx)
if err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
for _, parentID := range parents {
if nextC.invisible {
if err := i.q.SetInvisible(ctx, nextC.ddb, parentID); err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
}
if err := i.q.AddPendingIfUnseen(ctx, nextC.ddb, parentID); err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
}
@@ -374,18 +371,18 @@ func (i *dotDotCommiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit,
if i.matchFn != nil {
matches, err = i.matchFn(nextC.commit)
if err != nil {
return hash.Hash{}, nil, err
return hash.Hash{}, nil, nil, 0, err
}
}
// If not invisible, return commit. Otherwise get next commit
// If not invisible, return commit. Otherwise, get next commit
if !nextC.invisible && matches {
return nextC.hash, nextC.commit, nil
return nextC.hash, nextC.commit, nextC.meta, nextC.height, nil
}
return i.Next(ctx)
}
return hash.Hash{}, nil, io.EOF
return hash.Hash{}, nil, nil, 0, io.EOF
}
// Reset implements doltdb.CommitItr

View File

@@ -200,7 +200,7 @@ func findRebaseCommits(ctx *sql.Context, currentBranchCommit, upstreamBranchComm
// Drain the iterator into a slice so that we can easily reverse the order of the commits
// so that the oldest commit is first in the generated rebase plan.
for {
_, optCmt, err := commitItr.Next(ctx)
_, optCmt, _, _, err := commitItr.Next(ctx)
if err == io.EOF {
return commits, nil
} else if err != nil {

View File

@@ -919,7 +919,7 @@ func resolveAsOfTime(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, asO
}
for {
_, optCmt, err := cmItr.Next(ctx)
_, optCmt, meta, _, err := cmItr.Next(ctx)
if err == io.EOF {
break
} else if err != nil {
@@ -930,9 +930,11 @@ func resolveAsOfTime(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, asO
return nil, nil, doltdb.ErrGhostCommitEncountered
}
meta, err := curr.GetCommitMeta(ctx)
if err != nil {
return nil, nil, err
if meta == nil {
meta, err = curr.GetCommitMeta(ctx)
if err != nil {
return nil, nil, err
}
}
if meta.Time().Equal(asOf) || meta.Time().Before(asOf) {

View File

@@ -146,7 +146,7 @@ var _ sql.PartitionIter = (*DoltProceduresDiffPartitionItr)(nil)
func (dpdp *DoltProceduresDiffPartitionItr) Next(ctx *sql.Context) (sql.Partition, error) {
// First iterate through commit history, then add working partition as the final step
for {
cmHash, optCmt, err := dpdp.cmItr.Next(ctx)
cmHash, optCmt, _, _, err := dpdp.cmItr.Next(ctx)
if err == io.EOF {
// Finished with commit history, now add working partition if not done
if !dpdp.workingPartitionDone {

View File

@@ -146,7 +146,7 @@ var _ sql.PartitionIter = (*DoltSchemasDiffPartitionItr)(nil)
func (dsdp *DoltSchemasDiffPartitionItr) Next(ctx *sql.Context) (sql.Partition, error) {
// First iterate through commit history, then add working partition as the final step
for {
cmHash, optCmt, err := dsdp.cmItr.Next(ctx)
cmHash, optCmt, _, _, err := dsdp.cmItr.Next(ctx)
if err == io.EOF {
// Finished with commit history, now add working partition if not done
if !dsdp.workingPartitionDone {

View File

@@ -146,7 +146,7 @@ func countCommitsInRange(ctx context.Context, ddb *doltdb.DoltDB, startCommitHas
}
count := 0
for {
nextHash, _, err := itr.Next(ctx)
nextHash, _, _, _, err := itr.Next(ctx)
if err == io.EOF {
return 0, fmt.Errorf("no match found to ancestor commit")
} else if err != nil {

View File

@@ -30,6 +30,7 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/utils/gpg"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/hash"
)
@@ -688,9 +689,11 @@ func (itr *logTableFunctionRowIter) Next(ctx *sql.Context) (sql.Row, error) {
var commitHash hash.Hash
var commit *doltdb.Commit
var optCmt *doltdb.OptionalCommit
var meta *datas.CommitMeta
var height uint64
var err error
for {
commitHash, optCmt, err = itr.child.Next(ctx)
commitHash, optCmt, meta, height, err = itr.child.Next(ctx)
if err != nil {
return nil, err
}
@@ -762,14 +765,19 @@ func (itr *logTableFunctionRowIter) Next(ctx *sql.Context) (sql.Row, error) {
}
}
meta, err := commit.GetCommitMeta(ctx)
if err != nil {
return nil, err
if meta == nil {
meta, err = commit.GetCommitMeta(ctx)
if err != nil {
return nil, err
}
}
height, err := commit.Height()
if err != nil {
return nil, err
// TODO: will retrieve height again if it's 0
if height == 0 {
height, err = commit.Height()
if err != nil {
return nil, err
}
}
row := sql.NewRow(commitHash.String(), meta.Name, meta.Email, meta.Time(), meta.Description, height)

View File

@@ -337,7 +337,7 @@ func (itr *doltColDiffCommitHistoryRowItr) Next(ctx *sql.Context) (sql.Row, erro
}
itr.commits = nil
} else if itr.child != nil {
_, optCmt, err := itr.child.Next(ctx)
_, optCmt, _, _, err := itr.child.Next(ctx)
if err != nil {
return nil, err
}

View File

@@ -159,7 +159,7 @@ func NewCommitAncestorsRowItr(sqlCtx *sql.Context, ddb *doltdb.DoltDB) (*CommitA
// After retrieving the last row, Close will be automatically closed.
func (itr *CommitAncestorsRowItr) Next(ctx *sql.Context) (sql.Row, error) {
if len(itr.cache) == 0 {
ch, optCmt, err := itr.itr.Next(ctx)
ch, optCmt, _, _, err := itr.itr.Next(ctx)
if err != nil {
// When complete itr.Next will return io.EOF
return nil, err

View File

@@ -152,7 +152,7 @@ func NewCommitsRowItr(ctx *sql.Context, ddb *doltdb.DoltDB) (CommitsRowItr, erro
// Next retrieves the next row. It will return io.EOF if it's the last row.
// After retrieving the last row, Close will be automatically closed.
func (itr CommitsRowItr) Next(ctx *sql.Context) (sql.Row, error) {
h, optCmt, err := itr.itr.Next(ctx)
h, optCmt, _, _, err := itr.itr.Next(ctx)
if err != nil {
return nil, err
}

View File

@@ -183,7 +183,7 @@ func (dt *DiffTable) PartitionRanges(ctx *sql.Context, ranges []prolly.Range) (s
return nil, err
}
cmHash, _, err := cmItr.Next(ctx)
cmHash, _, _, _, err := cmItr.Next(ctx)
if err != nil {
return nil, err
}
@@ -424,7 +424,7 @@ func (dt *DiffTable) scanHeightForChild(ctx *sql.Context, parent hash.Hash, heig
func (dt *DiffTable) reverseIterForChild(ctx *sql.Context, parent hash.Hash) (*doltdb.Commit, hash.Hash, error) {
iter := doltdb.CommitItrForRoots[*sql.Context](dt.ddb, dt.head)
for {
childHs, optCmt, err := iter.Next(ctx)
childHs, optCmt, _, _, err := iter.Next(ctx)
if errors.Is(err, io.EOF) {
return nil, hash.Hash{}, nil
} else if err != nil {
@@ -839,7 +839,7 @@ func (dps *DiffPartitions) Next(ctx *sql.Context) (sql.Partition, error) {
}
for {
cmHash, optCmt, err := dps.cmItr.Next(ctx)
cmHash, optCmt, _, _, err := dps.cmItr.Next(ctx)
if err != nil {
return nil, err
}

View File

@@ -235,7 +235,7 @@ func NewLogItr(ctx *sql.Context, ddb *doltdb.DoltDB, head *doltdb.Commit) (*LogI
// Next retrieves the next row. It will return io.EOF if it's the last row.
// After retrieving the last row, Close will be automatically closed.
func (itr *LogItr) Next(ctx *sql.Context) (sql.Row, error) {
h, optCmt, err := itr.child.Next(ctx)
h, optCmt, meta, height, err := itr.child.Next(ctx)
if err != nil {
return nil, err
}
@@ -246,14 +246,18 @@ func (itr *LogItr) Next(ctx *sql.Context) (sql.Row, error) {
return nil, doltdb.ErrGhostCommitRuntimeFailure
}
meta, err := cm.GetCommitMeta(ctx)
if err != nil {
return nil, err
if meta == nil {
meta, err = cm.GetCommitMeta(ctx)
if err != nil {
return nil, err
}
}
height, err := cm.Height()
if err != nil {
return nil, err
if height == 0 {
height, err = cm.Height()
if err != nil {
return nil, err
}
}
return sql.NewRow(h.String(), meta.Name, meta.Email, meta.Time(), meta.Description, height), nil

View File

@@ -338,7 +338,7 @@ func (itr *doltDiffCommitHistoryRowItr) Next(ctx *sql.Context) (sql.Row, error)
}
itr.commits = nil
} else if itr.child != nil {
_, optCmt, err := itr.child.Next(ctx)
_, optCmt, _, _, err := itr.child.Next(ctx)
if err != nil {
return nil, err
}
@@ -347,6 +347,7 @@ func (itr *doltDiffCommitHistoryRowItr) Next(ctx *sql.Context) (sql.Row, error)
return nil, io.EOF
}
// TODO: we already have meta and height
err = itr.loadTableChanges(ctx, commit)
if err == doltdb.ErrGhostCommitEncountered {
// When showing the diff table in a shallow clone, we show as much of the dolt_history_{table} as we can,

View File

@@ -456,7 +456,7 @@ type commitPartitioner struct {
// Next returns the next partition and nil, io.EOF when complete
func (cp commitPartitioner) Next(ctx *sql.Context) (sql.Partition, error) {
h, optCmt, err := cp.cmItr.Next(ctx)
h, optCmt, _, _, err := cp.cmItr.Next(ctx)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,5 @@
*.test
*.pprof
*.out
*.pdf
*.exe

View File

@@ -0,0 +1,94 @@
// Copyright 2025 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dolt_log_bench
import (
"context"
"fmt"
"io"
"testing"
"github.com/dolthub/go-mysql-server/sql"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/cmd/dolt/commands/engine"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
)
var dEnv *env.DoltEnv
func init() {
dEnv = dtestutils.CreateTestEnv()
populateCommitGraph(dEnv)
}
func setupBenchmark(t *testing.B, dEnv *env.DoltEnv) (*sql.Context, *engine.SqlEngine) {
ctx := context.Background()
config := &engine.SqlEngineConfig{
ServerUser: "root",
Autocommit: true,
}
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
require.NoError(t, err)
eng, err := engine.NewSqlEngine(ctx, mrEnv, config)
require.NoError(t, err)
sqlCtx, err := eng.NewLocalContext(ctx)
require.NoError(t, err)
sqlCtx.SetCurrentDatabase("dolt")
return sqlCtx, eng
}
func populateCommitGraph(dEnv *env.DoltEnv) {
ctx := context.Background()
cliCtx, err := commands.NewArgFreeCliContext(ctx, dEnv, dEnv.FS)
if err != nil {
panic(err)
}
defer cliCtx.Close()
execSql := func(dEnv *env.DoltEnv, q string) int {
args := []string{"-r", "null", "-q", q}
return commands.SqlCmd{}.Exec(ctx, "sql", args, dEnv, cliCtx)
}
for i := 0; i < 500; i++ {
execSql(dEnv, fmt.Sprintf("call dolt_commit('--allow-empty', '-m', '%d') ", i))
}
}
func BenchmarkDoltLog(b *testing.B) {
ctx, eng := setupBenchmark(b, dEnv)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, iter, _, err := eng.Query(ctx, "select * from dolt_log()")
require.NoError(b, err)
for {
_, err := iter.Next(ctx)
if err != nil {
break
}
}
require.Error(b, io.EOF)
err = iter.Close(ctx)
require.NoError(b, err)
}
_ = eng.Close()
b.ReportAllocs()
}

View File

@@ -106,7 +106,7 @@ func (db *database) loadDatasetsNomsMap(ctx context.Context, rootHash hash.Hash)
}
func (db *database) loadDatasetsRefmap(ctx context.Context, rootHash hash.Hash) (prolly.AddressMap, error) {
if rootHash == (hash.Hash{}) {
if rootHash.IsEmpty() {
return prolly.NewEmptyAddressMap(db.ns)
}
@@ -151,7 +151,7 @@ func (m nomsDatasetsMap) IterAll(ctx context.Context, cb func(string, hash.Hash)
}
// Datasets returns the Map of Datasets in the current root. If you intend to edit the map and commit changes back,
// then you should fetch the current root, then call DatasetsInRoot with that hash. Otherwise another writer could
// then you should fetch the current root, then call DatasetsInRoot with that hash. Otherwise, another writer could
// change the root value between when you get the root hash and call this method.
func (db *database) Datasets(ctx context.Context) (DatasetsMap, error) {
rootHash, err := db.rt.Root(ctx)