blob: 4cdeda98a326d21a2693248ffac6f69254f421ca [file] [log] [blame]
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package storecache is a caching proxy between a client and all stores.
// References are stored as files in the local file system.
package storecache
import (
"fmt"
"path"
"upspin.io/errors"
"upspin.io/log"
"upspin.io/upspin"
)
// server implements upspin.Storeserver.
type server struct {
cfg upspin.Config
// The on disk cache.
cache *storeCache
// The store server this dialed server should talk to.
authority upspin.Endpoint
}
// New creates a new store cache that implements upspin.StoreServer.
// For writeback caches, it also returns a function to flush Blocks
// that are waiting to be written back. This is important to allow
// the client to flush out Access file blocks before writing the
// DirEntry.
func New(cfg upspin.Config, cacheDir string, maxBytes int64, writethrough bool) (upspin.StoreServer, func(upspin.Location), error) {
c, blockFlusher, err := newCache(cfg, path.Join(cacheDir, "storecache"), path.Join(cacheDir, "storewritebackqueue"), maxBytes, writethrough)
if err != nil {
return nil, nil, err
}
return &server{
cfg: cfg,
cache: c,
}, blockFlusher, nil
}
func (s *server) Dial(config upspin.Config, e upspin.Endpoint) (upspin.Service, error) {
s2 := *s
s2.authority = e
return &s2, nil
}
var errNotDialed = errors.Str("store/cache: can't handle request to unassigned authority (must dial first)")
func (s *server) Get(ref upspin.Reference) ([]byte, *upspin.Refdata, []upspin.Location, error) {
if s.authority.Transport == upspin.Unassigned {
return nil, nil, nil, errNotDialed
}
op := logf("Get %q", ref)
// Do not pass on the HTTP base from the underlying storage
// or subsequent Gets will bypass the cache.
if ref == upspin.HTTPBaseMetadata {
return nil, nil, nil, op.error(errors.E(errors.NotExist))
}
data, locs, err := s.cache.get(s.cfg, ref, s.authority)
if err != nil {
return nil, nil, nil, op.error(err)
}
refdata := &upspin.Refdata{
Reference: ref,
Volatile: false, // TODO
Duration: 0, // TODO
}
return data, refdata, locs, nil
}
func (s *server) Put(data []byte) (*upspin.Refdata, error) {
if s.authority.Transport == upspin.Unassigned {
return nil, errNotDialed
}
op := logf("Put %.30x...", data)
ref, err := s.cache.put(s.cfg, data, s.authority)
if err != nil {
return nil, op.error(err)
}
refdata := &upspin.Refdata{
Reference: ref,
Volatile: false, // TODO
Duration: 0, // TODO
}
return refdata, nil
}
// Delete implements proto.StoreServer.
func (s *server) Delete(ref upspin.Reference) error {
if s.authority.Transport == upspin.Unassigned {
return errNotDialed
}
op := logf("Delete %q", ref)
err := s.cache.delete(s.cfg, ref, s.authority)
if err != nil {
return op.error(err)
}
return nil
}
func (s *server) Endpoint() upspin.Endpoint { return s.authority }
func (s *server) Close() {}
func logf(format string, args ...interface{}) operation {
s := fmt.Sprintf(format, args...)
log.Debug.Print("store/storecache: " + s)
return operation(s)
}
type operation string
func (op operation) error(err error) error {
logf("%v failed: %v", op, err)
return errors.E(errors.Op("store/storecache."+string(op)), err)
}