mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-24 10:07:21 +02:00
130 lines
2.2 KiB
Go
130 lines
2.2 KiB
Go
|
package fscache
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
type layeredCache struct {
|
||
|
layers []Cache
|
||
|
}
|
||
|
|
||
|
// NewLayered returns a Cache which stores its data in all the passed
|
||
|
// caches, when a key is requested it is loaded into all the caches above the first hit.
|
||
|
func NewLayered(caches ...Cache) Cache {
|
||
|
return &layeredCache{layers: caches}
|
||
|
}
|
||
|
|
||
|
func (l *layeredCache) Get(key string) (r ReadAtCloser, w io.WriteCloser, err error) {
|
||
|
var last ReadAtCloser
|
||
|
var writers []io.WriteCloser
|
||
|
|
||
|
for i, layer := range l.layers {
|
||
|
r, w, err = layer.Get(key)
|
||
|
if err != nil {
|
||
|
if len(writers) > 0 {
|
||
|
last.Close()
|
||
|
multiWC(writers...).Close()
|
||
|
}
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
// hit
|
||
|
if w == nil {
|
||
|
if len(writers) > 0 {
|
||
|
go func(r io.ReadCloser) {
|
||
|
wc := multiWC(writers...)
|
||
|
defer r.Close()
|
||
|
defer wc.Close()
|
||
|
io.Copy(wc, r)
|
||
|
}(r)
|
||
|
return last, nil, nil
|
||
|
}
|
||
|
return r, nil, nil
|
||
|
}
|
||
|
|
||
|
// miss
|
||
|
writers = append(writers, w)
|
||
|
|
||
|
if i == len(l.layers)-1 {
|
||
|
if last != nil {
|
||
|
last.Close()
|
||
|
}
|
||
|
return r, multiWC(writers...), nil
|
||
|
}
|
||
|
|
||
|
if last != nil {
|
||
|
last.Close()
|
||
|
}
|
||
|
last = r
|
||
|
}
|
||
|
|
||
|
return nil, nil, errors.New("no caches")
|
||
|
}
|
||
|
|
||
|
func (l *layeredCache) Remove(key string) error {
|
||
|
var grp sync.WaitGroup
|
||
|
// walk upwards so that lower layers don't
|
||
|
// restore upper layers on Get()
|
||
|
for i := len(l.layers) - 1; i >= 0; i-- {
|
||
|
grp.Add(1)
|
||
|
go func(layer Cache) {
|
||
|
defer grp.Done()
|
||
|
layer.Remove(key)
|
||
|
}(l.layers[i])
|
||
|
}
|
||
|
grp.Wait()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (l *layeredCache) Exists(key string) bool {
|
||
|
for _, layer := range l.layers {
|
||
|
if layer.Exists(key) {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (l *layeredCache) Clean() (err error) {
|
||
|
for _, layer := range l.layers {
|
||
|
er := layer.Clean()
|
||
|
if er != nil {
|
||
|
err = er
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func multiWC(wc ...io.WriteCloser) io.WriteCloser {
|
||
|
if len(wc) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return &multiWriteCloser{
|
||
|
writers: wc,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type multiWriteCloser struct {
|
||
|
writers []io.WriteCloser
|
||
|
}
|
||
|
|
||
|
func (t *multiWriteCloser) Write(p []byte) (n int, err error) {
|
||
|
for _, w := range t.writers {
|
||
|
n, err = w.Write(p)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
return len(p), nil
|
||
|
}
|
||
|
|
||
|
func (t *multiWriteCloser) Close() error {
|
||
|
for _, w := range t.writers {
|
||
|
w.Close()
|
||
|
}
|
||
|
return nil
|
||
|
}
|