github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/storage/localstore.go (about)

     1  // Copyleft 2016 The susy-graviton Authors
     2  // This file is part of the susy-graviton library.
     3  //
     4  // The susy-graviton 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 susy-graviton library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MSRCHANTABILITY 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 susy-graviton 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/susy-go/susy-graviton/metrics"
    25  	"github.com/susy-go/susy-graviton/swarm/log"
    26  	"github.com/susy-go/susy-graviton/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  // isValid returns true if chunk passes any of the LocalStore Validators.
    87  // isValid also returns true if LocalStore has no Validators.
    88  func (ls *LocalStore) isValid(chunk Chunk) bool {
    89  	// by default chunks are valid. if we have 0 validators, then all chunks are valid.
    90  	valid := true
    91  
    92  	// ls.Validators contains a list of one validator per chunk type.
    93  	// if one validator succeeds, then the chunk is valid
    94  	for _, v := range ls.Validators {
    95  		if valid = v.Validate(chunk); valid {
    96  			break
    97  		}
    98  	}
    99  	return valid
   100  }
   101  
   102  // Put is responsible for doing validation and storage of the chunk
   103  // by using configured ChunkValidators, MemStore and LDBStore.
   104  // If the chunk is not valid, its GetErrored function will
   105  // return ErrChunkInvalid.
   106  // This method will check if the chunk is already in the MemStore
   107  // and it will return it if it is. If there is an error from
   108  // the MemStore.Get, it will be returned by calling GetErrored
   109  // on the chunk.
   110  // This method is responsible for closing Chunk.ReqC channel
   111  // when the chunk is stored in memstore.
   112  // After the LDBStore.Put, it is ensured that the MemStore
   113  // contains the chunk with the same data, but nil ReqC channel.
   114  func (ls *LocalStore) Put(ctx context.Context, chunk Chunk) error {
   115  	if !ls.isValid(chunk) {
   116  		return ErrChunkInvalid
   117  	}
   118  
   119  	log.Trace("localstore.put", "key", chunk.Address())
   120  	ls.mu.Lock()
   121  	defer ls.mu.Unlock()
   122  
   123  	_, err := ls.memStore.Get(ctx, chunk.Address())
   124  	if err == nil {
   125  		return nil
   126  	}
   127  	if err != nil && err != ErrChunkNotFound {
   128  		return err
   129  	}
   130  	ls.memStore.Put(ctx, chunk)
   131  	err = ls.DbStore.Put(ctx, chunk)
   132  	return err
   133  }
   134  
   135  // Has queries the underlying DbStore if a chunk with the given address
   136  // is being stored there.
   137  // Returns true if it is stored, false if not
   138  func (ls *LocalStore) Has(ctx context.Context, addr Address) bool {
   139  	return ls.DbStore.Has(ctx, addr)
   140  }
   141  
   142  // Get(chunk *Chunk) looks up a chunk in the local stores
   143  // This method is blocking until the chunk is retrieved
   144  // so additional timeout may be needed to wrap this call if
   145  // ChunkStores are remote and can have long latency
   146  func (ls *LocalStore) Get(ctx context.Context, addr Address) (chunk Chunk, err error) {
   147  	ls.mu.Lock()
   148  	defer ls.mu.Unlock()
   149  
   150  	return ls.get(ctx, addr)
   151  }
   152  
   153  func (ls *LocalStore) get(ctx context.Context, addr Address) (chunk Chunk, err error) {
   154  	chunk, err = ls.memStore.Get(ctx, addr)
   155  
   156  	if err != nil && err != ErrChunkNotFound {
   157  		metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1)
   158  		return nil, err
   159  	}
   160  
   161  	if err == nil {
   162  		metrics.GetOrRegisterCounter("localstore.get.cachehit", nil).Inc(1)
   163  		go ls.DbStore.MarkAccessed(addr)
   164  		return chunk, nil
   165  	}
   166  
   167  	metrics.GetOrRegisterCounter("localstore.get.cachemiss", nil).Inc(1)
   168  	chunk, err = ls.DbStore.Get(ctx, addr)
   169  	if err != nil {
   170  		metrics.GetOrRegisterCounter("localstore.get.error", nil).Inc(1)
   171  		return nil, err
   172  	}
   173  
   174  	ls.memStore.Put(ctx, chunk)
   175  	return chunk, nil
   176  }
   177  
   178  func (ls *LocalStore) FetchFunc(ctx context.Context, addr Address) func(context.Context) error {
   179  	ls.mu.Lock()
   180  	defer ls.mu.Unlock()
   181  
   182  	_, err := ls.get(ctx, addr)
   183  	if err == nil {
   184  		return nil
   185  	}
   186  	return func(context.Context) error {
   187  		return err
   188  	}
   189  }
   190  
   191  func (ls *LocalStore) BinIndex(po uint8) uint64 {
   192  	return ls.DbStore.BinIndex(po)
   193  }
   194  
   195  func (ls *LocalStore) Iterator(from uint64, to uint64, po uint8, f func(Address, uint64) bool) error {
   196  	return ls.DbStore.SyncIterator(from, to, po, f)
   197  }
   198  
   199  // Close the local store
   200  func (ls *LocalStore) Close() {
   201  	ls.DbStore.Close()
   202  }
   203  
   204  // Migrate checks the datastore schema vs the runtime schema and runs
   205  // migrations if they don't match
   206  func (ls *LocalStore) Migrate() error {
   207  	actualDbSchema, err := ls.DbStore.GetSchema()
   208  	if err != nil {
   209  		log.Error(err.Error())
   210  		return err
   211  	}
   212  
   213  	if actualDbSchema == CurrentDbSchema {
   214  		return nil
   215  	}
   216  
   217  	log.Debug("running migrations for", "schema", actualDbSchema, "runtime-schema", CurrentDbSchema)
   218  
   219  	if actualDbSchema == DbSchemaNone {
   220  		ls.migrateFromNoneToPurity()
   221  		actualDbSchema = DbSchemaPurity
   222  	}
   223  
   224  	if err := ls.DbStore.PutSchema(actualDbSchema); err != nil {
   225  		return err
   226  	}
   227  
   228  	if actualDbSchema == DbSchemaPurity {
   229  		if err := ls.migrateFromPurityToHalloween(); err != nil {
   230  			return err
   231  		}
   232  		actualDbSchema = DbSchemaHalloween
   233  	}
   234  
   235  	if err := ls.DbStore.PutSchema(actualDbSchema); err != nil {
   236  		return err
   237  	}
   238  	return nil
   239  }
   240  
   241  func (ls *LocalStore) migrateFromNoneToPurity() {
   242  	// delete chunks that are not valid, i.e. chunks that do not pass
   243  	// any of the ls.Validators
   244  	ls.DbStore.Cleanup(func(c *chunk) bool {
   245  		return !ls.isValid(c)
   246  	})
   247  }
   248  
   249  func (ls *LocalStore) migrateFromPurityToHalloween() error {
   250  	return ls.DbStore.CleanGCIndex()
   251  }