Skip to content

Commit 20da34c

Browse files
aclementsgopherbot
authored andcommitted
cmd/go: move GOCACHEPROG protocol types to their own package
This is a step toward making it easy to point to them in documentation. The other option is that we copy-paste all of these type definitions wholesale, which seems ridiculous. Updates #71032 Updates #59719 Change-Id: I7117e03308ae0adc721ed7a57792c33ba68ce827 Reviewed-on: https://go-review.googlesource.com/c/go/+/638995 Auto-Submit: Austin Clements <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Matloob <[email protected]>
1 parent 858a0e9 commit 20da34c

File tree

2 files changed

+155
-145
lines changed

2 files changed

+155
-145
lines changed

Diff for: src/cmd/go/internal/cache/prog.go

+18-145
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package cache
77
import (
88
"bufio"
99
"cmd/go/internal/base"
10+
"cmd/go/internal/cacheprog"
1011
"cmd/internal/quoted"
1112
"context"
1213
"crypto/sha256"
@@ -38,7 +39,7 @@ type ProgCache struct {
3839

3940
// can are the commands that the child process declared that it supports.
4041
// This is effectively the versioning mechanism.
41-
can map[ProgCmd]bool
42+
can map[cacheprog.ProgCmd]bool
4243

4344
// fuzzDirCache is another Cache implementation to use for the FuzzDir
4445
// method. In practice this is the default GOCACHE disk-based
@@ -55,142 +56,14 @@ type ProgCache struct {
5556

5657
mu sync.Mutex // guards following fields
5758
nextID int64
58-
inFlight map[int64]chan<- *ProgResponse
59+
inFlight map[int64]chan<- *cacheprog.ProgResponse
5960
outputFile map[OutputID]string // object => abs path on disk
6061

6162
// writeMu serializes writing to the child process.
6263
// It must never be held at the same time as mu.
6364
writeMu sync.Mutex
6465
}
6566

66-
// The following types define the protocol for a GOCACHEPROG program.
67-
//
68-
// By default, the go command manages a build cache stored in the file system
69-
// itself. GOCACHEPROG can be set to the name of a command (with optional
70-
// space-separated flags) that implements the go command build cache externally.
71-
// This permits defining a different cache policy.
72-
//
73-
// The go command will start the GOCACHEPROG as a subprocess and communicate
74-
// with it via JSON messages over stdin/stdout. The subprocess's stderr will be
75-
// connected to the go command's stderr.
76-
//
77-
// The subprocess should immediately send a [ProgResponse] with its capabilities.
78-
// After that, the go command will send a stream of [ProgRequest] messages and the
79-
// subprocess should reply to each [ProgRequest] with a [ProgResponse] message.
80-
81-
// ProgCmd is a command that can be issued to a child process.
82-
//
83-
// If the interface needs to grow, the go command can add new commands or new
84-
// versioned commands like "get2" in the future. The initial [ProgResponse] from
85-
// the child process indicates which commands it supports.
86-
type ProgCmd string
87-
88-
const (
89-
// cmdPut tells the cache program to store an object in the cache.
90-
//
91-
// [ProgRequest.ActionID] is the cache key of this object. The cache should
92-
// store [ProgRequest.OutputID] and [ProgRequest.Body] under this key for a
93-
// later "get" request. It must also store the Body in a file in the local
94-
// file system and return the path to that file in [ProgResponse.DiskPath],
95-
// which must exist at least until a "close" request.
96-
cmdPut = ProgCmd("put")
97-
98-
// cmdGet tells the cache program to retrieve an object from the cache.
99-
//
100-
// [ProgRequest.ActionID] specifies the key of the object to get. If the
101-
// cache does not contain this object, it should set [ProgResponse.Miss] to
102-
// true. Otherwise, it should populate the fields of [ProgResponse],
103-
// including setting [ProgResponse.OutputID] to the OutputID of the original
104-
// "put" request and [ProgResponse.DiskPath] to the path of a local file
105-
// containing the Body of the original "put" request. That file must
106-
// continue to exist at least until a "close" request.
107-
cmdGet = ProgCmd("get")
108-
109-
// cmdClose requests that the cache program exit gracefully.
110-
//
111-
// The cache program should reply to this request and then exit
112-
// (thus closing its stdout).
113-
cmdClose = ProgCmd("close")
114-
)
115-
116-
// ProgRequest is the JSON-encoded message that's sent from the go command to
117-
// the GOCACHEPROG child process over stdin. Each JSON object is on its own
118-
// line. A ProgRequest of Type "put" with BodySize > 0 will be followed by a
119-
// line containing a base64-encoded JSON string literal of the body.
120-
type ProgRequest struct {
121-
// ID is a unique number per process across all requests.
122-
// It must be echoed in the ProgResponse from the child.
123-
ID int64
124-
125-
// Command is the type of request.
126-
// The go command will only send commands that were declared
127-
// as supported by the child.
128-
Command ProgCmd
129-
130-
// ActionID is the cache key for "put" and "get" requests.
131-
ActionID []byte `json:",omitempty"` // or nil if not used
132-
133-
// OutputID is stored with the body for "put" requests.
134-
//
135-
// Prior to Go 1.24, when GOCACHEPROG was still an experiment, this was
136-
// accidentally named ObjectID. It was renamed to OutputID in Go 1.24.
137-
OutputID []byte `json:",omitempty"` // or nil if not used
138-
139-
// Body is the body for "put" requests. It's sent after the JSON object
140-
// as a base64-encoded JSON string when BodySize is non-zero.
141-
// It's sent as a separate JSON value instead of being a struct field
142-
// send in this JSON object so large values can be streamed in both directions.
143-
// The base64 string body of a ProgRequest will always be written
144-
// immediately after the JSON object and a newline.
145-
Body io.Reader `json:"-"`
146-
147-
// BodySize is the number of bytes of Body. If zero, the body isn't written.
148-
BodySize int64 `json:",omitempty"`
149-
150-
// ObjectID is the accidental spelling of OutputID that was used prior to Go
151-
// 1.24.
152-
//
153-
// Deprecated: use OutputID. This field is only populated temporarily for
154-
// backwards compatibility with Go 1.23 and earlier when
155-
// GOEXPERIMENT=gocacheprog is set. It will be removed in Go 1.25.
156-
ObjectID []byte `json:",omitempty"`
157-
}
158-
159-
// ProgResponse is the JSON response from the child process to the go command.
160-
//
161-
// With the exception of the first protocol message that the child writes to its
162-
// stdout with ID==0 and KnownCommands populated, these are only sent in
163-
// response to a ProgRequest from the go command.
164-
//
165-
// ProgResponses can be sent in any order. The ID must match the request they're
166-
// replying to.
167-
type ProgResponse struct {
168-
ID int64 // that corresponds to ProgRequest; they can be answered out of order
169-
Err string `json:",omitempty"` // if non-empty, the error
170-
171-
// KnownCommands is included in the first message that cache helper program
172-
// writes to stdout on startup (with ID==0). It includes the
173-
// ProgRequest.Command types that are supported by the program.
174-
//
175-
// This lets the go command extend the protocol gracefully over time (adding
176-
// "get2", etc), or fail gracefully when needed. It also lets the go command
177-
// verify the program wants to be a cache helper.
178-
KnownCommands []ProgCmd `json:",omitempty"`
179-
180-
// For "get" requests.
181-
182-
Miss bool `json:",omitempty"` // cache miss
183-
OutputID []byte `json:",omitempty"` // the ObjectID stored with the body
184-
Size int64 `json:",omitempty"` // body size in bytes
185-
Time *time.Time `json:",omitempty"` // when the object was put in the cache (optional; used for cache expiration)
186-
187-
// For "get" and "put" requests.
188-
189-
// DiskPath is the absolute path on disk of the body corresponding to a
190-
// "get" (on cache hit) or "put" request's ActionID.
191-
DiskPath string `json:",omitempty"`
192-
}
193-
19467
// startCacheProg starts the prog binary (with optional space-separated flags)
19568
// and returns a Cache implementation that talks to it.
19669
//
@@ -238,14 +111,14 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache {
238111
stdout: out,
239112
stdin: in,
240113
bw: bufio.NewWriter(in),
241-
inFlight: make(map[int64]chan<- *ProgResponse),
114+
inFlight: make(map[int64]chan<- *cacheprog.ProgResponse),
242115
outputFile: make(map[OutputID]string),
243116
readLoopDone: make(chan struct{}),
244117
}
245118

246119
// Register our interest in the initial protocol message from the child to
247120
// us, saying what it can do.
248-
capResc := make(chan *ProgResponse, 1)
121+
capResc := make(chan *cacheprog.ProgResponse, 1)
249122
pc.inFlight[0] = capResc
250123

251124
pc.jenc = json.NewEncoder(pc.bw)
@@ -260,7 +133,7 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache {
260133
case <-timer.C:
261134
log.Printf("# still waiting for GOCACHEPROG %v ...", prog)
262135
case capRes := <-capResc:
263-
can := map[ProgCmd]bool{}
136+
can := map[cacheprog.ProgCmd]bool{}
264137
for _, cmd := range capRes.KnownCommands {
265138
can[cmd] = true
266139
}
@@ -277,7 +150,7 @@ func (c *ProgCache) readLoop(readLoopDone chan<- struct{}) {
277150
defer close(readLoopDone)
278151
jd := json.NewDecoder(c.stdout)
279152
for {
280-
res := new(ProgResponse)
153+
res := new(cacheprog.ProgResponse)
281154
if err := jd.Decode(res); err != nil {
282155
if c.closing.Load() {
283156
return // quietly
@@ -302,8 +175,8 @@ func (c *ProgCache) readLoop(readLoopDone chan<- struct{}) {
302175
}
303176
}
304177

305-
func (c *ProgCache) send(ctx context.Context, req *ProgRequest) (*ProgResponse, error) {
306-
resc := make(chan *ProgResponse, 1)
178+
func (c *ProgCache) send(ctx context.Context, req *cacheprog.ProgRequest) (*cacheprog.ProgResponse, error) {
179+
resc := make(chan *cacheprog.ProgResponse, 1)
307180
if err := c.writeToChild(req, resc); err != nil {
308181
return nil, err
309182
}
@@ -318,7 +191,7 @@ func (c *ProgCache) send(ctx context.Context, req *ProgRequest) (*ProgResponse,
318191
}
319192
}
320193

321-
func (c *ProgCache) writeToChild(req *ProgRequest, resc chan<- *ProgResponse) (err error) {
194+
func (c *ProgCache) writeToChild(req *cacheprog.ProgRequest, resc chan<- *cacheprog.ProgResponse) (err error) {
322195
c.mu.Lock()
323196
c.nextID++
324197
req.ID = c.nextID
@@ -369,7 +242,7 @@ func (c *ProgCache) writeToChild(req *ProgRequest, resc chan<- *ProgResponse) (e
369242
}
370243

371244
func (c *ProgCache) Get(a ActionID) (Entry, error) {
372-
if !c.can[cmdGet] {
245+
if !c.can[cacheprog.CmdGet] {
373246
// They can't do a "get". Maybe they're a write-only cache.
374247
//
375248
// TODO(bradfitz,bcmills): figure out the proper error type here. Maybe
@@ -379,8 +252,8 @@ func (c *ProgCache) Get(a ActionID) (Entry, error) {
379252
// error types on the Cache interface.
380253
return Entry{}, &entryNotFoundError{}
381254
}
382-
res, err := c.send(c.ctx, &ProgRequest{
383-
Command: cmdGet,
255+
res, err := c.send(c.ctx, &cacheprog.ProgRequest{
256+
Command: cacheprog.CmdGet,
384257
ActionID: a[:],
385258
})
386259
if err != nil {
@@ -436,7 +309,7 @@ func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64,
436309
return OutputID{}, 0, err
437310
}
438311

439-
if !c.can[cmdPut] {
312+
if !c.can[cacheprog.CmdPut] {
440313
// Child is a read-only cache. Do nothing.
441314
return out, size, nil
442315
}
@@ -448,8 +321,8 @@ func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64,
448321
deprecatedValue = out[:]
449322
}
450323

451-
res, err := c.send(c.ctx, &ProgRequest{
452-
Command: cmdPut,
324+
res, err := c.send(c.ctx, &cacheprog.ProgRequest{
325+
Command: cacheprog.CmdPut,
453326
ActionID: a[:],
454327
OutputID: out[:],
455328
ObjectID: deprecatedValue, // TODO(bradfitz): remove in Go 1.25
@@ -473,8 +346,8 @@ func (c *ProgCache) Close() error {
473346
// First write a "close" message to the child so it can exit nicely
474347
// and clean up if it wants. Only after that exchange do we cancel
475348
// the context that kills the process.
476-
if c.can[cmdClose] {
477-
_, err = c.send(c.ctx, &ProgRequest{Command: cmdClose})
349+
if c.can[cacheprog.CmdClose] {
350+
_, err = c.send(c.ctx, &cacheprog.ProgRequest{Command: cacheprog.CmdClose})
478351
}
479352
// Cancel the context, which will close the helper's stdin.
480353
c.ctxCancel()

0 commit comments

Comments
 (0)