Skip to content

Commit e46df40

Browse files
committed
gopls/internal/lsp/filecache: delayed tweaks from code review
These were supposed to be part of CL 495800 but I failed to notice that 'git codereview mail' failed due to my failure to refresh my SSO certs. Sorry for the fumble. Change-Id: I8e08d2624cc365defc6f5848e9178267f313917d Reviewed-on: https://go-review.googlesource.com/c/tools/+/496436 Run-TryBot: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 3df69b8 commit e46df40

File tree

1 file changed

+25
-10
lines changed

1 file changed

+25
-10
lines changed

gopls/internal/lsp/filecache/filecache.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func Set(kind string, key [32]byte, value []byte) error {
164164
}
165165
// Avoiding O_TRUNC here is merely an optimization to avoid
166166
// cache misses when two threads race to write the same file.
167-
if err := writeFileNoTrunc(casName, value, 0666); err != nil {
167+
if err := writeFileNoTrunc(casName, value, 0600); err != nil {
168168
os.Remove(casName) // ignore error
169169
return err // e.g. disk full
170170
}
@@ -178,7 +178,7 @@ func Set(kind string, key [32]byte, value []byte) error {
178178
if err := os.MkdirAll(filepath.Dir(indexName), 0700); err != nil {
179179
return err
180180
}
181-
if err := writeFileNoTrunc(indexName, hash[:], 0666); err != nil {
181+
if err := writeFileNoTrunc(indexName, hash[:], 0600); err != nil {
182182
os.Remove(indexName) // ignore error
183183
return err // e.g. disk full
184184
}
@@ -203,19 +203,23 @@ func writeFileNoTrunc(filename string, data []byte, perm os.FileMode) error {
203203
return err
204204
}
205205

206-
const casKind = "cas"
206+
const casKind = "cas" // kind for CAS (content-addressable store) files
207207

208208
var iolimit = make(chan struct{}, 128) // counting semaphore to limit I/O concurrency in Set.
209209

210210
var budget int64 = 1e9 // 1GB
211211

212-
// SetBudget sets a soft limit on disk usage of the cache (in bytes)
213-
// and returns the previous value. Supplying a negative value queries
214-
// the current value without changing it.
212+
// SetBudget sets a soft limit on disk usage of files in the cache (in
213+
// bytes) and returns the previous value. Supplying a negative value
214+
// queries the current value without changing it.
215215
//
216216
// If two gopls processes have different budgets, the one with the
217217
// lower budget will collect garbage more actively, but both will
218218
// observe the effect.
219+
//
220+
// Even in the steady state, the storage usage reported by the 'du'
221+
// command may exceed the budget by as much as 50-70% due to the
222+
// overheads of directories and the effects of block quantization.
219223
func SetBudget(new int64) (old int64) {
220224
if new < 0 {
221225
return atomic.LoadInt64(&budget)
@@ -250,6 +254,15 @@ func SetBudget(new int64) (old int64) {
250254
// practice atomic (all or nothing) on all platforms.
251255
// (See GOROOT/src/cmd/go/internal/cache/cache.go.)
252256
//
257+
// Russ Cox notes: "all file systems use an rwlock around every file
258+
// system block, including data blocks, so any writes or reads within
259+
// the same block are going to be handled atomically by the FS
260+
// implementation without any need to request file locking explicitly.
261+
// And since the files are so small, there's only one block. (A block
262+
// is at minimum 512 bytes, usually much more.)" And: "all modern file
263+
// systems protect against [partial writes due to power loss] with
264+
// journals."
265+
//
253266
// We use a two-level scheme consisting of an index and a
254267
// content-addressable store (CAS). A single cache entry consists of
255268
// two files. The value of a cache entry is written into the file at
@@ -262,10 +275,12 @@ func SetBudget(new int64) (old int64) {
262275
// Once the CAS file has been written, we write a small fixed-size
263276
// index file at filename(kind, key), using the values supplied by the
264277
// caller. The index file contains the hash that identifies the value
265-
// file in the CAS. (We could add a small amount of extra metadata to
266-
// this file if later desired.) Because the index file is small,
278+
// file in the CAS. (We could add extra metadata to this file, up to
279+
// 512B, the minimum size of a disk block, if later desired, so long
280+
// as the total size remains fixed.) Because the index file is small,
267281
// concurrent writes to it are atomic in practice, even though this is
268-
// not guaranteed by any OS.
282+
// not guaranteed by any OS. The fixed size ensures that readers can't
283+
// see a palimpsest when a short new file overwrites a longer old one.
269284
//
270285
// New versions of gopls are free to reorganize the contents of the
271286
// version directory as needs evolve. But all versions of gopls must
@@ -275,7 +290,7 @@ func SetBudget(new int64) (old int64) {
275290
// the entire gopls directory so that newer binaries can clean up
276291
// after older ones: in the development cycle especially, new
277292
// new versions may be created frequently.
278-
293+
//
279294
// TODO(adonovan): opt: use "VVVVVVVV / KK / KKKK...KKKK-kind" to
280295
// avoid creating 256 directories per distinct kind (+ cas).
281296
func filename(kind string, key [32]byte) (string, error) {

0 commit comments

Comments
 (0)