Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solver: add sqlcachestorage as an alternative to the bolt db #5246

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cmd/buildkitd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type Config struct {
} `toml:"frontend"`

System *SystemConfig `toml:"system"`

Cache CacheConfig `toml:"cache"`
}

type SystemConfig struct {
Expand Down Expand Up @@ -179,3 +181,7 @@ type GatewayFrontendConfig struct {
Enabled *bool `toml:"enabled"`
AllowedRepositories []string `toml:"allowedRepositories"`
}

type CacheConfig struct {
IndexFormat string `toml:"index-format"`
}
24 changes: 21 additions & 3 deletions cmd/buildkitd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/bboltcachestorage"
"github.com/moby/buildkit/solver/sqlcachestorage"
"github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/appdefaults"
Expand Down Expand Up @@ -540,10 +541,16 @@ func setDefaultConfig(cfg *config.Config) {
if cfg.OTEL.SocketPath == "" {
cfg.OTEL.SocketPath = appdefaults.TraceSocketPath(isRootlessConfig())
}

if cfg.Cache.IndexFormat == "" {
cfg.Cache.IndexFormat = appdefaults.CacheIndexFormat
}
}

var isRootlessConfigOnce sync.Once
var isRootlessConfigValue bool
var (
isRootlessConfigOnce sync.Once
isRootlessConfigValue bool
)

// isRootlessConfig is true if we should be using the rootless config
// defaults instead of the normal defaults.
Expand Down Expand Up @@ -799,7 +806,7 @@ func newController(c *cli.Context, cfg *config.Config) (*control.Controller, err
frontends["gateway.v0"] = gwfe
}

cacheStorage, err := bboltcachestorage.NewStore(filepath.Join(cfg.Root, "cache.db"))
cacheStorage, err := newCacheKeyStorage(cfg.Root, cfg.Cache)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -962,6 +969,17 @@ func parseBoolOrAuto(s string) (*bool, error) {
return &b, err
}

func newCacheKeyStorage(root string, cfg config.CacheConfig) (solver.PersistentCacheKeyStorage, error) {
switch cfg.IndexFormat {
case "bolt":
return bboltcachestorage.NewStore(filepath.Join(root, "cache.db"))
case "sqlite":
return sqlcachestorage.NewStore(filepath.Join(root, "cache.db"))
default:
return nil, errors.Errorf("invalid cache index format: %s", cfg.IndexFormat)
}
}

func runTraceController(p string, exp sdktrace.SpanExporter) error {
server := grpc.NewServer()
tracev1.RegisterTraceServiceServer(server, &traceCollector{exporter: exp})
Expand Down
5 changes: 2 additions & 3 deletions control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"github.com/moby/buildkit/session/grpchijack"
containerdsnapshot "github.com/moby/buildkit/snapshot/containerd"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/bboltcachestorage"
"github.com/moby/buildkit/solver/llbsolver"
"github.com/moby/buildkit/solver/llbsolver/proc"
"github.com/moby/buildkit/solver/pb"
Expand Down Expand Up @@ -63,7 +62,7 @@ type Opt struct {
Entitlements []string
TraceCollector sdktrace.SpanExporter
HistoryDB db.DB
CacheStore *bboltcachestorage.Store
CacheStore solver.PersistentCacheKeyStorage
LeaseManager *leaseutil.Manager
ContentStore *containerdsnapshot.Store
HistoryConfig *config.HistoryConfig
Expand Down Expand Up @@ -673,7 +672,7 @@ func cacheOptKey(opt controlapi.CacheOptionsEntry) (string, error) {
if opt.Type == "registry" && opt.Attrs["ref"] != "" {
return opt.Attrs["ref"], nil
}
var rawOpt = struct {
rawOpt := struct {
Type string
Attrs map[string]string
}{
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ require (
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/in-toto/in-toto-golang v0.5.0
github.com/klauspost/compress v1.17.9
github.com/mattn/go-sqlite3 v1.14.22
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/moby/docker-image-spec v1.3.1
github.com/moby/locker v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
Expand Down
5 changes: 4 additions & 1 deletion hack/composefiles/buildkitd.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
debug = true

[grpc]
debugAddress = "0.0.0.0:6060"
debugAddress = "0.0.0.0:6060"

[cache]
index-format = "sqlite"
3 changes: 2 additions & 1 deletion hack/composefiles/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ services:
build:
context: ../..
args:
BUILDKIT_DEBUG: 1
#BUILDKIT_DEBUG: 1
CGO_ENABLED: 1
Comment on lines +8 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For CGO mode-free you can look at https://pkg.go.dev/modernc.org/sqlite

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can consider this if we need a CGO free mode. At the current moment, we still have the bolt database and we can just easily say "this feature only exists with cgo". I took a look at that project and the C library seems to be faster in most places.

I updated the PR to include some better messages for when cgo is disabled and to also only enable it on linux and darwin which shouldn't even matter much because I don't think you can run buildkit on darwin or windows anyway.

image: moby/buildkit:local
ports:
- 127.0.0.1:5000:5000
Expand Down
1 change: 1 addition & 0 deletions solver/bboltcachestorage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func NewStore(dbPath string) (*Store, error) {
}); err != nil {
return nil, err
}

return &Store{db: db}, nil
}

Expand Down
23 changes: 23 additions & 0 deletions solver/bboltcachestorage/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package bboltcachestorage
import (
"path/filepath"
"testing"
"time"

"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/testutil"
"github.com/stretchr/testify/require"
Expand All @@ -22,3 +24,24 @@ func TestBoltCacheStorage(t *testing.T) {
return st
})
}

func BenchmarkAddResult(b *testing.B) {
tmpdir := b.TempDir()
store, err := NewStore(filepath.Join(tmpdir, "cache.db"))
if err != nil {
b.Fatal(err)
}
defer store.Close()

for i := 0; i < b.N; i++ {
id, res := newResult()
store.AddResult(id, res)
}
}

func newResult() (string, solver.CacheResult) {
return identity.NewID(), solver.CacheResult{
CreatedAt: time.Now(),
ID: identity.NewID(),
}
}
6 changes: 5 additions & 1 deletion solver/cachemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,11 @@ func (c *cacheManager) Save(k *CacheKey, r Result, createdAt time.Time) (rck *Ex
"stack": bklog.TraceLevelOnlyStack(),
})
defer func() {
lg.WithError(rerr).WithField("return_cachekey", rck.TraceFields()).Trace("cache manager")
l := lg.WithError(rerr)
if rck != nil {
l = l.WithField("return_cachekey", rck.TraceFields())
}
l.Trace("cache manager")
}()

c.mu.Lock()
Expand Down
6 changes: 6 additions & 0 deletions solver/cachestorage.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package solver

import (
"context"
"io"
"time"

"github.com/moby/buildkit/session"
Expand Down Expand Up @@ -29,6 +30,11 @@ type CacheKeyStorage interface {
WalkBacklinks(id string, fn func(id string, link CacheInfoLink) error) error
}

type PersistentCacheKeyStorage interface {
CacheKeyStorage
io.Closer
}

// CacheResult is a record for a single solve result
type CacheResult struct {
CreatedAt time.Time
Expand Down
116 changes: 116 additions & 0 deletions solver/sqlcachestorage/sql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package sqlcachestorage

const (
createResultsTableSQL = `
CREATE TABLE IF NOT EXISTS results (
id text NOT NULL,
created_at timestamp,
worker_ref_id text NOT NULL
);
`

createResultsIndexSQL = `
CREATE INDEX IF NOT EXISTS results_index
ON results (id);
`

createResultsByRefIndexSQL = `
CREATE INDEX IF NOT EXISTS results_by_ref_index
ON results (worker_ref_id);
`

createLinksTableSQL = `
CREATE TABLE IF NOT EXISTS links (
source_result_id text NOT NULL,
vertex_input integer DEFAULT 0,
vertex_output integer DEFAULT 0,
vertex_digest text NOT NULL,
vertex_selector text DEFAULT '',
target_result_id text NOT NULL,
UNIQUE (source_result_id, vertex_input, vertex_output, vertex_digest, vertex_selector, target_result_id)
);
`

createUnreferencedLinksTriggerSQL = `
CREATE TRIGGER IF NOT EXISTS unreferenced_links_sql AFTER DELETE ON results
BEGIN
DELETE FROM links WHERE source_result_id = old.id OR target_result_id = old.id;
END
`

createLinksIndexSQL = `
CREATE INDEX IF NOT EXISTS links_index
ON links (source_result_id, vertex_input, vertex_output, vertex_digest, vertex_selector, target_result_id);
`

createBacklinksIndexSQL = `
CREATE INDEX IF NOT EXISTS backlinks_index
ON links (target_result_id);
`

existsSQL = `
SELECT 1 FROM results WHERE id = ? LIMIT 1;
`

walkSQL = `
SELECT id FROM results;
`

walkResultsSQL = `
SELECT worker_ref_id, created_at FROM results WHERE id = ?;
`

loadSQL = `
SELECT worker_ref_id, created_at FROM results
WHERE id = ? AND worker_ref_id = ?
LIMIT 1;
`

addResultSQL = `
INSERT INTO results (id, created_at, worker_ref_id)
VALUES(?, ?, ?);
`

deleteResultByRefIDSQL = `
DELETE FROM results WHERE worker_ref_id = ?;
`

walkIDsByResultSQL = `
SELECT DISTINCT id FROM results WHERE worker_ref_id = ?;
`

addLinkSQL = `
INSERT INTO links (source_result_id, vertex_input, vertex_output, vertex_digest, vertex_selector, target_result_id)
VALUES (?, ?, ?, ?, ?, ?);
`

walkLinksSQL = `
SELECT target_result_id FROM links
WHERE source_result_id = ? AND vertex_input = ? AND vertex_output = ? AND vertex_digest = ? AND vertex_selector = ?;
`

hasLinkSQL = `
SELECT 1 FROM links
WHERE source_result_id = ?
AND vertex_input = ?
AND vertex_output = ?
AND vertex_digest = ?
AND vertex_selector = ?
AND target_result_id = ?
LIMIT 1;
`

walkBacklinksSQL = `
SELECT source_result_id, vertex_input, vertex_output, vertex_digest, vertex_selector FROM links WHERE target_result_id = ?;
`
)

var createSQL = []string{
createResultsTableSQL,
createResultsIndexSQL,
createResultsByRefIndexSQL,
createLinksTableSQL,
createUnreferencedLinksTriggerSQL,
createLinksIndexSQL,
createBacklinksIndexSQL,
}
Loading
Loading