github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/swarm/storage/memstore.go (about)

     1  // Copyright 2018 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  // memory storage layer for the package blockhash
    18  
    19  package storage
    20  
    21  import (
    22  	"context"
    23  	"sync"
    24  
    25  	lru "github.com/hashicorp/golang-lru"
    26  )
    27  
    28  type MemStore struct {
    29  	cache    *lru.Cache
    30  	requests *lru.Cache
    31  	mu       sync.RWMutex
    32  	disabled bool
    33  }
    34  
    35  //NewMemStore is instantiating a MemStore cache. We are keeping a record of all outgoing requests for chunks, that
    36  //should later be delivered by peer nodes, in the `requests` LRU cache. We are also keeping all frequently requested
    37  //chunks in the `cache` LRU cache.
    38  //
    39  //`requests` LRU cache capacity should ideally never be reached, this is why for the time being it should be initialised
    40  //with the same value as the LDBStore capacity.
    41  func NewMemStore(params *StoreParams, _ *LDBStore) (m *MemStore) {
    42  	if params.CacheCapacity == 0 {
    43  		return &MemStore{
    44  			disabled: true,
    45  		}
    46  	}
    47  
    48  	onEvicted := func(key interface{}, value interface{}) {
    49  		v := value.(*Chunk)
    50  		<-v.dbStoredC
    51  	}
    52  	c, err := lru.NewWithEvict(int(params.CacheCapacity), onEvicted)
    53  	if err != nil {
    54  		panic(err)
    55  	}
    56  
    57  	requestEvicted := func(key interface{}, value interface{}) {
    58  		// temporary remove of the error log, until we figure out the problem, as it is too spamy
    59  		//log.Error("evict called on outgoing request")
    60  	}
    61  	r, err := lru.NewWithEvict(int(params.ChunkRequestsCacheCapacity), requestEvicted)
    62  	if err != nil {
    63  		panic(err)
    64  	}
    65  
    66  	return &MemStore{
    67  		cache:    c,
    68  		requests: r,
    69  	}
    70  }
    71  
    72  func (m *MemStore) Get(ctx context.Context, addr Address) (*Chunk, error) {
    73  	if m.disabled {
    74  		return nil, ErrChunkNotFound
    75  	}
    76  
    77  	m.mu.RLock()
    78  	defer m.mu.RUnlock()
    79  
    80  	r, ok := m.requests.Get(string(addr))
    81  	// it is a request
    82  	if ok {
    83  		return r.(*Chunk), nil
    84  	}
    85  
    86  	// it is not a request
    87  	c, ok := m.cache.Get(string(addr))
    88  	if !ok {
    89  		return nil, ErrChunkNotFound
    90  	}
    91  	return c.(*Chunk), nil
    92  }
    93  
    94  func (m *MemStore) Put(ctx context.Context, c *Chunk) {
    95  	if m.disabled {
    96  		return
    97  	}
    98  
    99  	m.mu.Lock()
   100  	defer m.mu.Unlock()
   101  
   102  	// it is a request
   103  	if c.ReqC != nil {
   104  		select {
   105  		case <-c.ReqC:
   106  			if c.GetErrored() != nil {
   107  				m.requests.Remove(string(c.Addr))
   108  				return
   109  			}
   110  			m.cache.Add(string(c.Addr), c)
   111  			m.requests.Remove(string(c.Addr))
   112  		default:
   113  			m.requests.Add(string(c.Addr), c)
   114  		}
   115  		return
   116  	}
   117  
   118  	// it is not a request
   119  	m.cache.Add(string(c.Addr), c)
   120  	m.requests.Remove(string(c.Addr))
   121  }
   122  
   123  func (m *MemStore) setCapacity(n int) {
   124  	if n <= 0 {
   125  		m.disabled = true
   126  	} else {
   127  		onEvicted := func(key interface{}, value interface{}) {
   128  			v := value.(*Chunk)
   129  			<-v.dbStoredC
   130  		}
   131  		c, err := lru.NewWithEvict(n, onEvicted)
   132  		if err != nil {
   133  			panic(err)
   134  		}
   135  
   136  		r, err := lru.New(defaultChunkRequestsCacheCapacity)
   137  		if err != nil {
   138  			panic(err)
   139  		}
   140  
   141  		m = &MemStore{
   142  			cache:    c,
   143  			requests: r,
   144  		}
   145  	}
   146  }
   147  
   148  func (s *MemStore) Close() {}