github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/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/chunk"
    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 chunk.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 chunk.Address) (err error) {
    65  	// protect parallel updates
    66  	db.batchMu.Lock()
    67  	defer db.batchMu.Unlock()
    68  
    69  	batch := new(leveldb.Batch)
    70  
    71  	// variables that provide information for operations
    72  	// to be done after write batch function successfully executes
    73  	var gcSizeChange int64   // number to add or subtract from gcSize
    74  	var triggerPullFeed bool // signal pull feed subscriptions to iterate
    75  
    76  	item := addressToItem(addr)
    77  
    78  	switch mode {
    79  	case ModeSetAccess:
    80  		// add to pull, insert to gc
    81  
    82  		// need to get access timestamp here as it is not
    83  		// provided by the access function, and it is not
    84  		// a property of a chunk provided to Accessor.Put.
    85  
    86  		i, err := db.retrievalDataIndex.Get(item)
    87  		switch err {
    88  		case nil:
    89  			item.StoreTimestamp = i.StoreTimestamp
    90  		case leveldb.ErrNotFound:
    91  			db.pushIndex.DeleteInBatch(batch, item)
    92  			item.StoreTimestamp = now()
    93  		default:
    94  			return err
    95  		}
    96  
    97  		i, err = db.retrievalAccessIndex.Get(item)
    98  		switch err {
    99  		case nil:
   100  			item.AccessTimestamp = i.AccessTimestamp
   101  			db.gcIndex.DeleteInBatch(batch, item)
   102  			gcSizeChange--
   103  		case leveldb.ErrNotFound:
   104  			// the chunk is not accessed before
   105  		default:
   106  			return err
   107  		}
   108  		item.AccessTimestamp = now()
   109  		db.retrievalAccessIndex.PutInBatch(batch, item)
   110  		db.pullIndex.PutInBatch(batch, item)
   111  		triggerPullFeed = true
   112  		db.gcIndex.PutInBatch(batch, item)
   113  		gcSizeChange++
   114  
   115  	case ModeSetSync:
   116  		// delete from push, insert to gc
   117  
   118  		// need to get access timestamp here as it is not
   119  		// provided by the access function, and it is not
   120  		// a property of a chunk provided to Accessor.Put.
   121  		i, err := db.retrievalDataIndex.Get(item)
   122  		if err != nil {
   123  			if err == leveldb.ErrNotFound {
   124  				// chunk is not found,
   125  				// no need to update gc index
   126  				// just delete from the push index
   127  				// if it is there
   128  				db.pushIndex.DeleteInBatch(batch, item)
   129  				return nil
   130  			}
   131  			return err
   132  		}
   133  		item.StoreTimestamp = i.StoreTimestamp
   134  
   135  		i, err = db.retrievalAccessIndex.Get(item)
   136  		switch err {
   137  		case nil:
   138  			item.AccessTimestamp = i.AccessTimestamp
   139  			db.gcIndex.DeleteInBatch(batch, item)
   140  			gcSizeChange--
   141  		case leveldb.ErrNotFound:
   142  			// the chunk is not accessed before
   143  		default:
   144  			return err
   145  		}
   146  		item.AccessTimestamp = now()
   147  		db.retrievalAccessIndex.PutInBatch(batch, item)
   148  		db.pushIndex.DeleteInBatch(batch, item)
   149  		db.gcIndex.PutInBatch(batch, item)
   150  		gcSizeChange++
   151  
   152  	case modeSetRemove:
   153  		// delete from retrieve, pull, gc
   154  
   155  		// need to get access timestamp here as it is not
   156  		// provided by the access function, and it is not
   157  		// a property of a chunk provided to Accessor.Put.
   158  
   159  		i, err := db.retrievalAccessIndex.Get(item)
   160  		switch err {
   161  		case nil:
   162  			item.AccessTimestamp = i.AccessTimestamp
   163  		case leveldb.ErrNotFound:
   164  		default:
   165  			return err
   166  		}
   167  		i, err = db.retrievalDataIndex.Get(item)
   168  		if err != nil {
   169  			return err
   170  		}
   171  		item.StoreTimestamp = i.StoreTimestamp
   172  
   173  		db.retrievalDataIndex.DeleteInBatch(batch, item)
   174  		db.retrievalAccessIndex.DeleteInBatch(batch, item)
   175  		db.pullIndex.DeleteInBatch(batch, item)
   176  		db.gcIndex.DeleteInBatch(batch, item)
   177  		// a check is needed for decrementing gcSize
   178  		// as delete is not reporting if the key/value pair
   179  		// is deleted or not
   180  		if _, err := db.gcIndex.Get(item); err == nil {
   181  			gcSizeChange = -1
   182  		}
   183  
   184  	default:
   185  		return ErrInvalidMode
   186  	}
   187  
   188  	err = db.incGCSizeInBatch(batch, gcSizeChange)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	err = db.shed.WriteBatch(batch)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	if triggerPullFeed {
   198  		db.triggerPullSubscriptions(db.po(item.Address))
   199  	}
   200  	return nil
   201  }