github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/localstore.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package storage
    18  
    19  import (
    20  	"context"
    21  	"path/filepath"
    22  	"sync"
    23  
    24  	"github.com/FusionFoundation/efsn/metrics"
    25  	"github.com/FusionFoundation/efsn/swarm/log"
    26  	"github.com/FusionFoundation/efsn/swarm/storage/mock"
    27  )
    28  
    29  type LocalStoreParams struct {
    30  	*StoreParams
    31  	ChunkDbPath string
    32  	Validators  []ChunkValidator `toml:"-"`
    33  }
    34  
    35  func NewDefaultLocalStoreParams() *LocalStoreParams {
    36  	return &LocalStoreParams{
    37  		StoreParams: NewDefaultStoreParams(),
    38  	}
    39  }
    40  
    41  //this can only finally be set after all config options (file, cmd line, env vars)
    42  //have been evaluated
    43  func (p *LocalStoreParams) Init(path string) {
    44  	if p.ChunkDbPath == "" {
    45  		p.ChunkDbPath = filepath.Join(path, "chunks")
    46  	}
    47  }
    48  
    49  // LocalStore is a combination of inmemory db over a disk persisted db
    50  // implements a Get/Put with fallback (caching) logic using any 2 ChunkStores
    51  type LocalStore struct {
    52  	Validators []ChunkValidator
    53  	memStore   *MemStore
    54  	DbStore    *LDBStore
    55  	mu         sync.Mutex
    56  }
    57  
    58  // This constructor uses MemStore and DbStore as components
    59  func NewLocalStore(params *LocalStoreParams, mockStore *mock.NodeStore) (*LocalStore, error) {
    60  	ldbparams := NewLDBStoreParams(params.StoreParams, params.ChunkDbPath)
    61  	dbStore, err := NewMockDbStore(ldbparams, mockStore)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	return &LocalStore{
    66  		memStore:   NewMemStore(params.StoreParams, dbStore),
    67  		DbStore:    dbStore,
    68  		Validators: params.Validators,
    69  	}, nil
    70  }
    71  
    72  func NewTestLocalStoreForAddr(params *LocalStoreParams) (*LocalStore, error) {
    73  	ldbparams := NewLDBStoreParams(params.StoreParams, params.ChunkDbPath)
    74  	dbStore, err := NewLDBStore(ldbparams)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	localStore := &LocalStore{
    79  		memStore:   NewMemStore(params.StoreParams, dbStore),
    80  		DbStore:    dbStore,
    81  		Validators: params.Validators,
    82  	}
    83  	return localStore, nil
    84  }
    85  
    86  // Put is responsible for doing validation and storage of the chunk
    87  // by using configured ChunkValidators, MemStore and LDBStore.
    88  // If the chunk is not valid, its GetErrored function will
    89  // return ErrChunkInvalid.
    90  // This method will check if the chunk is already in the MemStore
    91  // and it will return it if it is. If there is an error from
    92  // the MemStore.Get, it will be returned by calling GetErrored
    93  // on the chunk.
    94  // This method is responsible for closing Chunk.ReqC channel
    95  // when the chunk is stored in memstore.
    96  // After the LDBStore.Put, it is ensured that the MemStore
    97  // contains the chunk with the same data, but nil ReqC channel.
    98  func (ls *LocalStore) Put(ctx context.Context, chunk Chunk) error {
    99  	valid := true
   100  	// ls.Validators contains a list of one validator per chunk type.
   101  	// if one validator succeeds, then the chunk is valid
   102  	for _, v := range ls.Validators {
   103  		if valid = v.Validate(chunk.Address(), chunk.Data()); valid {
   104  			break
   105  		}
   106  	}
   107  	if !valid {
   108  		return ErrChunkInvalid
   109  	}
   110  
   111  	log.Trace("localstore.put", "key", chunk.Address())
   112  	ls.mu.Lock()
   113  	defer ls.mu.Unlock()
   114  
   115  	_, err := ls.memStore.Get(ctx, chunk.Address())
   116  	if err == nil {
   117  		return nil
   118  	}
   119  	if err != nil && err != ErrChunkNotFound {
   120  		return err
   121  	}
   122  	ls.memStore.Put(ctx, chunk)
   123  	err = ls.DbStore.Put(ctx, chunk)
   124  	return err
   125  }
   126  
   127  // Get(chunk *Chunk) looks up a chunk in the local stores
   128  // This method is blocking until the chunk is retrieved
   129  // so additional timeout may be needed to wrap this call if
   130  // ChunkStores are remote and can have long latency
   131  func (ls *LocalStore) Get(ctx context.Context, addr Address) (chunk Chunk, err error) {
   132  	ls.mu.Lock()
   133  	defer ls.mu.Unlock()
   134  
   135  	return ls.get(ctx, addr)
   136  }
   137  
   138  func (ls *LocalStore) get(ctx context.Context, addr Address) (chunk Chunk, err error) {
   139  	chunk, err = ls.memStore.Get(ctx, addr)
   140  
   141  	if err != nil && err != ErrChunkNotFound {
   142  		metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1)
   143  		return nil, err
   144  	}
   145  
   146  	if err == nil {
   147  		metrics.GetOrRegisterCounter("localstore.get.cachehit", nil).Inc(1)
   148  		return chunk, nil
   149  	}
   150  
   151  	metrics.GetOrRegisterCounter("localstore.get.cachemiss", nil).Inc(1)
   152  	chunk, err = ls.DbStore.Get(ctx, addr)
   153  	if err != nil {
   154  		metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1)
   155  		return nil, err
   156  	}
   157  
   158  	ls.memStore.Put(ctx, chunk)
   159  	return chunk, nil
   160  }
   161  
   162  func (ls *LocalStore) FetchFunc(ctx context.Context, addr Address) func(context.Context) error {
   163  	ls.mu.Lock()
   164  	defer ls.mu.Unlock()
   165  
   166  	_, err := ls.get(ctx, addr)
   167  	if err == nil {
   168  		return nil
   169  	}
   170  	return func(context.Context) error {
   171  		return err
   172  	}
   173  }
   174  
   175  func (ls *LocalStore) BinIndex(po uint8) uint64 {
   176  	return ls.DbStore.BinIndex(po)
   177  }
   178  
   179  func (ls *LocalStore) Iterator(from uint64, to uint64, po uint8, f func(Address, uint64) bool) error {
   180  	return ls.DbStore.SyncIterator(from, to, po, f)
   181  }
   182  
   183  // Close the local store
   184  func (ls *LocalStore) Close() {
   185  	ls.DbStore.Close()
   186  }