|
|
|
@ -14,6 +14,7 @@ import ( |
|
|
|
|
|
|
|
|
|
"github.com/djherbis/atime" |
|
|
|
|
"github.com/minio/minio/cmd/logger" |
|
|
|
|
"github.com/minio/minio/pkg/hash" |
|
|
|
|
"github.com/minio/minio/pkg/wildcard" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -35,6 +36,7 @@ type CacheObjectLayer interface { |
|
|
|
|
GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) |
|
|
|
|
DeleteObject(ctx context.Context, bucket, object string) error |
|
|
|
|
DeleteObjects(ctx context.Context, bucket string, objects []string) ([]error, error) |
|
|
|
|
PutObject(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) |
|
|
|
|
// Storage operations.
|
|
|
|
|
StorageInfo(ctx context.Context) CacheStorageInfo |
|
|
|
|
} |
|
|
|
@ -58,6 +60,7 @@ type cacheObjects struct { |
|
|
|
|
GetObjectInfoFn func(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) |
|
|
|
|
DeleteObjectFn func(ctx context.Context, bucket, object string) error |
|
|
|
|
DeleteObjectsFn func(ctx context.Context, bucket string, objects []string) ([]error, error) |
|
|
|
|
PutObjectFn func(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *cacheObjects) delete(ctx context.Context, dcache *diskCache, bucket, object string) (err error) { |
|
|
|
@ -231,7 +234,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string |
|
|
|
|
pipeReader, pipeWriter := io.Pipe() |
|
|
|
|
teeReader := io.TeeReader(bkReader, pipeWriter) |
|
|
|
|
go func() { |
|
|
|
|
putErr := dcache.Put(ctx, bucket, object, io.LimitReader(pipeReader, bkReader.ObjInfo.Size), bkReader.ObjInfo.Size, ObjectOptions{UserDefined: getMetadata(bkReader.ObjInfo)}) |
|
|
|
|
putErr := c.put(ctx, dcache, bucket, object, io.LimitReader(pipeReader, bkReader.ObjInfo.Size), bkReader.ObjInfo.Size, ObjectOptions{UserDefined: getMetadata(bkReader.ObjInfo)}) |
|
|
|
|
// close the write end of the pipe, so the error gets
|
|
|
|
|
// propagated to getObjReader
|
|
|
|
|
pipeWriter.CloseWithError(putErr) |
|
|
|
@ -483,6 +486,84 @@ func (c *cacheObjects) migrateCacheFromV1toV2(ctx context.Context) { |
|
|
|
|
logger.StartupMessage(colorBlue("Cache migration completed successfully.")) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// PutObject - caches the uploaded object for single Put operations
|
|
|
|
|
func (c *cacheObjects) PutObject(ctx context.Context, bucket, object string, r *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
|
putObjectFn := c.PutObjectFn |
|
|
|
|
data := r.rawReader |
|
|
|
|
dcache, err := c.getCacheToLoc(ctx, bucket, object) |
|
|
|
|
if err != nil { |
|
|
|
|
// disk cache could not be located,execute backend call.
|
|
|
|
|
return putObjectFn(ctx, bucket, object, r, opts) |
|
|
|
|
} |
|
|
|
|
size := r.Size() |
|
|
|
|
if c.skipCache() { |
|
|
|
|
return putObjectFn(ctx, bucket, object, r, opts) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// fetch from backend if there is no space on cache drive
|
|
|
|
|
if !dcache.diskAvailable(size) { |
|
|
|
|
return putObjectFn(ctx, bucket, object, r, opts) |
|
|
|
|
} |
|
|
|
|
if opts.ServerSideEncryption != nil { |
|
|
|
|
return putObjectFn(ctx, bucket, object, r, opts) |
|
|
|
|
} |
|
|
|
|
// fetch from backend if cache exclude pattern or cache-control
|
|
|
|
|
// directive set to exclude
|
|
|
|
|
if c.isCacheExclude(bucket, object) { |
|
|
|
|
dcache.Delete(ctx, bucket, object) |
|
|
|
|
return putObjectFn(ctx, bucket, object, r, opts) |
|
|
|
|
} |
|
|
|
|
// Initialize pipe to stream data to backend
|
|
|
|
|
pipeReader, pipeWriter := io.Pipe() |
|
|
|
|
hashReader, err := hash.NewReader(pipeReader, size, "", "", data.ActualSize(), globalCLIContext.StrictS3Compat) |
|
|
|
|
if err != nil { |
|
|
|
|
return ObjectInfo{}, err |
|
|
|
|
} |
|
|
|
|
// Initialize pipe to stream data to cache
|
|
|
|
|
rPipe, wPipe := io.Pipe() |
|
|
|
|
|
|
|
|
|
oinfoCh := make(chan ObjectInfo) |
|
|
|
|
errCh := make(chan error) |
|
|
|
|
go func() { |
|
|
|
|
oinfo, perr := putObjectFn(ctx, bucket, object, NewPutObjReader(hashReader, nil, nil), opts) |
|
|
|
|
if perr != nil { |
|
|
|
|
pipeWriter.CloseWithError(perr) |
|
|
|
|
wPipe.CloseWithError(perr) |
|
|
|
|
close(oinfoCh) |
|
|
|
|
errCh <- perr |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
close(errCh) |
|
|
|
|
oinfoCh <- oinfo |
|
|
|
|
}() |
|
|
|
|
// get a namespace lock on cache until cache is filled.
|
|
|
|
|
cLock := c.nsMutex.NewNSLock(ctx, bucket, object) |
|
|
|
|
if err := cLock.GetLock(globalObjectTimeout); err != nil { |
|
|
|
|
return ObjectInfo{}, err |
|
|
|
|
} |
|
|
|
|
defer cLock.Unlock() |
|
|
|
|
go func() { |
|
|
|
|
if err = dcache.Put(ctx, bucket, object, rPipe, data.Size(), opts); err != nil { |
|
|
|
|
wPipe.CloseWithError(err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
mwriter := io.MultiWriter(pipeWriter, wPipe) |
|
|
|
|
_, err = io.Copy(mwriter, data) |
|
|
|
|
if err != nil { |
|
|
|
|
err = <-errCh |
|
|
|
|
return objInfo, err |
|
|
|
|
} |
|
|
|
|
pipeWriter.Close() |
|
|
|
|
wPipe.Close() |
|
|
|
|
objInfo = <-oinfoCh |
|
|
|
|
dcache.updateETag(ctx, bucket, object, objInfo.ETag) |
|
|
|
|
|
|
|
|
|
return objInfo, err |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns cacheObjects for use by Server.
|
|
|
|
|
func newServerCacheObjects(ctx context.Context, config CacheConfig) (CacheObjectLayer, error) { |
|
|
|
|
// list of disk caches for cache "drives" specified in config.json or MINIO_CACHE_DRIVES env var.
|
|
|
|
@ -513,6 +594,9 @@ func newServerCacheObjects(ctx context.Context, config CacheConfig) (CacheObject |
|
|
|
|
} |
|
|
|
|
return errs, nil |
|
|
|
|
}, |
|
|
|
|
PutObjectFn: func(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
|
return newObjectLayerFn().PutObject(ctx, bucket, object, data, opts) |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
if migrateSw { |
|
|
|
|
go c.migrateCacheFromV1toV2(ctx) |
|
|
|
|