Skip to content

Commit 544f298

Browse files
authored
Merge pull request #9726 from dolthub/aaron/auto-gc-for-dolt-sql
go: cmd/dolt: sql.go: Enable Auto GC when running `dolt sql`.
2 parents 5fa69b5 + c2cf39e commit 544f298

File tree

11 files changed

+140
-21
lines changed

11 files changed

+140
-21
lines changed

go/cmd/dolt/cli/cli_context.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ type LateBindQueryistResult struct {
3333
Closer func()
3434
}
3535

36+
type LateBindQueryistConfig struct {
37+
EnableAutoGC bool
38+
}
39+
40+
type LateBindQueryistOption func(*LateBindQueryistConfig)
41+
3642
// LateBindQueryist is a function that will be called the first time Queryist is needed for use. Input is a context which
3743
// is appropriate for the call to commence. Output is a LateBindQueryistResult, which includes a Queryist, a sql.Context, and
3844
// a closer function. It can also result in an error.
@@ -48,15 +54,15 @@ type LateBindQueryistResult struct {
4854
// This state is useful for determining whether a command making use of the CliContext is being run within the context of
4955
// another command. This is particularly interesting when running a \checkout in a dolt sql session. It makes sense to do
5056
// so in the context of `dolt sql`, but not in the context of `dolt checkout` when connected to a remote server.
51-
type LateBindQueryist func(ctx context.Context) (LateBindQueryistResult, error)
57+
type LateBindQueryist func(ctx context.Context, opts ...LateBindQueryistOption) (LateBindQueryistResult, error)
5258

5359
// CliContexct is used to pass top level command information down to subcommands.
5460
type CliContext interface {
5561
// GlobalArgs returns the arguments passed before the subcommand.
5662
GlobalArgs() *argparser.ArgParseResults
5763
WorkingDir() filesys.Filesys
5864
Config() *env.DoltCliConfig
59-
QueryEngine(ctx context.Context) (QueryEngineResult, error)
65+
QueryEngine(ctx context.Context, opts ...LateBindQueryistOption) (QueryEngineResult, error)
6066
// Release resources associated with the CliContext, including
6167
// any QueryEngines which were provisioned over the lifetime
6268
// of the CliContext.
@@ -115,7 +121,7 @@ func (lbc LateBindCliContext) GlobalArgs() *argparser.ArgParseResults {
115121
// QueryEngine returns a Queryist, a sql.Context, a closer function, and an error. It ensures that only one call to the
116122
// LateBindQueryist is made, and caches the result. Note that if this is called twice, the closer function returns will
117123
// be nil, callers should check if is nil.
118-
func (lbc LateBindCliContext) QueryEngine(ctx context.Context) (res QueryEngineResult, err error) {
124+
func (lbc LateBindCliContext) QueryEngine(ctx context.Context, opts ...LateBindQueryistOption) (res QueryEngineResult, err error) {
119125
if lbc.activeContext != nil && lbc.activeContext.qryist != nil && lbc.activeContext.sqlCtx != nil {
120126
res.Queryist = *lbc.activeContext.qryist
121127
res.Context = lbc.activeContext.sqlCtx
@@ -125,7 +131,7 @@ func (lbc LateBindCliContext) QueryEngine(ctx context.Context) (res QueryEngineR
125131
return res, nil
126132
}
127133

128-
bindRes, err := lbc.bind(ctx)
134+
bindRes, err := lbc.bind(ctx, opts...)
129135
if err != nil {
130136
return res, err
131137
}

go/cmd/dolt/commands/engine/sqlengine.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ type SqlEngineConfig struct {
8787
EventSchedulerStatus eventscheduler.SchedulerStatus
8888
}
8989

90+
type SqlEngineConfigOption func(*SqlEngineConfig)
91+
9092
// NewSqlEngine returns a SqlEngine
9193
func NewSqlEngine(
9294
ctx context.Context,

go/cmd/dolt/commands/init_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ func TestInit(t *testing.T) {
7171
gCfg, _ := dEnv.Config.GetConfig(env.GlobalConfig)
7272
gCfg.SetStrings(test.GlobalConfig)
7373
apr := argparser.ArgParseResults{}
74-
latebind := func(ctx context.Context) (res cli.LateBindQueryistResult, err error) { return res, nil }
74+
latebind := func(ctx context.Context, opts ...cli.LateBindQueryistOption) (res cli.LateBindQueryistResult, err error) {
75+
return res, nil
76+
}
7577
cliCtx, _ := cli.NewCliContext(&apr, dEnv.Config, dEnv.FS, latebind)
7678

7779
result := InitCmd{}.Exec(ctx, "dolt init", test.Args, dEnv, cliCtx)

go/cmd/dolt/commands/sql.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ const (
100100
outputFlag = "output"
101101
binaryAsHexFlag = "binary-as-hex"
102102
skipBinaryAsHexFlag = "skip-binary-as-hex"
103+
disableAutoGCFlag = "disable-auto-gc"
103104
// TODO: Consider simplifying to use MySQL's skip pattern with single flag definition
104105
// MySQL handles both --binary-as-hex and --skip-binary-as-hex with one option definition
105106
// and uses disabled_my_option to distinguish between enable/disable
@@ -155,6 +156,7 @@ func (cmd SqlCmd) ArgParser() *argparser.ArgParser {
155156
ap.SupportsFlag(binaryAsHexFlag, "", "Print binary data as hex. Enabled by default for interactive terminals.")
156157
// TODO: MySQL uses a skip- pattern for negating flags and doesn't show them in help
157158
ap.SupportsFlag(skipBinaryAsHexFlag, "", "Disable binary data as hex output.")
159+
ap.SupportsFlag(disableAutoGCFlag, "", "Disable automatically running GC.")
158160
return ap
159161
}
160162

@@ -227,7 +229,13 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
227229
return HandleVErrAndExitCode(errhand.BuildDError("cannot use both --%s and --%s", binaryAsHexFlag, skipBinaryAsHexFlag).Build(), usage)
228230
}
229231

230-
queryist, err := cliCtx.QueryEngine(ctx)
232+
enableAutoGC := true
233+
if apr.Contains(disableAutoGCFlag) {
234+
enableAutoGC = false
235+
}
236+
queryist, err := cliCtx.QueryEngine(ctx, func(config *cli.LateBindQueryistConfig) {
237+
config.EnableAutoGC = enableAutoGC
238+
})
231239
if err != nil {
232240
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
233241
}
@@ -618,6 +626,11 @@ func execBatchMode(ctx *sql.Context, qryist cli.Queryist, input io.Reader, conti
618626
scanner := NewStreamScanner(input)
619627
var query string
620628
for scanner.Scan() {
629+
// The session we get is wrapped in a command begin/end block.
630+
// By ending the command and starting a new one, Auto GC is able
631+
// to form safe points if/when it is enabled.
632+
sql.SessionCommandEnd(ctx.Session)
633+
sql.SessionCommandBegin(ctx.Session)
621634
if fileReadProg != nil {
622635
updateFileReadProgressOutput()
623636
fileReadProg.setReadBytes(int64(len(scanner.Bytes())))
@@ -763,6 +776,12 @@ func execShell(sqlCtx *sql.Context, qryist cli.Queryist, format engine.PrintResu
763776
lastSqlCmd := ""
764777

765778
shell.Uninterpreted(func(c *ishell.Context) {
779+
// The session we get is wrapped in a command begin/end block.
780+
// By ending the command and starting a new one, Auto GC is able
781+
// to form safe points if/when it is enabled.
782+
sql.SessionCommandEnd(sqlCtx.Session)
783+
sql.SessionCommandBegin(sqlCtx.Session)
784+
766785
query := c.Args[0]
767786
query = strings.TrimSpace(query)
768787
if len(query) == 0 {

go/cmd/dolt/commands/sqlserver/queryist_utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func BuildConnectionStringQueryist(ctx context.Context, cwdFS filesys.Filesys, c
6868
gatherWarnings := false
6969
queryist := ConnectionQueryist{connection: conn, gatherWarnings: &gatherWarnings}
7070

71-
var lateBind cli.LateBindQueryist = func(ctx context.Context) (res cli.LateBindQueryistResult, err error) {
71+
var lateBind cli.LateBindQueryist = func(ctx context.Context, opts ...cli.LateBindQueryistOption) (res cli.LateBindQueryistResult, err error) {
7272
sqlCtx := sql.NewContext(ctx)
7373
sqlCtx.SetCurrentDatabase(dbRev)
7474
res.Queryist = queryist

go/cmd/dolt/commands/utils.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"crypto/sha1"
2020
"fmt"
21+
"io"
2122
"net"
2223
"os"
2324
"path/filepath"
@@ -31,6 +32,7 @@ import (
3132
"github.com/fatih/color"
3233
"github.com/gocraft/dbr/v2"
3334
"github.com/gocraft/dbr/v2/dialect"
35+
"github.com/sirupsen/logrus"
3436

3537
"github.com/dolthub/dolt/go/cmd/dolt/cli"
3638
"github.com/dolthub/dolt/go/cmd/dolt/commands/engine"
@@ -39,11 +41,13 @@ import (
3941
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
4042
"github.com/dolthub/dolt/go/libraries/doltcore/env"
4143
"github.com/dolthub/dolt/go/libraries/doltcore/env/actions"
44+
"github.com/dolthub/dolt/go/libraries/doltcore/sqle"
4245
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
4346
"github.com/dolthub/dolt/go/libraries/utils/argparser"
4447
"github.com/dolthub/dolt/go/libraries/utils/config"
4548
"github.com/dolthub/dolt/go/libraries/utils/editor"
4649
"github.com/dolthub/dolt/go/libraries/utils/filesys"
50+
"github.com/dolthub/dolt/go/store/chunks"
4751
"github.com/dolthub/dolt/go/store/datas"
4852
"github.com/dolthub/dolt/go/store/util/outputpager"
4953
)
@@ -246,12 +250,25 @@ func newLateBindingEngine(
246250
Autocommit: true,
247251
}
248252

249-
var lateBinder cli.LateBindQueryist = func(ctx context.Context) (res cli.LateBindQueryistResult, err error) {
253+
var lateBinder cli.LateBindQueryist = func(ctx context.Context, opts ...cli.LateBindQueryistOption) (res cli.LateBindQueryistResult, err error) {
250254
// We've deferred loading the database as long as we can.
251255
// If we're binding the Queryist, that means that engine is actually
252256
// going to be used.
253257
mrEnv.ReloadDBs(ctx)
254258

259+
queryistConfig := &cli.LateBindQueryistConfig{}
260+
for _, opt := range opts {
261+
opt(queryistConfig)
262+
}
263+
264+
if queryistConfig.EnableAutoGC {
265+
// We use a null logger here, as we do not want `dolt sql` output
266+
// to include auto-gc log lines.
267+
nullLgr := logrus.New()
268+
nullLgr.SetOutput(io.Discard)
269+
config.AutoGCController = sqle.NewAutoGCController(chunks.NoArchive, nullLgr)
270+
}
271+
255272
se, err := engine.NewSqlEngine(
256273
ctx,
257274
mrEnv,

go/cmd/dolt/dolt.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ If you're interested in running this command against a remote host, hit us up on
664664
isValidRepositoryRequired := subcommandName != "init" && subcommandName != "sql" && subcommandName != "sql-server" && subcommandName != "sql-client"
665665

666666
if noValidRepository && isValidRepositoryRequired {
667-
return func(ctx context.Context) (res cli.LateBindQueryistResult, err error) {
667+
return func(ctx context.Context, opts ...cli.LateBindQueryistOption) (res cli.LateBindQueryistResult, err error) {
668668
err = errors.New("The current directory is not a valid dolt repository.")
669669
if errors.Is(rootEnv.DBLoadError, nbs.ErrUnsupportedTableFileFormat) {
670670
// This is fairly targeted and specific to allow for better error messaging. We should consider

go/libraries/doltcore/sqle/auto_gc.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,14 @@ func (c *AutoGCController) newCommitHook(name string, db *doltdb.DoltDB) *autoGC
178178
closed := make(chan *gcWorkReport)
179179
close(closed)
180180
ret := &autoGCCommitHook{
181-
c: c,
182-
name: name,
183-
done: closed,
184-
next: make(chan *gcWorkReport, 1),
185-
db: db,
186-
tickCh: make(chan struct{}),
187-
stopCh: make(chan struct{}),
181+
c: c,
182+
name: name,
183+
done: closed,
184+
next: make(chan *gcWorkReport, 1),
185+
db: db,
186+
tickCh: make(chan struct{}),
187+
stopCh: make(chan struct{}),
188+
stoppedCh: make(chan struct{}),
188189
}
189190
c.hooks[name] = ret
190191
if c.threads != nil {
@@ -231,6 +232,11 @@ type autoGCCommitHook struct {
231232
// Closed when the thread should shutdown because the database
232233
// is being removed.
233234
stopCh chan struct{}
235+
// Closed as the background processing thread shuts down. The
236+
// database hook selects on this to avoid deadlocking if it
237+
// is trying to send to the worker thread after it has been
238+
// shutdown.
239+
stoppedCh chan struct{}
234240
// An optimistic send on this channel notifies the background
235241
// thread that the sizes may have changed and it can check for
236242
// the GC condition.
@@ -278,6 +284,8 @@ func (h *autoGCCommitHook) Execute(ctx context.Context, _ datas.Dataset, _ *dolt
278284
select {
279285
case h.tickCh <- struct{}{}:
280286
return nil, nil
287+
case <-h.stoppedCh:
288+
return nil, nil
281289
case <-ctx.Done():
282290
return nil, context.Cause(ctx)
283291
}
@@ -357,6 +365,7 @@ func (h *autoGCCommitHook) checkForGC(ctx context.Context) error {
357365

358366
func (h *autoGCCommitHook) thread(ctx context.Context) {
359367
defer h.wg.Done()
368+
defer close(h.stoppedCh)
360369
timer := time.NewTimer(checkInterval)
361370
defer timer.Stop()
362371
for {

go/performance/microsysbench/sysbench_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323
"strconv"
2424
"strings"
25+
"sync"
2526
"testing"
2627

2728
"github.com/dolthub/go-mysql-server/server"
@@ -49,11 +50,6 @@ const (
4950

5051
var dEnv *env.DoltEnv
5152

52-
func init() {
53-
dEnv = dtestutils.CreateTestEnv()
54-
populateRepo(dEnv, readTestData(dataFile))
55-
}
56-
5753
func BenchmarkOltpPointSelect(b *testing.B) {
5854
benchmarkSysbenchQuery(b, func(int) string {
5955
q := "SELECT c FROM sbtest1 WHERE id=%d"
@@ -119,7 +115,13 @@ func BenchmarkSelectRandomRanges(b *testing.B) {
119115
})
120116
}
121117

118+
var initOnce sync.Once
119+
122120
func benchmarkSysbenchQuery(b *testing.B, getQuery func(int) string) {
121+
initOnce.Do(func() {
122+
dEnv = dtestutils.CreateTestEnv()
123+
populateRepo(dEnv, readTestData(dataFile))
124+
})
123125
ctx, eng := setupBenchmark(b, dEnv)
124126
for i := 0; i < b.N; i++ {
125127
schema, iter, _, err := eng.Query(ctx, getQuery(i))
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
function randint(n)
2+
{
3+
return int(n * rand())
4+
}
5+
function valueslines(n)
6+
{
7+
end = "),"
8+
for (i = 0; i < n; i++) {
9+
if (i == n-1) {
10+
end = ");"
11+
}
12+
print "(" randint(65536) ",", randint(65536) ",", randint(65536) ",", randint(65536) end;
13+
}
14+
}
15+
BEGIN {
16+
print "DROP TABLE IF EXISTS vals;";
17+
print "CREATE TABLE vals (c1 int, c2 int, c3 int, c4 int);";
18+
for (j = 0; j < 256; j++) {
19+
print "INSERT INTO vals VALUES";
20+
valueslines(1024);
21+
}
22+
}
23+

0 commit comments

Comments
 (0)