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