Skip to content

Commit 50846d6

Browse files
committed
Local disk cache
1 parent 6dd3970 commit 50846d6

File tree

13 files changed

+877
-11
lines changed

13 files changed

+877
-11
lines changed

go/pkg/client/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ go_library(
2525
"//go/pkg/command",
2626
"//go/pkg/contextmd",
2727
"//go/pkg/digest",
28+
"//go/pkg/diskcache",
2829
"//go/pkg/filemetadata",
2930
"//go/pkg/io/impath",
3031
"//go/pkg/io/walker",

go/pkg/client/cas_download.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ func (c *Client) DownloadOutputs(ctx context.Context, outs map[string]*TreeOutpu
102102
symlinks = append(symlinks, out)
103103
continue
104104
}
105+
if c.diskCache != nil {
106+
absPath := out.Path
107+
if !filepath.IsAbs(absPath) {
108+
absPath = filepath.Join(outDir, absPath)
109+
}
110+
if c.diskCache.LoadCas(out.Digest, absPath) {
111+
fullStats.Requested += out.Digest.Size
112+
fullStats.Cached += out.Digest.Size
113+
continue
114+
}
115+
}
105116
if _, ok := downloads[out.Digest]; ok {
106117
copies = append(copies, out)
107118
// All copies are effectivelly cached
@@ -130,6 +141,11 @@ func (c *Client) DownloadOutputs(ctx context.Context, outs map[string]*TreeOutpu
130141
if err := cache.Update(absPath, md); err != nil {
131142
return fullStats, err
132143
}
144+
if c.diskCache != nil {
145+
if err := c.diskCache.StoreCas(output.Digest, absPath); err != nil {
146+
return fullStats, err
147+
}
148+
}
133149
}
134150
for _, out := range copies {
135151
perm := c.RegularMode

go/pkg/client/client.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/bazelbuild/remote-apis-sdks/go/pkg/casng"
2020
"github.com/bazelbuild/remote-apis-sdks/go/pkg/chunker"
2121
"github.com/bazelbuild/remote-apis-sdks/go/pkg/digest"
22+
"github.com/bazelbuild/remote-apis-sdks/go/pkg/diskcache"
2223
"github.com/bazelbuild/remote-apis-sdks/go/pkg/retry"
2324
"github.com/bazelbuild/remote-apis-sdks/go/pkg/uploadinfo"
2425
"github.com/pkg/errors"
@@ -188,6 +189,7 @@ type Client struct {
188189
uploadOnce sync.Once
189190
downloadOnce sync.Once
190191
useBatchCompression UseBatchCompression
192+
diskCache *diskcache.DiskCache
191193
}
192194

193195
const (
@@ -333,6 +335,20 @@ func (o *TreeSymlinkOpts) Apply(c *Client) {
333335
c.TreeSymlinkOpts = o
334336
}
335337

338+
type DiskCacheOpts struct {
339+
Context context.Context
340+
Path string
341+
MaxCapacityGb float64
342+
}
343+
344+
// Apply sets the client's TreeSymlinkOpts.
345+
func (o *DiskCacheOpts) Apply(c *Client) {
346+
if o.Path != "" {
347+
capBytes := uint64(o.MaxCapacityGb * 1024 * 1024 * 1024)
348+
c.diskCache = diskcache.New(o.Context, o.Path, capBytes)
349+
}
350+
}
351+
336352
// MaxBatchDigests is maximum amount of digests to batch in upload and download operations.
337353
type MaxBatchDigests int
338354

go/pkg/client/exec.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,23 @@ func (c *Client) ExecuteAction(ctx context.Context, ac *Action) (*repb.ActionRes
9898
}
9999

100100
// CheckActionCache queries remote action cache, returning an ActionResult or nil if it doesn't exist.
101-
func (c *Client) CheckActionCache(ctx context.Context, acDg *repb.Digest) (*repb.ActionResult, error) {
101+
func (c *Client) CheckActionCache(ctx context.Context, dg digest.Digest) (*repb.ActionResult, error) {
102+
if c.diskCache != nil {
103+
if res, loaded := c.diskCache.LoadActionCache(dg); loaded {
104+
return res, nil
105+
}
106+
}
102107
res, err := c.GetActionResult(ctx, &repb.GetActionResultRequest{
103108
InstanceName: c.InstanceName,
104-
ActionDigest: acDg,
109+
ActionDigest: dg.ToProto(),
105110
})
106111
switch st, _ := status.FromError(err); st.Code() {
107112
case codes.OK:
113+
if c.diskCache != nil {
114+
if err := c.diskCache.StoreActionCache(dg, res); err != nil {
115+
log.Errorf("error storing ActionResult of %s to disk cache: %v", dg, err)
116+
}
117+
}
108118
return res, nil
109119
case codes.NotFound:
110120
return nil, nil
@@ -166,12 +176,13 @@ func (c *Client) PrepAction(ctx context.Context, ac *Action) (*repb.Digest, *rep
166176
if err != nil {
167177
return nil, nil, gerrors.WithMessage(err, "marshalling Action proto")
168178
}
169-
acDg := digest.NewFromBlob(acBlob).ToProto()
179+
dg := digest.NewFromBlob(acBlob)
180+
acDg := dg.ToProto()
170181

171182
// If the result is cacheable, check if it's already in the cache.
172183
if !ac.DoNotCache || !ac.SkipCache {
173184
log.V(1).Info("Checking cache")
174-
res, err := c.CheckActionCache(ctx, acDg)
185+
res, err := c.CheckActionCache(ctx, dg)
175186
if err != nil {
176187
return nil, nil, err
177188
}

go/pkg/diskcache/BUILD.bazel

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "diskcache",
5+
srcs = [
6+
"atim_darwin.go",
7+
"atim_linux.go",
8+
"atim_windows.go",
9+
"diskcache.go",
10+
],
11+
importpath = "github.com/bazelbuild/remote-apis-sdks/go/pkg/diskcache",
12+
visibility = ["//visibility:public"],
13+
deps = [
14+
"//go/pkg/digest",
15+
"@com_github_bazelbuild_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto",
16+
"@com_github_golang_glog//:go_default_library",
17+
"@org_golang_google_protobuf//proto:go_default_library",
18+
],
19+
)
20+
21+
go_test(
22+
name = "diskcache_test",
23+
srcs = ["diskcache_test.go"],
24+
embed = [":diskcache"],
25+
deps = [
26+
"//go/pkg/digest",
27+
"//go/pkg/testutil",
28+
"@com_github_bazelbuild_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto",
29+
"@com_github_google_go_cmp//cmp:go_default_library",
30+
"@com_github_pborman_uuid//:go_default_library",
31+
"@org_golang_x_sync//errgroup:go_default_library",
32+
],
33+
)

go/pkg/diskcache/atim_darwin.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Utility to get the last accessed time on Darwin.
2+
package diskcache
3+
4+
import (
5+
"os"
6+
"syscall"
7+
"time"
8+
)
9+
10+
func GetLastAccessTime(path string) (time.Time, error) {
11+
info, err := os.Stat(path)
12+
if err != nil {
13+
return time.Time{}, err
14+
}
15+
return time.Unix(info.Sys().(*syscall.Stat_t).Atimespec.Unix()), nil
16+
}

go/pkg/diskcache/atim_linux.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Utility to get the last accessed time on Linux.
2+
package diskcache
3+
4+
import (
5+
"os"
6+
"syscall"
7+
"time"
8+
)
9+
10+
func GetLastAccessTime(path string) (time.Time, error) {
11+
info, err := os.Stat(path)
12+
if err != nil {
13+
return time.Time{}, err
14+
}
15+
return time.Unix(info.Sys().(*syscall.Stat_t).Atim.Unix()), nil
16+
}

go/pkg/diskcache/atim_windows.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Utility to get the last accessed time on Windows.
2+
package diskcache
3+
4+
import (
5+
"os"
6+
"syscall"
7+
"time"
8+
)
9+
10+
// This will return correct values only if `fsutil behavior set disablelastaccess 0` is set.
11+
// Tracking of last access time is disabled by default on Windows.
12+
func GetLastAccessTime(path string) (time.Time, error) {
13+
info, err := os.Stat(path)
14+
if err != nil {
15+
return time.Time{}, err
16+
}
17+
return time.Unix(0, info.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()), nil
18+
}

0 commit comments

Comments
 (0)