X-Cache sets cache status of HIT if object is served from the disk cache, or MISS otherwise. X-Cache-Lookup is set to HIT if object was found in the cache even if not served (for e.g. if cache entry was invalidated by ETag verification)
Unified · Split
| @@ -91,7 +91,10 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp · 90 unmodified lines | ||
| 91 | 91 | if !objInfo.Expires.IsZero() { |
| 92 | 92 | w.Header().Set(xhttp.Expires, objInfo.Expires.UTC().Format(http.TimeFormat)) |
| 93 | 93 | } |
| 94 | − | |
| 94 | + if globalCacheConfig.Enabled { | |
| 95 | + w.Header().Set(xhttp.XCache, objInfo.CacheStatus.String()) | |
| 96 | + w.Header().Set(xhttp.XCacheLookup, objInfo.CacheLookupStatus.String()) | |
| 97 | + } | |
| 95 | 98 | // Set all other user defined metadata. |
| 96 | 99 | for k, v := range objInfo.UserDefined { |
| 97 | 100 | if HasPrefix(k, ReservedMetadataPrefix) { |
| @@ -89,8 +89,10 @@ func (m *cacheMeta) ToObjectInfo(bucket, object string) (o ObjectInfo) { · 88 unmodified lines | ||
| 89 | 89 | } |
| 90 | 90 | |
| 91 | 91 | o = ObjectInfo{ |
| 92 | − Bucket: bucket, | |
| 93 | − Name: object, | |
| 92 | + Bucket: bucket, | |
| 93 | + Name: object, | |
| 94 | + CacheStatus: CacheHit, | |
| 95 | + CacheLookupStatus: CacheHit, | |
| 94 | 96 | } |
| 95 | 97 | |
| 96 | 98 | // We set file info only if its valid. |
| @@ -28,6 +28,24 @@ import ( · 27 unmodified lines | ||
| 28 | 28 | "github.com/minio/minio/cmd/crypto" |
| 29 | 29 | ) |
| 30 | 30 | |
| 31 | +// CacheStatusType - whether the request was served from cache. | |
| 32 | +type CacheStatusType string | |
| 33 | + | |
| 34 | +const ( | |
| 35 | + // CacheHit - whether object was served from cache. | |
| 36 | + CacheHit CacheStatusType = "HIT" | |
| 37 | + | |
| 38 | + // CacheMiss - object served from backend. | |
| 39 | + CacheMiss CacheStatusType = "MISS" | |
| 40 | +) | |
| 41 | + | |
| 42 | +func (c CacheStatusType) String() string { | |
| 43 | + if c != "" { | |
| 44 | + return string(c) | |
| 45 | + } | |
| 46 | + return string(CacheMiss) | |
| 47 | +} | |
| 48 | + | |
| 31 | 49 | type cacheControl struct { |
| 32 | 50 | expiry time.Time |
| 33 | 51 | maxAge int |
| @@ -223,7 +223,10 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string · 222 unmodified lines | ||
| 223 | 223 | } |
| 224 | 224 | if cc.noStore { |
| 225 | 225 | c.cacheStats.incMiss() |
| 226 | − return c.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts) | |
| 226 | + bReader, err := c.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts) | |
| 227 | + bReader.ObjInfo.CacheLookupStatus = CacheHit | |
| 228 | + bReader.ObjInfo.CacheStatus = CacheMiss | |
| 229 | + return bReader, err | |
| 227 | 230 | } |
| 228 | 231 | } |
| 229 | 232 | |
| @@ -272,8 +275,14 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string · 42 unmodified lines | ||
| 272 | 275 | default: |
| 273 | 276 | } |
| 274 | 277 | } |
| 278 | + | |
| 279 | + bkReader, bkErr := c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts) | |
| 280 | + // Record if cache has a hit that was invalidated by ETag verification | |
| 281 | + if cacheErr == nil { | |
| 282 | + bkReader.ObjInfo.CacheLookupStatus = CacheHit | |
| 283 | + } | |
| 275 | 284 | if !dcache.diskAvailable(objInfo.Size) { |
| 276 | − return c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts) | |
| 285 | + return bkReader, bkErr | |
| 277 | 286 | } |
| 278 | 287 | |
| 279 | 288 | if rs != nil { |
| @@ -290,12 +299,9 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string · 10 unmodified lines | ||
| 290 | 299 | c.put(ctx, dcache, bucket, object, bReader, bReader.ObjInfo.Size, rs, ObjectOptions{UserDefined: getMetadata(bReader.ObjInfo)}) |
| 291 | 300 | } |
| 292 | 301 | }() |
| 293 | − return c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts) | |
| 294 | − } | |
| 295 | − bkReader, bkErr := c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts) | |
| 296 | − if bkErr != nil { | |
| 297 | − return nil, bkErr | |
| 302 | + return bkReader, bkErr | |
| 298 | 303 | } |
| 304 | + | |
| 299 | 305 | // Initialize pipe. |
| 300 | 306 | pipeReader, pipeWriter := io.Pipe() |
| 301 | 307 | teeReader := io.TeeReader(bkReader, pipeWriter) |
| @@ -40,6 +40,12 @@ const ( · 39 unmodified lines | ||
| 40 | 40 | Action = "Action" |
| 41 | 41 | ) |
| 42 | 42 | |
| 43 | +// Non standard S3 HTTP response constants | |
| 44 | +const ( | |
| 45 | + XCache = "X-Cache" | |
| 46 | + XCacheLookup = "X-Cache-Lookup" | |
| 47 | +) | |
| 48 | + | |
| 43 | 49 | // Standard S3 HTTP request constants |
| 44 | 50 | const ( |
| 45 | 51 | IfModifiedSince = "If-Modified-Since" |
| @@ -143,6 +143,11 @@ type ObjectInfo struct { · 142 unmodified lines | ||
| 143 | 143 | // Date and time at which the object is no longer able to be cached |
| 144 | 144 | Expires time.Time |
| 145 | 145 | |
| 146 | + // CacheStatus sets status of whether this is a cache hit/miss | |
| 147 | + CacheStatus CacheStatusType | |
| 148 | + // CacheLookupStatus sets whether a cacheable response is present in the cache | |
| 149 | + CacheLookupStatus CacheStatusType | |
| 150 | + | |
| 146 | 151 | // Specify object storage class |
| 147 | 152 | StorageClass string |
| 148 | 153 | |