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

     1  // Copyleft 2018 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 localstore
    18  
    19  import (
    20  	"github.com/susy-go/susy-graviton/log"
    21  	"github.com/susy-go/susy-graviton/swarm/shed"
    22  	"github.com/susy-go/susy-graviton/swarm/storage"
    23  	"github.com/syndtr/goleveldb/leveldb"
    24  )
    25  
    26  // ModeGet enumerates different Getter modes.
    27  type ModeGet int
    28  
    29  // Getter modes.
    30  const (
    31  	// ModeGetRequest: when accessed for retrieval
    32  	ModeGetRequest ModeGet = iota
    33  	// ModeGetSync: when accessed for syncing or proof of custody request
    34  	ModeGetSync
    35  )
    36  
    37  // Getter provides Get method to retrieve Chunks
    38  // from database.
    39  type Getter struct {
    40  	db   *DB
    41  	mode ModeGet
    42  }
    43  
    44  // NewGetter returns a new Getter on database
    45  // with a specific Mode.
    46  func (db *DB) NewGetter(mode ModeGet) *Getter {
    47  	return &Getter{
    48  		mode: mode,
    49  		db:   db,
    50  	}
    51  }
    52  
    53  // Get returns a chunk from the database. If the chunk is
    54  // not found storage.ErrChunkNotFound will be returned.
    55  // All required indexes will be updated required by the
    56  // Getter Mode.
    57  func (g *Getter) Get(addr storage.Address) (chunk storage.Chunk, err error) {
    58  	out, err := g.db.get(g.mode, addr)
    59  	if err != nil {
    60  		if err == leveldb.ErrNotFound {
    61  			return nil, storage.ErrChunkNotFound
    62  		}
    63  		return nil, err
    64  	}
    65  	return storage.NewChunk(out.Address, out.Data), nil
    66  }
    67  
    68  // get returns Item from the retrieval index
    69  // and updates other indexes.
    70  func (db *DB) get(mode ModeGet, addr storage.Address) (out shed.Item, err error) {
    71  	item := addressToItem(addr)
    72  
    73  	out, err = db.retrievalDataIndex.Get(item)
    74  	if err != nil {
    75  		return out, err
    76  	}
    77  	switch mode {
    78  	// update the access timestamp and gc index
    79  	case ModeGetRequest:
    80  		if db.updateGCSem != nil {
    81  			// wait before creating new goroutines
    82  			// if updateGCSem buffer id full
    83  			db.updateGCSem <- struct{}{}
    84  		}
    85  		db.updateGCWG.Add(1)
    86  		go func() {
    87  			defer db.updateGCWG.Done()
    88  			if db.updateGCSem != nil {
    89  				// free a spot in updateGCSem buffer
    90  				// for a new goroutine
    91  				defer func() { <-db.updateGCSem }()
    92  			}
    93  			err := db.updateGC(out)
    94  			if err != nil {
    95  				log.Error("localstore update gc", "err", err)
    96  			}
    97  			// if gc update hook is defined, call it
    98  			if testHookUpdateGC != nil {
    99  				testHookUpdateGC()
   100  			}
   101  		}()
   102  
   103  	// no updates to indexes
   104  	case ModeGetSync:
   105  	default:
   106  		return out, ErrInvalidMode
   107  	}
   108  	return out, nil
   109  }
   110  
   111  // updateGC updates garbage collection index for
   112  // a single item. Provided item is expected to have
   113  // only Address and Data fields with non zero values,
   114  // which is ensured by the get function.
   115  func (db *DB) updateGC(item shed.Item) (err error) {
   116  	unlock, err := db.lockAddr(item.Address)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	defer unlock()
   121  
   122  	batch := new(leveldb.Batch)
   123  
   124  	// update accessTimeStamp in retrieve, gc
   125  
   126  	i, err := db.retrievalAccessIndex.Get(item)
   127  	switch err {
   128  	case nil:
   129  		item.AccessTimestamp = i.AccessTimestamp
   130  	case leveldb.ErrNotFound:
   131  		// no chunk accesses
   132  	default:
   133  		return err
   134  	}
   135  	if item.AccessTimestamp == 0 {
   136  		// chunk is not yet synced
   137  		// do not add it to the gc index
   138  		return nil
   139  	}
   140  	// delete current entry from the gc index
   141  	db.gcIndex.DeleteInBatch(batch, item)
   142  	// update access timestamp
   143  	item.AccessTimestamp = now()
   144  	// update retrieve access index
   145  	db.retrievalAccessIndex.PutInBatch(batch, item)
   146  	// add new entry to gc index
   147  	db.gcIndex.PutInBatch(batch, item)
   148  
   149  	return db.shed.WriteBatch(batch)
   150  }
   151  
   152  // testHookUpdateGC is a hook that can provide
   153  // information when a garbage collection index is updated.
   154  var testHookUpdateGC func()