go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/appengine/coordinator/flex/storage_cache.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package flex
    16  
    17  import (
    18  	"bytes"
    19  	"compress/zlib"
    20  	"context"
    21  	"io"
    22  	"time"
    23  
    24  	log "go.chromium.org/luci/common/logging"
    25  	"go.chromium.org/luci/logdog/common/storage"
    26  	"go.chromium.org/luci/server/caching"
    27  )
    28  
    29  const (
    30  	// defaultCompressionThreshold is the threshold where entries will become
    31  	// compressed. If the size of data exceeds this threshold, it will be
    32  	// compressed with zlib in the cache.
    33  	defaultCompressionThreshold = 64 * 1024 // 64KiB
    34  )
    35  
    36  var storageCache = caching.RegisterLRUCache[storage.CacheKey, []byte](65535)
    37  
    38  // StorageCache implements a generic storage.Cache for Storage instances.
    39  //
    40  // This uses the process cache for the underlying cache.
    41  type StorageCache struct {
    42  	compressionThreshold int
    43  }
    44  
    45  // Get implements storage.Cache.
    46  func (sc *StorageCache) Get(c context.Context, key storage.CacheKey) ([]byte, bool) {
    47  	data, ok := storageCache.LRU(c).Get(c, key)
    48  	if !ok {
    49  		return nil, false
    50  	}
    51  
    52  	if len(data) == 0 {
    53  		// No cache item (missing or invalid/empty).
    54  		return nil, false
    55  	}
    56  
    57  	isCompressed, data := data[0], data[1:]
    58  	if isCompressed != 0x00 {
    59  		// This entry is compressed.
    60  		zr, err := zlib.NewReader(bytes.NewReader(data))
    61  		if err != nil {
    62  			log.Fields{
    63  				log.ErrorKey: err,
    64  				"key":        key,
    65  			}.Warningf(c, "Failed to create ZLIB reader.")
    66  			return nil, false
    67  		}
    68  		defer zr.Close()
    69  
    70  		if data, err = io.ReadAll(zr); err != nil {
    71  			log.Fields{
    72  				log.ErrorKey: err,
    73  				"key":        key,
    74  			}.Warningf(c, "Failed to decompress cached item.")
    75  			return nil, false
    76  		}
    77  	}
    78  	return data, true
    79  }
    80  
    81  // Put implements storage.Cache.
    82  func (sc *StorageCache) Put(c context.Context, key storage.CacheKey, data []byte, exp time.Duration) {
    83  	ct := sc.compressionThreshold
    84  	if ct <= 0 {
    85  		ct = defaultCompressionThreshold
    86  	}
    87  
    88  	var buf bytes.Buffer
    89  	buf.Grow(len(data) + 1)
    90  
    91  	if len(data) < ct {
    92  		// Do not compress the item. Write a "0x00" to indicate that it is
    93  		// not compressed.
    94  		if err := buf.WriteByte(0x00); err != nil {
    95  			log.WithError(err).Warningf(c, "Failed to write compression byte.")
    96  			return
    97  		}
    98  		if _, err := buf.Write(data); err != nil {
    99  			log.WithError(err).Warningf(c, "Failed to write storage cache data.")
   100  			return
   101  		}
   102  	} else {
   103  		// Compress the item. Write a "0x01" to indicate that it is compressed.
   104  		if err := buf.WriteByte(0x01); err != nil {
   105  			log.WithError(err).Warningf(c, "Failed to write compression byte.")
   106  			return
   107  		}
   108  
   109  		zw := zlib.NewWriter(&buf)
   110  		if _, err := zw.Write(data); err != nil {
   111  			log.WithError(err).Warningf(c, "Failed to compress storage cache data.")
   112  			zw.Close()
   113  			return
   114  		}
   115  		if err := zw.Close(); err != nil {
   116  			log.WithError(err).Warningf(c, "Failed to close compressed storage cache data.")
   117  			return
   118  		}
   119  	}
   120  
   121  	storageCache.LRU(c).Put(c, key, buf.Bytes(), exp)
   122  }