github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/localstore/mode_put.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/shed"
    21  	"github.com/ethereum/go-ethereum/swarm/storage"
    22  	"github.com/syndtr/goleveldb/leveldb"
    23  )
    24  
    25  // ModePut enumerates different Putter modes.
    26  type ModePut int
    27  
    28  // Putter modes.
    29  const (
    30  	// ModePutRequest: when a chunk is received as a result of retrieve request and delivery
    31  	ModePutRequest ModePut = iota
    32  	// ModePutSync: when a chunk is received via syncing
    33  	ModePutSync
    34  	// ModePutUpload: when a chunk is created by local upload
    35  	ModePutUpload
    36  )
    37  
    38  // Putter provides Put method to store Chunks
    39  // to database.
    40  type Putter struct {
    41  	db   *DB
    42  	mode ModePut
    43  }
    44  
    45  // NewPutter returns a new Putter on database
    46  // with a specific Mode.
    47  func (db *DB) NewPutter(mode ModePut) *Putter {
    48  	return &Putter{
    49  		mode: mode,
    50  		db:   db,
    51  	}
    52  }
    53  
    54  // Put stores the Chunk to database and depending
    55  // on the Putter mode, it updates required indexes.
    56  func (p *Putter) Put(ch storage.Chunk) (err error) {
    57  	return p.db.put(p.mode, chunkToItem(ch))
    58  }
    59  
    60  // put stores Item to database and updates other
    61  // indexes. It acquires lockAddr to protect two calls
    62  // of this function for the same address in parallel.
    63  // Item fields Address and Data must not be
    64  // with their nil values.
    65  func (db *DB) put(mode ModePut, item shed.Item) (err error) {
    66  	// protect parallel updates
    67  	unlock, err := db.lockAddr(item.Address)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	defer unlock()
    72  
    73  	batch := new(leveldb.Batch)
    74  
    75  	// variables that provide information for operations
    76  	// to be done after write batch function successfully executes
    77  	var gcSizeChange int64   // number to add or subtract from gcSize
    78  	var triggerPullFeed bool // signal pull feed subscriptions to iterate
    79  	var triggerPushFeed bool // signal push feed subscriptions to iterate
    80  
    81  	switch mode {
    82  	case ModePutRequest:
    83  		// put to indexes: retrieve, gc; it does not enter the syncpool
    84  
    85  		// check if the chunk already is in the database
    86  		// as gc index is updated
    87  		i, err := db.retrievalAccessIndex.Get(item)
    88  		switch err {
    89  		case nil:
    90  			item.AccessTimestamp = i.AccessTimestamp
    91  		case leveldb.ErrNotFound:
    92  			// no chunk accesses
    93  		default:
    94  			return err
    95  		}
    96  		i, err = db.retrievalDataIndex.Get(item)
    97  		switch err {
    98  		case nil:
    99  			item.StoreTimestamp = i.StoreTimestamp
   100  		case leveldb.ErrNotFound:
   101  			// no chunk accesses
   102  		default:
   103  			return err
   104  		}
   105  		if item.AccessTimestamp != 0 {
   106  			// delete current entry from the gc index
   107  			db.gcIndex.DeleteInBatch(batch, item)
   108  			gcSizeChange--
   109  		}
   110  		if item.StoreTimestamp == 0 {
   111  			item.StoreTimestamp = now()
   112  		}
   113  		// update access timestamp
   114  		item.AccessTimestamp = now()
   115  		// update retrieve access index
   116  		db.retrievalAccessIndex.PutInBatch(batch, item)
   117  		// add new entry to gc index
   118  		db.gcIndex.PutInBatch(batch, item)
   119  		db.gcUncountedHashesIndex.PutInBatch(batch, item)
   120  		gcSizeChange++
   121  
   122  		db.retrievalDataIndex.PutInBatch(batch, item)
   123  
   124  	case ModePutUpload:
   125  		// put to indexes: retrieve, push, pull
   126  
   127  		item.StoreTimestamp = now()
   128  		db.retrievalDataIndex.PutInBatch(batch, item)
   129  		db.pullIndex.PutInBatch(batch, item)
   130  		triggerPullFeed = true
   131  		db.pushIndex.PutInBatch(batch, item)
   132  		triggerPushFeed = true
   133  
   134  	case ModePutSync:
   135  		// put to indexes: retrieve, pull
   136  
   137  		item.StoreTimestamp = now()
   138  		db.retrievalDataIndex.PutInBatch(batch, item)
   139  		db.pullIndex.PutInBatch(batch, item)
   140  		triggerPullFeed = true
   141  
   142  	default:
   143  		return ErrInvalidMode
   144  	}
   145  
   146  	err = db.shed.WriteBatch(batch)
   147  	if err != nil {
   148  		return err
   149  	}
   150  	if gcSizeChange != 0 {
   151  		db.incGCSize(gcSizeChange)
   152  	}
   153  	if triggerPullFeed {
   154  		db.triggerPullSubscriptions(db.po(item.Address))
   155  	}
   156  	if triggerPushFeed {
   157  		db.triggerPushSubscriptions()
   158  	}
   159  	return nil
   160  }