github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/localstore/mode_set.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  	"github.com/ethereum/go-ethereum/swarm/storage"
    21  	"github.com/syndtr/goleveldb/leveldb"
    22  )
    23  
    24  // ModeSet enumerates different Setter modes.
    25  type ModeSet int
    26  
    27  // Setter modes.
    28  const (
    29  	// ModeSetAccess: when an update request is received for a chunk or chunk is retrieved for delivery
    30  	ModeSetAccess ModeSet = iota
    31  	// ModeSetSync: when push sync receipt is received
    32  	ModeSetSync
    33  	// modeSetRemove: when GC-d
    34  	// unexported as no external packages should remove chunks from database
    35  	modeSetRemove
    36  )
    37  
    38  // Setter sets the state of a particular
    39  // Chunk in database by changing indexes.
    40  type Setter struct {
    41  	db   *DB
    42  	mode ModeSet
    43  }
    44  
    45  // NewSetter returns a new Setter on database
    46  // with a specific Mode.
    47  func (db *DB) NewSetter(mode ModeSet) *Setter {
    48  	return &Setter{
    49  		mode: mode,
    50  		db:   db,
    51  	}
    52  }
    53  
    54  // Set updates database indexes for a specific
    55  // chunk represented by the address.
    56  func (s *Setter) Set(addr storage.Address) (err error) {
    57  	return s.db.set(s.mode, addr)
    58  }
    59  
    60  // set updates database indexes for a specific
    61  // chunk represented by the address.
    62  // It acquires lockAddr to protect two calls
    63  // of this function for the same address in parallel.
    64  func (db *DB) set(mode ModeSet, addr storage.Address) (err error) {
    65  	// protect parallel updates
    66  	unlock, err := db.lockAddr(addr)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	defer unlock()
    71  
    72  	batch := new(leveldb.Batch)
    73  
    74  	// variables that provide information for operations
    75  	// to be done after write batch function successfully executes
    76  	var gcSizeChange int64   // number to add or subtract from gcSize
    77  	var triggerPullFeed bool // signal pull feed subscriptions to iterate
    78  
    79  	item := addressToItem(addr)
    80  
    81  	switch mode {
    82  	case ModeSetAccess:
    83  		// add to pull, insert to gc
    84  
    85  		// need to get access timestamp here as it is not
    86  		// provided by the access function, and it is not
    87  		// a property of a chunk provided to Accessor.Put.
    88  
    89  		i, err := db.retrievalDataIndex.Get(item)
    90  		switch err {
    91  		case nil:
    92  			item.StoreTimestamp = i.StoreTimestamp
    93  		case leveldb.ErrNotFound:
    94  			db.pushIndex.DeleteInBatch(batch, item)
    95  			item.StoreTimestamp = now()
    96  		default:
    97  			return err
    98  		}
    99  
   100  		i, err = db.retrievalAccessIndex.Get(item)
   101  		switch err {
   102  		case nil:
   103  			item.AccessTimestamp = i.AccessTimestamp
   104  			db.gcIndex.DeleteInBatch(batch, item)
   105  			gcSizeChange--
   106  		case leveldb.ErrNotFound:
   107  			// the chunk is not accessed before
   108  		default:
   109  			return err
   110  		}
   111  		item.AccessTimestamp = now()
   112  		db.retrievalAccessIndex.PutInBatch(batch, item)
   113  		db.pullIndex.PutInBatch(batch, item)
   114  		triggerPullFeed = true
   115  		db.gcIndex.PutInBatch(batch, item)
   116  		db.gcUncountedHashesIndex.PutInBatch(batch, item)
   117  		gcSizeChange++
   118  
   119  	case ModeSetSync:
   120  		// delete from push, insert to gc
   121  
   122  		// need to get access timestamp here as it is not
   123  		// provided by the access function, and it is not
   124  		// a property of a chunk provided to Accessor.Put.
   125  		i, err := db.retrievalDataIndex.Get(item)
   126  		if err != nil {
   127  			if err == leveldb.ErrNotFound {
   128  				// chunk is not found,
   129  				// no need to update gc index
   130  				// just delete from the push index
   131  				// if it is there
   132  				db.pushIndex.DeleteInBatch(batch, item)
   133  				return nil
   134  			}
   135  			return err
   136  		}
   137  		item.StoreTimestamp = i.StoreTimestamp
   138  
   139  		i, err = db.retrievalAccessIndex.Get(item)
   140  		switch err {
   141  		case nil:
   142  			item.AccessTimestamp = i.AccessTimestamp
   143  			db.gcIndex.DeleteInBatch(batch, item)
   144  			gcSizeChange--
   145  		case leveldb.ErrNotFound:
   146  			// the chunk is not accessed before
   147  		default:
   148  			return err
   149  		}
   150  		item.AccessTimestamp = now()
   151  		db.retrievalAccessIndex.PutInBatch(batch, item)
   152  		db.pushIndex.DeleteInBatch(batch, item)
   153  		db.gcIndex.PutInBatch(batch, item)
   154  		db.gcUncountedHashesIndex.PutInBatch(batch, item)
   155  		gcSizeChange++
   156  
   157  	case modeSetRemove:
   158  		// delete from retrieve, pull, gc
   159  
   160  		// need to get access timestamp here as it is not
   161  		// provided by the access function, and it is not
   162  		// a property of a chunk provided to Accessor.Put.
   163  
   164  		i, err := db.retrievalAccessIndex.Get(item)
   165  		switch err {
   166  		case nil:
   167  			item.AccessTimestamp = i.AccessTimestamp
   168  		case leveldb.ErrNotFound:
   169  		default:
   170  			return err
   171  		}
   172  		i, err = db.retrievalDataIndex.Get(item)
   173  		if err != nil {
   174  			return err
   175  		}
   176  		item.StoreTimestamp = i.StoreTimestamp
   177  
   178  		db.retrievalDataIndex.DeleteInBatch(batch, item)
   179  		db.retrievalAccessIndex.DeleteInBatch(batch, item)
   180  		db.pullIndex.DeleteInBatch(batch, item)
   181  		db.gcIndex.DeleteInBatch(batch, item)
   182  		db.gcUncountedHashesIndex.DeleteInBatch(batch, item)
   183  		// a check is needed for decrementing gcSize
   184  		// as delete is not reporting if the key/value pair
   185  		// is deleted or not
   186  		if _, err := db.gcIndex.Get(item); err == nil {
   187  			gcSizeChange = -1
   188  		}
   189  
   190  	default:
   191  		return ErrInvalidMode
   192  	}
   193  
   194  	err = db.shed.WriteBatch(batch)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	if gcSizeChange != 0 {
   199  		db.incGCSize(gcSizeChange)
   200  	}
   201  	if triggerPullFeed {
   202  		db.triggerPullSubscriptions(db.po(item.Address))
   203  	}
   204  	return nil
   205  }