github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/swarm/storage/localstore/localstore.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  package localstore
    18  
    19  import (
    20  	"encoding/binary"
    21  	"errors"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/log"
    26  	"github.com/ethereum/go-ethereum/swarm/chunk"
    27  	"github.com/ethereum/go-ethereum/swarm/shed"
    28  	"github.com/ethereum/go-ethereum/swarm/storage/mock"
    29  )
    30  
    31  var (
    32  	// ErrInvalidMode is retuned when an unknown Mode
    33  	// is provided to the function.
    34  	ErrInvalidMode = errors.New("invalid mode")
    35  	// ErrAddressLockTimeout is returned when the same chunk
    36  	// is updated in parallel and one of the updates
    37  	// takes longer then the configured timeout duration.
    38  	ErrAddressLockTimeout = errors.New("address lock timeout")
    39  )
    40  
    41  var (
    42  	// Default value for Capacity DB option.
    43  	defaultCapacity uint64 = 5000000
    44  	// Limit the number of goroutines created by Getters
    45  	// that call updateGC function. Value 0 sets no limit.
    46  	maxParallelUpdateGC = 1000
    47  )
    48  
    49  // DB is the local store implementation and holds
    50  // database related objects.
    51  type DB struct {
    52  	shed *shed.DB
    53  
    54  	// schema name of loaded data
    55  	schemaName shed.StringField
    56  
    57  	// retrieval indexes
    58  	retrievalDataIndex   shed.Index
    59  	retrievalAccessIndex shed.Index
    60  	// push syncing index
    61  	pushIndex shed.Index
    62  	// push syncing subscriptions triggers
    63  	pushTriggers   []chan struct{}
    64  	pushTriggersMu sync.RWMutex
    65  
    66  	// pull syncing index
    67  	pullIndex shed.Index
    68  	// pull syncing subscriptions triggers per bin
    69  	pullTriggers   map[uint8][]chan struct{}
    70  	pullTriggersMu sync.RWMutex
    71  
    72  	// garbage collection index
    73  	gcIndex shed.Index
    74  
    75  	// field that stores number of intems in gc index
    76  	gcSize shed.Uint64Field
    77  
    78  	// garbage collection is triggered when gcSize exceeds
    79  	// the capacity value
    80  	capacity uint64
    81  
    82  	// triggers garbage collection event loop
    83  	collectGarbageTrigger chan struct{}
    84  
    85  	// a buffered channel acting as a semaphore
    86  	// to limit the maximal number of goroutines
    87  	// created by Getters to call updateGC function
    88  	updateGCSem chan struct{}
    89  	// a wait group to ensure all updateGC goroutines
    90  	// are done before closing the database
    91  	updateGCWG sync.WaitGroup
    92  
    93  	baseKey []byte
    94  
    95  	batchMu sync.Mutex
    96  
    97  	// this channel is closed when close function is called
    98  	// to terminate other goroutines
    99  	close chan struct{}
   100  
   101  	// protect Close method from exiting before
   102  	// garbage collection and gc size write workers
   103  	// are done
   104  	collectGarbageWorkerDone chan struct{}
   105  }
   106  
   107  // Options struct holds optional parameters for configuring DB.
   108  type Options struct {
   109  	// MockStore is a mock node store that is used to store
   110  	// chunk data in a central store. It can be used to reduce
   111  	// total storage space requirements in testing large number
   112  	// of swarm nodes with chunk data deduplication provided by
   113  	// the mock global store.
   114  	MockStore *mock.NodeStore
   115  	// Capacity is a limit that triggers garbage collection when
   116  	// number of items in gcIndex equals or exceeds it.
   117  	Capacity uint64
   118  	// MetricsPrefix defines a prefix for metrics names.
   119  	MetricsPrefix string
   120  }
   121  
   122  // New returns a new DB.  All fields and indexes are initialized
   123  // and possible conflicts with schema from existing database is checked.
   124  // One goroutine for writing batches is created.
   125  func New(path string, baseKey []byte, o *Options) (db *DB, err error) {
   126  	if o == nil {
   127  		o = new(Options)
   128  	}
   129  	db = &DB{
   130  		capacity: o.Capacity,
   131  		baseKey:  baseKey,
   132  		// channel collectGarbageTrigger
   133  		// needs to be buffered with the size of 1
   134  		// to signal another event if it
   135  		// is triggered during already running function
   136  		collectGarbageTrigger:    make(chan struct{}, 1),
   137  		close:                    make(chan struct{}),
   138  		collectGarbageWorkerDone: make(chan struct{}),
   139  	}
   140  	if db.capacity <= 0 {
   141  		db.capacity = defaultCapacity
   142  	}
   143  	if maxParallelUpdateGC > 0 {
   144  		db.updateGCSem = make(chan struct{}, maxParallelUpdateGC)
   145  	}
   146  
   147  	db.shed, err = shed.NewDB(path, o.MetricsPrefix)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	// Identify current storage schema by arbitrary name.
   152  	db.schemaName, err = db.shed.NewStringField("schema-name")
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	// Persist gc size.
   157  	db.gcSize, err = db.shed.NewUint64Field("gc-size")
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	// Functions for retrieval data index.
   162  	var (
   163  		encodeValueFunc func(fields shed.Item) (value []byte, err error)
   164  		decodeValueFunc func(keyItem shed.Item, value []byte) (e shed.Item, err error)
   165  	)
   166  	if o.MockStore != nil {
   167  		encodeValueFunc = func(fields shed.Item) (value []byte, err error) {
   168  			b := make([]byte, 8)
   169  			binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp))
   170  			err = o.MockStore.Put(fields.Address, fields.Data)
   171  			if err != nil {
   172  				return nil, err
   173  			}
   174  			return b, nil
   175  		}
   176  		decodeValueFunc = func(keyItem shed.Item, value []byte) (e shed.Item, err error) {
   177  			e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8]))
   178  			e.Data, err = o.MockStore.Get(keyItem.Address)
   179  			return e, err
   180  		}
   181  	} else {
   182  		encodeValueFunc = func(fields shed.Item) (value []byte, err error) {
   183  			b := make([]byte, 8)
   184  			binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp))
   185  			value = append(b, fields.Data...)
   186  			return value, nil
   187  		}
   188  		decodeValueFunc = func(keyItem shed.Item, value []byte) (e shed.Item, err error) {
   189  			e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8]))
   190  			e.Data = value[8:]
   191  			return e, nil
   192  		}
   193  	}
   194  	// Index storing actual chunk address, data and store timestamp.
   195  	db.retrievalDataIndex, err = db.shed.NewIndex("Address->StoreTimestamp|Data", shed.IndexFuncs{
   196  		EncodeKey: func(fields shed.Item) (key []byte, err error) {
   197  			return fields.Address, nil
   198  		},
   199  		DecodeKey: func(key []byte) (e shed.Item, err error) {
   200  			e.Address = key
   201  			return e, nil
   202  		},
   203  		EncodeValue: encodeValueFunc,
   204  		DecodeValue: decodeValueFunc,
   205  	})
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	// Index storing access timestamp for a particular address.
   210  	// It is needed in order to update gc index keys for iteration order.
   211  	db.retrievalAccessIndex, err = db.shed.NewIndex("Address->AccessTimestamp", shed.IndexFuncs{
   212  		EncodeKey: func(fields shed.Item) (key []byte, err error) {
   213  			return fields.Address, nil
   214  		},
   215  		DecodeKey: func(key []byte) (e shed.Item, err error) {
   216  			e.Address = key
   217  			return e, nil
   218  		},
   219  		EncodeValue: func(fields shed.Item) (value []byte, err error) {
   220  			b := make([]byte, 8)
   221  			binary.BigEndian.PutUint64(b, uint64(fields.AccessTimestamp))
   222  			return b, nil
   223  		},
   224  		DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) {
   225  			e.AccessTimestamp = int64(binary.BigEndian.Uint64(value))
   226  			return e, nil
   227  		},
   228  	})
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	// pull index allows history and live syncing per po bin
   233  	db.pullIndex, err = db.shed.NewIndex("PO|StoredTimestamp|Hash->nil", shed.IndexFuncs{
   234  		EncodeKey: func(fields shed.Item) (key []byte, err error) {
   235  			key = make([]byte, 41)
   236  			key[0] = db.po(fields.Address)
   237  			binary.BigEndian.PutUint64(key[1:9], uint64(fields.StoreTimestamp))
   238  			copy(key[9:], fields.Address[:])
   239  			return key, nil
   240  		},
   241  		DecodeKey: func(key []byte) (e shed.Item, err error) {
   242  			e.Address = key[9:]
   243  			e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[1:9]))
   244  			return e, nil
   245  		},
   246  		EncodeValue: func(fields shed.Item) (value []byte, err error) {
   247  			return nil, nil
   248  		},
   249  		DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) {
   250  			return e, nil
   251  		},
   252  	})
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	// create a pull syncing triggers used by SubscribePull function
   257  	db.pullTriggers = make(map[uint8][]chan struct{})
   258  	// push index contains as yet unsynced chunks
   259  	db.pushIndex, err = db.shed.NewIndex("StoredTimestamp|Hash->nil", shed.IndexFuncs{
   260  		EncodeKey: func(fields shed.Item) (key []byte, err error) {
   261  			key = make([]byte, 40)
   262  			binary.BigEndian.PutUint64(key[:8], uint64(fields.StoreTimestamp))
   263  			copy(key[8:], fields.Address[:])
   264  			return key, nil
   265  		},
   266  		DecodeKey: func(key []byte) (e shed.Item, err error) {
   267  			e.Address = key[8:]
   268  			e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[:8]))
   269  			return e, nil
   270  		},
   271  		EncodeValue: func(fields shed.Item) (value []byte, err error) {
   272  			return nil, nil
   273  		},
   274  		DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) {
   275  			return e, nil
   276  		},
   277  	})
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	// create a push syncing triggers used by SubscribePush function
   282  	db.pushTriggers = make([]chan struct{}, 0)
   283  	// gc index for removable chunk ordered by ascending last access time
   284  	db.gcIndex, err = db.shed.NewIndex("AccessTimestamp|StoredTimestamp|Hash->nil", shed.IndexFuncs{
   285  		EncodeKey: func(fields shed.Item) (key []byte, err error) {
   286  			b := make([]byte, 16, 16+len(fields.Address))
   287  			binary.BigEndian.PutUint64(b[:8], uint64(fields.AccessTimestamp))
   288  			binary.BigEndian.PutUint64(b[8:16], uint64(fields.StoreTimestamp))
   289  			key = append(b, fields.Address...)
   290  			return key, nil
   291  		},
   292  		DecodeKey: func(key []byte) (e shed.Item, err error) {
   293  			e.AccessTimestamp = int64(binary.BigEndian.Uint64(key[:8]))
   294  			e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[8:16]))
   295  			e.Address = key[16:]
   296  			return e, nil
   297  		},
   298  		EncodeValue: func(fields shed.Item) (value []byte, err error) {
   299  			return nil, nil
   300  		},
   301  		DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) {
   302  			return e, nil
   303  		},
   304  	})
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	// start garbage collection worker
   309  	go db.collectGarbageWorker()
   310  	return db, nil
   311  }
   312  
   313  // Close closes the underlying database.
   314  func (db *DB) Close() (err error) {
   315  	close(db.close)
   316  	db.updateGCWG.Wait()
   317  
   318  	// wait for gc worker to
   319  	// return before closing the shed
   320  	select {
   321  	case <-db.collectGarbageWorkerDone:
   322  	case <-time.After(5 * time.Second):
   323  		log.Error("localstore: collect garbage worker did not return after db close")
   324  	}
   325  	return db.shed.Close()
   326  }
   327  
   328  // po computes the proximity order between the address
   329  // and database base key.
   330  func (db *DB) po(addr chunk.Address) (bin uint8) {
   331  	return uint8(chunk.Proximity(db.baseKey, addr))
   332  }
   333  
   334  // chunkToItem creates new Item with data provided by the Chunk.
   335  func chunkToItem(ch chunk.Chunk) shed.Item {
   336  	return shed.Item{
   337  		Address: ch.Address(),
   338  		Data:    ch.Data(),
   339  	}
   340  }
   341  
   342  // addressToItem creates new Item with a provided address.
   343  func addressToItem(addr chunk.Address) shed.Item {
   344  	return shed.Item{
   345  		Address: addr,
   346  	}
   347  }
   348  
   349  // now is a helper function that returns a current unix timestamp
   350  // in UTC timezone.
   351  // It is set in the init function for usage in production, and
   352  // optionally overridden in tests for data validation.
   353  var now func() int64
   354  
   355  func init() {
   356  	// set the now function
   357  	now = func() (t int64) {
   358  		return time.Now().UTC().UnixNano()
   359  	}
   360  }