func First() DReader {
	return func(key []byte, storages []Storage) ([]byte, error) {
		for _, stor := range storages {
			data, err := stor.Get(key)
			if err == nil {
				return data, nil
			}
		}
		return nil, os.ErrNotExist
	}
}
First non-empty value for key will be used as result
package storages
import (
	"github.com/pkg/errors"
	"os"
	"strings"
	"sync"
)
type DWriter func(key, data []byte, storages []Storage) error
type DReader func(key []byte, storages []Storage) ([]byte, error)
func RedundantAll(keysDeduplication Dedup, back ...Storage) *redundant {
	return Redundant(AtLeast(len(back)), First(), keysDeduplication, back...)
}
func Redundant(writer DWriter, reader DReader, keysDeduplication Dedup, back ...Storage) *redundant {
	return &redundant{
		backed:            back,
		writer:            writer,
		reader:            reader,
		keysDeduplication: keysDeduplication,
	}
}
type redundant struct {
	backed            []Storage  
	keysDeduplication Dedup      
	writer            DWriter
	reader            DReader
	iterationLock     sync.Mutex
}
func (dt *redundant) Put(key []byte, data []byte) error {
	return dt.writer(key, data, dt.backed)
}
func (dt *redundant) Get(key []byte) ([]byte, error) {
	return dt.reader(key, dt.backed)
}
func (dt *redundant) Close() error {
	var list []error
	for _, stor := range dt.backed {
		list = append(list, stor.Close())
	}
	return allErr(list...)
}
func (dt *redundant) Del(key []byte) error {
	var list []error
	for _, stor := range dt.backed {
		err := stor.Del(key)
		if err != nil {
			list = append(list, err)
		}
	}
	return allErr(list...)
}
func (dt *redundant) Keys(handler func(key []byte) error) error {
	dt.iterationLock.Lock()
	defer dt.iterationLock.Unlock()
	var list []error
	for _, stor := range dt.backed {
		err := stor.Keys(func(key []byte) error {
			isExists, err := dt.keysDeduplication.IsDuplicated(key)
			if err != nil {
				return err
			}
			if isExists {
				return nil
			}
			err = dt.keysDeduplication.Save(key)
			if err != nil {
				return err
			}
			return handler(key)
		})
		if err != nil {
			list = append(list, err)
		}
	}
	if clearable, ok := dt.keysDeduplication.(Clearable); ok {
		_ = clearable.Clear()
	}
	return allErr(list...)
}
func allErr(list ...error) error {
	var ans []string
	for _, err := range list {
		if err != nil {
			ans = append(ans, err.Error())
		}
	}
	if len(ans) == 0 {
		return nil
	}
	return errors.New(strings.Join(ans, "; "))
}
func AtLeast(minWrite int) DWriter {
	return func(key, data []byte, storages []Storage) error {
		var wrote int
		var list []error
		for _, stor := range storages {
			err := stor.Put(key, data)
			if err != nil {
				list = append(list, err)
			} else {
				wrote++
			}
		}
		if wrote < minWrite {
			return allErr(list...)
		}
		return nil
	}
}
func Any() DWriter { return AtLeast(1) }