github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/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  	valid := true
   102  	// ls.Validators contains a list of one validator per chunk type.
   103  	// if one validator succeeds, then the chunk is valid
   104  	for _, v := range ls.Validators {
   105  		if valid = v.Validate(chunk.Addr, chunk.SData); valid {
   106  			break
   107  		}
   108  	}
   109  	if !valid {
   110  		log.Trace("invalid chunk", "addr", chunk.Addr, "len", len(chunk.SData))
   111  		chunk.SetErrored(ErrChunkInvalid)
   112  		chunk.markAsStored()
   113  		return
   114  	}
   115  
   116  	log.Trace("localstore.put", "addr", chunk.Addr)
   117  
   118  	ls.mu.Lock()
   119  	defer ls.mu.Unlock()
   120  
   121  	chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   122  
   123  	memChunk, err := ls.memStore.Get(ctx, chunk.Addr)
   124  	switch err {
   125  	case nil:
   126  		if memChunk.ReqC == nil {
   127  			chunk.markAsStored()
   128  			return
   129  		}
   130  	case ErrChunkNotFound:
   131  	default:
   132  		chunk.SetErrored(err)
   133  		return
   134  	}
   135  
   136  	ls.DbStore.Put(ctx, chunk)
   137  
   138  	// chunk is no longer a request, but a chunk with data, so replace it in memStore
   139  	newc := NewChunk(chunk.Addr, nil)
   140  	newc.SData = chunk.SData
   141  	newc.Size = chunk.Size
   142  	newc.dbStoredC = chunk.dbStoredC
   143  
   144  	ls.memStore.Put(ctx, newc)
   145  
   146  	if memChunk != nil && memChunk.ReqC != nil {
   147  		close(memChunk.ReqC)
   148  	}
   149  }
   150  
   151  // Get(chunk *Chunk) looks up a chunk in the local stores
   152  // This method is blocking until the chunk is retrieved
   153  // so additional timeout may be needed to wrap this call if
   154  // ChunkStores are remote and can have long latency
   155  func (ls *LocalStore) Get(ctx context.Context, addr Address) (chunk *Chunk, err error) {
   156  	ls.mu.Lock()
   157  	defer ls.mu.Unlock()
   158  
   159  	return ls.get(ctx, addr)
   160  }
   161  
   162  func (ls *LocalStore) get(ctx context.Context, addr Address) (chunk *Chunk, err error) {
   163  	chunk, err = ls.memStore.Get(ctx, addr)
   164  	if err == nil {
   165  		if chunk.ReqC != nil {
   166  			select {
   167  			case <-chunk.ReqC:
   168  			default:
   169  				metrics.GetOrRegisterCounter("localstore.get.errfetching", nil).Inc(1)
   170  				return chunk, ErrFetching
   171  			}
   172  		}
   173  		metrics.GetOrRegisterCounter("localstore.get.cachehit", nil).Inc(1)
   174  		return
   175  	}
   176  	metrics.GetOrRegisterCounter("localstore.get.cachemiss", nil).Inc(1)
   177  	chunk, err = ls.DbStore.Get(ctx, addr)
   178  	if err != nil {
   179  		metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1)
   180  		return
   181  	}
   182  	chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   183  	ls.memStore.Put(ctx, chunk)
   184  	return
   185  }
   186  
   187  // retrieve logic common for local and network chunk retrieval requests
   188  func (ls *LocalStore) GetOrCreateRequest(ctx context.Context, addr Address) (chunk *Chunk, created bool) {
   189  	metrics.GetOrRegisterCounter("localstore.getorcreaterequest", nil).Inc(1)
   190  
   191  	ls.mu.Lock()
   192  	defer ls.mu.Unlock()
   193  
   194  	var err error
   195  	chunk, err = ls.get(ctx, addr)
   196  	if err == nil && chunk.GetErrored() == nil {
   197  		metrics.GetOrRegisterCounter("localstore.getorcreaterequest.hit", nil).Inc(1)
   198  		log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v found locally", addr))
   199  		return chunk, false
   200  	}
   201  	if err == ErrFetching && chunk.GetErrored() == nil {
   202  		metrics.GetOrRegisterCounter("localstore.getorcreaterequest.errfetching", nil).Inc(1)
   203  		log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request %v", addr, chunk.ReqC))
   204  		return chunk, false
   205  	}
   206  	// no data and no request status
   207  	metrics.GetOrRegisterCounter("localstore.getorcreaterequest.miss", nil).Inc(1)
   208  	log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v not found locally. open new request", addr))
   209  	chunk = NewChunk(addr, make(chan bool))
   210  	ls.memStore.Put(ctx, chunk)
   211  	return chunk, true
   212  }
   213  
   214  // RequestsCacheLen returns the current number of outgoing requests stored in the cache
   215  func (ls *LocalStore) RequestsCacheLen() int {
   216  	return ls.memStore.requests.Len()
   217  }
   218  
   219  // Close the local store
   220  func (ls *LocalStore) Close() {
   221  	ls.DbStore.Close()
   222  }