-
Notifications
You must be signed in to change notification settings - Fork 63
/
shared_build_directory_creator.go
115 lines (104 loc) · 4.3 KB
/
shared_build_directory_creator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package builder
import (
"context"
"log"
"strconv"
"sync/atomic"
"github.com/buildbarn/bb-storage/pkg/digest"
"github.com/buildbarn/bb-storage/pkg/filesystem/path"
"github.com/buildbarn/bb-storage/pkg/util"
"google.golang.org/grpc/codes"
)
type sharedBuildDirectoryCreator struct {
base BuildDirectoryCreator
nextParallelActionID *atomic.Uint64
}
// NewSharedBuildDirectoryCreator is an adapter for
// BuildDirectoryCreator that causes build actions to be executed inside
// a subdirectory within the build directory, as opposed to inside the
// build directory itself. The subdirectory is either named after the
// action digest of the build action or uses an incrementing number,
// based on whether collisions may occur.
//
// This adapter can be used to add concurrency to a single worker. When
// executing build actions in parallel, every build action needs its own
// build directory.
func NewSharedBuildDirectoryCreator(base BuildDirectoryCreator, nextParallelActionID *atomic.Uint64) BuildDirectoryCreator {
return &sharedBuildDirectoryCreator{
base: base,
nextParallelActionID: nextParallelActionID,
}
}
func (dc *sharedBuildDirectoryCreator) GetBuildDirectory(ctx context.Context, actionDigestIfNotRunInParallel *digest.Digest) (BuildDirectory, *path.Trace, error) {
parentDirectory, parentDirectoryPath, err := dc.base.GetBuildDirectory(ctx, actionDigestIfNotRunInParallel)
if err != nil {
return nil, nil, err
}
// Determine the name of the subdirectory.
var name string
if actionDigestIfNotRunInParallel == nil {
// Multiple instances of this action may run in
// parallel, as the scheduler is not permitted to
// deduplicate them. This is likely caused by the
// 'do_not_cache' flag being set in the Action message.
//
// Number subdirectories incrementally to prevent
// collisions if multiple of them are scheduled on the
// same worker.
name = strconv.FormatUint(dc.nextParallelActionID.Add(1), 10)
} else {
// This action is guaranteed not to run in parallel, due
// to the scheduler being permitted to deduplicate
// execution requests. Use a directory name based on the
// action digest. This ensures that the working
// directory of the build action is deterministic,
// thereby increasing reproducibility.
//
// Only use a small number of characters from the digest
// to ensure the absolute path of the build directory
// remains short. This avoids reaching PATH_MAX and
// sockaddr_un::sun_path size limits for stronger digest
// functions. 16 characters is more than sufficient to
// prevent collisions.
name = actionDigestIfNotRunInParallel.GetHashString()[:16]
}
// Create the subdirectory.
childDirectoryName := path.MustNewComponent(name)
childDirectoryPath := parentDirectoryPath.Append(childDirectoryName)
if err := parentDirectory.Mkdir(childDirectoryName, 0o777); err != nil {
parentDirectory.Close()
return nil, nil, util.StatusWrapfWithCode(err, codes.Internal, "Failed to create build directory %#v", childDirectoryPath.GetUNIXString())
}
childDirectory, err := parentDirectory.EnterBuildDirectory(childDirectoryName)
if err != nil {
if err := parentDirectory.Remove(childDirectoryName); err != nil {
log.Printf("Failed to remove action digest build directory %#v upon failure to enter: %s", childDirectoryPath.GetUNIXString(), err)
}
parentDirectory.Close()
return nil, nil, util.StatusWrapfWithCode(err, codes.Internal, "Failed to enter build directory %#v", childDirectoryPath.GetUNIXString())
}
return &sharedBuildDirectory{
BuildDirectory: childDirectory,
parentDirectory: parentDirectory,
childDirectoryName: childDirectoryName,
childDirectoryPath: childDirectoryPath.GetUNIXString(),
}, childDirectoryPath, nil
}
type sharedBuildDirectory struct {
BuildDirectory
parentDirectory BuildDirectory
childDirectoryName path.Component
childDirectoryPath string
}
func (d *sharedBuildDirectory) Close() error {
err1 := d.BuildDirectory.Close()
err2 := d.parentDirectory.RemoveAll(d.childDirectoryName)
err3 := d.parentDirectory.Close()
if err1 != nil {
return util.StatusWrapf(err1, "Failed to close build directory %#v", d.childDirectoryPath)
}
if err2 != nil {
return util.StatusWrapfWithCode(err2, codes.Internal, "Failed to remove build directory %#v", d.childDirectoryPath)
}
return err3
}