1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
17 var DefaultBlockCache = &BlockCache{}
19 type BlockCache struct {
20 // Maximum number of blocks to keep in the cache. If 0, a
21 // default size (currently 4) is used instead.
24 cache map[string]*cacheBlock
28 const defaultMaxBlocks = 4
30 // Sweep deletes the least recently used blocks from the cache until
31 // there are no more than MaxBlocks left.
32 func (c *BlockCache) Sweep() {
35 max = defaultMaxBlocks
39 if len(c.cache) <= max {
42 lru := make([]time.Time, 0, len(c.cache))
43 for _, b := range c.cache {
44 lru = append(lru, b.lastUse)
46 sort.Sort(sort.Reverse(timeSlice(lru)))
48 for loc, b := range c.cache {
49 if !b.lastUse.After(threshold) {
55 // ReadAt returns data from the cache, first retrieving it from Keep if
57 func (c *BlockCache) ReadAt(kc *KeepClient, locator string, p []byte, off int) (int, error) {
58 buf, err := c.Get(kc, locator)
63 return 0, io.ErrUnexpectedEOF
65 return copy(p, buf[off:]), nil
68 // Get returns data from the cache, first retrieving it from Keep if
70 func (c *BlockCache) Get(kc *KeepClient, locator string) ([]byte, error) {
71 cacheKey := locator[:32]
73 if parts := strings.SplitN(locator, "+", 3); len(parts) >= 2 {
74 datasize, err := strconv.ParseInt(parts[1], 10, 32)
75 if err == nil && datasize >= 0 {
76 bufsize = int(datasize)
81 c.cache = make(map[string]*cacheBlock)
83 b, ok := c.cache[cacheKey]
84 if !ok || b.err != nil {
86 fetched: make(chan struct{}),
91 rdr, size, _, err := kc.Get(locator)
94 data = make([]byte, size, bufsize)
95 _, err = io.ReadFull(rdr, data)
97 if err == nil && err2 != nil {
98 err = fmt.Errorf("close(): %w", err2)
101 err = fmt.Errorf("Get %s: %w", locator, err)
105 b.data, b.err = data, err
113 // Wait (with mtx unlocked) for the fetch goroutine to finish,
114 // in case it hasn't already.
118 b.lastUse = time.Now()
123 func (c *BlockCache) Clear() {
129 type timeSlice []time.Time
131 func (ts timeSlice) Len() int { return len(ts) }
133 func (ts timeSlice) Less(i, j int) bool { return ts[i].Before(ts[j]) }
135 func (ts timeSlice) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] }
137 type cacheBlock struct {
140 fetched chan struct{}