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 | if !objInfo.Expires.IsZero() { | 91 | if !objInfo.Expires.IsZero() { |
| 92 | w.Header().Set(xhttp.Expires, objInfo.Expires.UTC().Format(http.TimeFormat)) | 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 | // Set all other user defined metadata. | 98 | // Set all other user defined metadata. |
| 96 | for k, v := range objInfo.UserDefined { | 99 | for k, v := range objInfo.UserDefined { |
| 97 | if HasPrefix(k, ReservedMetadataPrefix) { | 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 | o = ObjectInfo{ | 91 | o = ObjectInfo{ |
| 92 | Bucket: bucket, | 92 | Bucket: bucket, |
| 93 | Name: object, | 93 | Name: object, |
| 94 | CacheStatus: CacheHit, | ||
| 95 | CacheLookupStatus: CacheHit, | ||
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | // We set file info only if its valid. | 98 | // We set file info only if its valid. |
| @@ -28,6 +28,24 @@ import ( · 27 unmodified lines | |||
| 28 | "github.com/minio/minio/cmd/crypto" | 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 | type cacheControl struct { | 49 | type cacheControl struct { |
| 32 | expiry time.Time | 50 | expiry time.Time |
| 33 | maxAge int | 51 | maxAge int |
| @@ -223,7 +223,10 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string · 222 unmodified lines | |||
| 223 | } | 223 | } |
| 224 | if cc.noStore { | 224 | if cc.noStore { |
| 225 | c.cacheStats.incMiss() | 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 | default: | 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 | if !dcache.diskAvailable(objInfo.Size) { | 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 | if rs != nil { | 288 | if rs != nil { |
| @@ -290,12 +299,9 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string · 10 unmodified lines | |||
| 290 | c.put(ctx, dcache, bucket, object, bReader, bReader.ObjInfo.Size, rs, ObjectOptions{UserDefined: getMetadata(bReader.ObjInfo)}) | 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) | 302 | return bkReader, bkErr |
| 294 | } | ||
| 295 | bkReader, bkErr := c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts) | ||
| 296 | if bkErr != nil { | ||
| 297 | return nil, bkErr | ||
| 298 | } | 303 | } |
| 304 | |||
| 299 | // Initialize pipe. | 305 | // Initialize pipe. |
| 300 | pipeReader, pipeWriter := io.Pipe() | 306 | pipeReader, pipeWriter := io.Pipe() |
| 301 | teeReader := io.TeeReader(bkReader, pipeWriter) | 307 | teeReader := io.TeeReader(bkReader, pipeWriter) |
| @@ -40,6 +40,12 @@ const ( · 39 unmodified lines | |||
| 40 | Action = "Action" | 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 | // Standard S3 HTTP request constants | 49 | // Standard S3 HTTP request constants |
| 44 | const ( | 50 | const ( |
| 45 | IfModifiedSince = "If-Modified-Since" | 51 | IfModifiedSince = "If-Modified-Since" |
| @@ -143,6 +143,11 @@ type ObjectInfo struct { · 142 unmodified lines | |||
| 143 | // Date and time at which the object is no longer able to be cached | 143 | // Date and time at which the object is no longer able to be cached |
| 144 | Expires time.Time | 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 | // Specify object storage class | 151 | // Specify object storage class |
| 147 | StorageClass string | 152 | StorageClass string |
| 148 | 153 | ||