1 // A Volume is an interface representing a Keep back-end storage unit:
2 // for example, a single mounted disk, a RAID array, an Amazon S3 volume,
15 type Volume interface {
16 Get(loc string) ([]byte, error)
17 Put(loc string, block []byte) error
18 Touch(loc string) error
19 Mtime(loc string) (time.Time, error)
20 Index(prefix string) string
21 Delete(loc string) error
22 Status() *VolumeStatus
26 // MockVolumes are Volumes used to test the Keep front end.
28 // If the Bad field is true, this volume should return an error
29 // on all writes and puts.
31 // The Touchable field signifies whether the Touch method will
32 // succeed. Defaults to true. Note that Bad and Touchable are
33 // independent: a MockVolume may be set up so that Put fails but Touch
34 // works or vice versa.
36 // TODO(twp): rename Bad to something more descriptive, e.g. Writable,
37 // and make sure that the tests that rely on it are testing the right
38 // thing. We may need to simulate Writable, Touchable and Corrupt
39 // volumes in different ways.
41 type MockVolume struct {
42 Store map[string][]byte
43 Timestamps map[string]time.Time
48 func CreateMockVolume() *MockVolume {
50 Store: make(map[string][]byte),
51 Timestamps: make(map[string]time.Time),
57 func (v *MockVolume) Get(loc string) ([]byte, error) {
59 return nil, errors.New("Bad volume")
60 } else if block, ok := v.Store[loc]; ok {
63 return nil, os.ErrNotExist
66 func (v *MockVolume) Put(loc string, block []byte) error {
68 return errors.New("Bad volume")
74 func (v *MockVolume) Touch(loc string) error {
76 v.Timestamps[loc] = time.Now()
79 return errors.New("Touch failed")
82 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
86 err = errors.New("Bad volume")
87 } else if t, ok := v.Timestamps[loc]; ok {
95 func (v *MockVolume) Index(prefix string) string {
97 for loc, block := range v.Store {
98 if IsValidLocator(loc) && strings.HasPrefix(loc, prefix) {
99 result = result + fmt.Sprintf("%s+%d %d\n",
100 loc, len(block), 123456789)
106 func (v *MockVolume) Delete(loc string) error {
107 if _, ok := v.Store[loc]; ok {
108 if time.Since(v.Timestamps[loc]) < permission_ttl {
114 return os.ErrNotExist
117 func (v *MockVolume) Status() *VolumeStatus {
119 for _, block := range v.Store {
120 used = used + uint64(len(block))
122 return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
125 func (v *MockVolume) String() string {
126 return "[MockVolume]"
129 // A VolumeManager manages a collection of volumes.
131 // - Volumes is a slice of available Volumes.
132 // - Choose() returns a Volume suitable for writing to.
133 // - Quit() instructs the VolumeManager to shut down gracefully.
135 type VolumeManager interface {
141 type RRVolumeManager struct {
143 nextwrite chan Volume
147 func MakeRRVolumeManager(vols []Volume) *RRVolumeManager {
148 // Create a new VolumeManager struct with the specified volumes,
149 // and with new Nextwrite and Quit channels.
150 // The Quit channel is buffered with a capacity of 1 so that
151 // another routine may write to it without blocking.
152 vm := &RRVolumeManager{vols, make(chan Volume), make(chan int, 1)}
154 // This goroutine implements round-robin volume selection.
155 // It sends each available Volume in turn to the Nextwrite
156 // channel, until receiving a notification on the Quit channel
157 // that it should terminate.
164 case vm.nextwrite <- vm.volumes[i]:
165 i = (i + 1) % len(vm.volumes)
173 func (vm *RRVolumeManager) Volumes() []Volume {
177 func (vm *RRVolumeManager) Choose() Volume {
178 return <-vm.nextwrite
181 func (vm *RRVolumeManager) Quit() {