github.com/gobitfly/go-ethereum@v1.8.12/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  	"encoding/binary"
    21  	"fmt"
    22  	"path/filepath"
    23  	"sync"
    24  
    25  	"github.com/ethereum/go-ethereum/metrics"
    26  	"github.com/ethereum/go-ethereum/swarm/log"
    27  	"github.com/ethereum/go-ethereum/swarm/storage/mock"
    28  )
    29  
    30  type LocalStoreParams struct {
    31  	*StoreParams
    32  	ChunkDbPath string
    33  	Validators  []ChunkValidator `toml:"-"`
    34  }
    35  
    36  func NewDefaultLocalStoreParams() *LocalStoreParams {
    37  	return &LocalStoreParams{
    38  		StoreParams: NewDefaultStoreParams(),
    39  	}
    40  }
    41  
    42  //this can only finally be set after all config options (file, cmd line, env vars)
    43  //have been evaluated
    44  func (p *LocalStoreParams) Init(path string) {
    45  	if p.ChunkDbPath == "" {
    46  		p.ChunkDbPath = filepath.Join(path, "chunks")
    47  	}
    48  }
    49  
    50  // LocalStore is a combination of inmemory db over a disk persisted db
    51  // implements a Get/Put with fallback (caching) logic using any 2 ChunkStores
    52  type LocalStore struct {
    53  	Validators []ChunkValidator
    54  	memStore   *MemStore
    55  	DbStore    *LDBStore
    56  	mu         sync.Mutex
    57  }
    58  
    59  // This constructor uses MemStore and DbStore as components
    60  func NewLocalStore(params *LocalStoreParams, mockStore *mock.NodeStore) (*LocalStore, error) {
    61  	ldbparams := NewLDBStoreParams(params.StoreParams, params.ChunkDbPath)
    62  	dbStore, err := NewMockDbStore(ldbparams, mockStore)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	return &LocalStore{
    67  		memStore:   NewMemStore(params.StoreParams, dbStore),
    68  		DbStore:    dbStore,
    69  		Validators: params.Validators,
    70  	}, nil
    71  }
    72  
    73  func NewTestLocalStoreForAddr(params *LocalStoreParams) (*LocalStore, error) {
    74  	ldbparams := NewLDBStoreParams(params.StoreParams, params.ChunkDbPath)
    75  	dbStore, err := NewLDBStore(ldbparams)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	localStore := &LocalStore{
    80  		memStore:   NewMemStore(params.StoreParams, dbStore),
    81  		DbStore:    dbStore,
    82  		Validators: params.Validators,
    83  	}
    84  	return localStore, nil
    85  }
    86  
    87  // Put is responsible for doing validation and storage of the chunk
    88  // by using configured ChunkValidators, MemStore and LDBStore.
    89  // If the chunk is not valid, its GetErrored function will
    90  // return ErrChunkInvalid.
    91  // This method will check if the chunk is already in the MemStore
    92  // and it will return it if it is. If there is an error from
    93  // the MemStore.Get, it will be returned by calling GetErrored
    94  // on the chunk.
    95  // This method is responsible for closing Chunk.ReqC channel
    96  // when the chunk is stored in memstore.
    97  // After the LDBStore.Put, it is ensured that the MemStore
    98  // contains the chunk with the same data, but nil ReqC channel.
    99  func (ls *LocalStore) Put(chunk *Chunk) {
   100  	if l := len(chunk.SData); l < 9 {
   101  		log.Debug("incomplete chunk data", "addr", chunk.Addr, "length", l)
   102  		chunk.SetErrored(ErrChunkInvalid)
   103  		chunk.markAsStored()
   104  		return
   105  	}
   106  	valid := true
   107  	for _, v := range ls.Validators {
   108  		if valid = v.Validate(chunk.Addr, chunk.SData); valid {
   109  			break
   110  		}
   111  	}
   112  	if !valid {
   113  		log.Trace("invalid content address", "addr", chunk.Addr)
   114  		chunk.SetErrored(ErrChunkInvalid)
   115  		chunk.markAsStored()
   116  		return
   117  	}
   118  
   119  	log.Trace("localstore.put", "addr", chunk.Addr)
   120  
   121  	ls.mu.Lock()
   122  	defer ls.mu.Unlock()
   123  
   124  	chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   125  
   126  	memChunk, err := ls.memStore.Get(chunk.Addr)
   127  	switch err {
   128  	case nil:
   129  		if memChunk.ReqC == nil {
   130  			chunk.markAsStored()
   131  			return
   132  		}
   133  	case ErrChunkNotFound:
   134  	default:
   135  		chunk.SetErrored(err)
   136  		return
   137  	}
   138  
   139  	ls.DbStore.Put(chunk)
   140  
   141  	// chunk is no longer a request, but a chunk with data, so replace it in memStore
   142  	newc := NewChunk(chunk.Addr, nil)
   143  	newc.SData = chunk.SData
   144  	newc.Size = chunk.Size
   145  	newc.dbStoredC = chunk.dbStoredC
   146  
   147  	ls.memStore.Put(newc)
   148  
   149  	if memChunk != nil && memChunk.ReqC != nil {
   150  		close(memChunk.ReqC)
   151  	}
   152  }
   153  
   154  // Get(chunk *Chunk) looks up a chunk in the local stores
   155  // This method is blocking until the chunk is retrieved
   156  // so additional timeout may be needed to wrap this call if
   157  // ChunkStores are remote and can have long latency
   158  func (ls *LocalStore) Get(addr Address) (chunk *Chunk, err error) {
   159  	ls.mu.Lock()
   160  	defer ls.mu.Unlock()
   161  
   162  	return ls.get(addr)
   163  }
   164  
   165  func (ls *LocalStore) get(addr Address) (chunk *Chunk, err error) {
   166  	chunk, err = ls.memStore.Get(addr)
   167  	if err == nil {
   168  		if chunk.ReqC != nil {
   169  			select {
   170  			case <-chunk.ReqC:
   171  			default:
   172  				metrics.GetOrRegisterCounter("localstore.get.errfetching", nil).Inc(1)
   173  				return chunk, ErrFetching
   174  			}
   175  		}
   176  		metrics.GetOrRegisterCounter("localstore.get.cachehit", nil).Inc(1)
   177  		return
   178  	}
   179  	metrics.GetOrRegisterCounter("localstore.get.cachemiss", nil).Inc(1)
   180  	chunk, err = ls.DbStore.Get(addr)
   181  	if err != nil {
   182  		metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1)
   183  		return
   184  	}
   185  	chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   186  	ls.memStore.Put(chunk)
   187  	return
   188  }
   189  
   190  // retrieve logic common for local and network chunk retrieval requests
   191  func (ls *LocalStore) GetOrCreateRequest(addr Address) (chunk *Chunk, created bool) {
   192  	metrics.GetOrRegisterCounter("localstore.getorcreaterequest", nil).Inc(1)
   193  
   194  	ls.mu.Lock()
   195  	defer ls.mu.Unlock()
   196  
   197  	var err error
   198  	chunk, err = ls.get(addr)
   199  	if err == nil && chunk.GetErrored() == nil {
   200  		metrics.GetOrRegisterCounter("localstore.getorcreaterequest.hit", nil).Inc(1)
   201  		log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v found locally", addr))
   202  		return chunk, false
   203  	}
   204  	if err == ErrFetching && chunk.GetErrored() == nil {
   205  		metrics.GetOrRegisterCounter("localstore.getorcreaterequest.errfetching", nil).Inc(1)
   206  		log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request %v", addr, chunk.ReqC))
   207  		return chunk, false
   208  	}
   209  	// no data and no request status
   210  	metrics.GetOrRegisterCounter("localstore.getorcreaterequest.miss", nil).Inc(1)
   211  	log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v not found locally. open new request", addr))
   212  	chunk = NewChunk(addr, make(chan bool))
   213  	ls.memStore.Put(chunk)
   214  	return chunk, true
   215  }
   216  
   217  // RequestsCacheLen returns the current number of outgoing requests stored in the cache
   218  func (ls *LocalStore) RequestsCacheLen() int {
   219  	return ls.memStore.requests.Len()
   220  }
   221  
   222  // Close the local store
   223  func (ls *LocalStore) Close() {
   224  	ls.DbStore.Close()
   225  }