github.com/gochain-io/gochain@v2.2.26+incompatible/swarm/storage/dpa.go (about)

     1  // Copyright 2016 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 storage
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/gochain-io/gochain/log"
    27  )
    28  
    29  /*
    30  DPA provides the client API entrypoints Store and Retrieve to store and retrieve
    31  It can store anything that has a byte slice representation, so files or serialised objects etc.
    32  
    33  Storage: DPA calls the Chunker to segment the input datastream of any size to a merkle hashed tree of chunks. The key of the root block is returned to the client.
    34  
    35  Retrieval: given the key of the root block, the DPA retrieves the block chunks and reconstructs the original data and passes it back as a lazy reader. A lazy reader is a reader with on-demand delayed processing, i.e. the chunks needed to reconstruct a large file are only fetched and processed if that particular part of the document is actually read.
    36  
    37  As the chunker produces chunks, DPA dispatches them to its own chunk store
    38  implementation for storage or retrieval.
    39  */
    40  
    41  const (
    42  	storeChanCapacity           = 100
    43  	retrieveChanCapacity        = 100
    44  	singletonSwarmDbCapacity    = 50000
    45  	singletonSwarmCacheCapacity = 500
    46  	maxStoreProcesses           = 8
    47  	maxRetrieveProcesses        = 8
    48  )
    49  
    50  var (
    51  	notFound = errors.New("not found")
    52  )
    53  
    54  type DPA struct {
    55  	ChunkStore
    56  	storeC    chan *Chunk
    57  	retrieveC chan *Chunk
    58  	Chunker   Chunker
    59  
    60  	lock    sync.Mutex
    61  	running bool
    62  	quitC   chan bool
    63  }
    64  
    65  // for testing locally
    66  func NewLocalDPA(datadir string) (*DPA, error) {
    67  
    68  	hash := MakeHashFunc("SHA256")
    69  
    70  	dbStore, err := NewDbStore(datadir, hash, singletonSwarmDbCapacity, 0)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return NewDPA(&LocalStore{
    76  		NewMemStore(dbStore, singletonSwarmCacheCapacity),
    77  		dbStore,
    78  	}, NewChunkerParams()), nil
    79  }
    80  
    81  func NewDPA(store ChunkStore, params *ChunkerParams) *DPA {
    82  	chunker := NewTreeChunker(params)
    83  	return &DPA{
    84  		Chunker:    chunker,
    85  		ChunkStore: store,
    86  	}
    87  }
    88  
    89  // Public API. Main entry point for document retrieval directly. Used by the
    90  // FS-aware API and httpaccess
    91  // Chunk retrieval blocks on netStore requests with a timeout so reader will
    92  // report error if retrieval of chunks within requested range time out.
    93  func (d *DPA) Retrieve(key Key) LazySectionReader {
    94  	return d.Chunker.Join(key, d.retrieveC)
    95  }
    96  
    97  // Public API. Main entry point for document storage directly. Used by the
    98  // FS-aware API and httpaccess
    99  func (d *DPA) Store(data io.Reader, size int64, swg *sync.WaitGroup) (key Key, err error) {
   100  	return d.Chunker.Split(data, size, d.storeC, swg)
   101  }
   102  
   103  func (d *DPA) Start() {
   104  	d.lock.Lock()
   105  	defer d.lock.Unlock()
   106  	if d.running {
   107  		return
   108  	}
   109  	d.running = true
   110  	d.retrieveC = make(chan *Chunk, retrieveChanCapacity)
   111  	d.storeC = make(chan *Chunk, storeChanCapacity)
   112  	d.quitC = make(chan bool)
   113  	d.storeLoop()
   114  	d.retrieveLoop()
   115  }
   116  
   117  func (d *DPA) Stop() {
   118  	d.lock.Lock()
   119  	defer d.lock.Unlock()
   120  	if !d.running {
   121  		return
   122  	}
   123  	d.running = false
   124  	close(d.quitC)
   125  }
   126  
   127  // retrieveLoop dispatches the parallel chunk retrieval requests received on the
   128  // retrieve channel to its ChunkStore  (NetStore or LocalStore)
   129  func (d *DPA) retrieveLoop() {
   130  	for i := 0; i < maxRetrieveProcesses; i++ {
   131  		go d.retrieveWorker()
   132  	}
   133  	log.Trace(fmt.Sprintf("dpa: retrieve loop spawning %v workers", maxRetrieveProcesses))
   134  }
   135  
   136  func (d *DPA) retrieveWorker() {
   137  	for chunk := range d.retrieveC {
   138  		log.Trace(fmt.Sprintf("dpa: retrieve loop : chunk %v", chunk.Key.Log()))
   139  		storedChunk, err := d.Get(chunk.Key)
   140  		if err == notFound {
   141  			log.Trace(fmt.Sprintf("chunk %v not found", chunk.Key.Log()))
   142  		} else if err != nil {
   143  			log.Trace(fmt.Sprintf("error retrieving chunk %v: %v", chunk.Key.Log(), err))
   144  		} else {
   145  			chunk.SData = storedChunk.SData
   146  			chunk.Size = storedChunk.Size
   147  		}
   148  		close(chunk.C)
   149  
   150  		select {
   151  		case <-d.quitC:
   152  			return
   153  		default:
   154  		}
   155  	}
   156  }
   157  
   158  // storeLoop dispatches the parallel chunk store request processors
   159  // received on the store channel to its ChunkStore (NetStore or LocalStore)
   160  func (d *DPA) storeLoop() {
   161  	for i := 0; i < maxStoreProcesses; i++ {
   162  		go d.storeWorker()
   163  	}
   164  	log.Trace(fmt.Sprintf("dpa: store spawning %v workers", maxStoreProcesses))
   165  }
   166  
   167  func (d *DPA) storeWorker() {
   168  	for chunk := range d.storeC {
   169  		d.Put(chunk)
   170  		if chunk.wg != nil {
   171  			log.Trace(fmt.Sprintf("dpa: store processor %v", chunk.Key.Log()))
   172  			chunk.wg.Done()
   173  		}
   174  		select {
   175  		case <-d.quitC:
   176  			return
   177  		default:
   178  		}
   179  	}
   180  }
   181  
   182  // DpaChunkStore implements the ChunkStore interface,
   183  // this chunk access layer assumed 2 chunk stores
   184  // local storage eg. LocalStore and network storage eg., NetStore
   185  // access by calling network is blocking with a timeout
   186  
   187  type dpaChunkStore struct {
   188  	n          int
   189  	localStore ChunkStore
   190  	netStore   ChunkStore
   191  }
   192  
   193  func NewDpaChunkStore(localStore, netStore ChunkStore) *dpaChunkStore {
   194  	return &dpaChunkStore{0, localStore, netStore}
   195  }
   196  
   197  // Get is the entrypoint for local retrieve requests
   198  // waits for response or times out
   199  func (d *dpaChunkStore) Get(key Key) (chunk *Chunk, err error) {
   200  	chunk, err = d.netStore.Get(key)
   201  	// timeout := time.Now().Add(searchTimeout)
   202  	if chunk.SData != nil {
   203  		log.Trace(fmt.Sprintf("DPA.Get: %v found locally, %d bytes", key.Log(), len(chunk.SData)))
   204  		return
   205  	}
   206  	// TODO: use d.timer time.Timer and reset with defer disableTimer
   207  	timer := time.After(searchTimeout)
   208  	select {
   209  	case <-timer:
   210  		log.Trace(fmt.Sprintf("DPA.Get: %v request time out ", key.Log()))
   211  		err = notFound
   212  	case <-chunk.Req.C:
   213  		log.Trace(fmt.Sprintf("DPA.Get: %v retrieved, %d bytes (%p)", key.Log(), len(chunk.SData), chunk))
   214  	}
   215  	return
   216  }
   217  
   218  // Put is the entrypoint for local store requests coming from storeLoop
   219  func (d *dpaChunkStore) Put(entry *Chunk) {
   220  	chunk, err := d.localStore.Get(entry.Key)
   221  	if err != nil {
   222  		log.Trace(fmt.Sprintf("DPA.Put: %v new chunk. call netStore.Put", entry.Key.Log()))
   223  		chunk = entry
   224  	} else if chunk.SData == nil {
   225  		log.Trace(fmt.Sprintf("DPA.Put: %v request entry found", entry.Key.Log()))
   226  		chunk.SData = entry.SData
   227  		chunk.Size = entry.Size
   228  	} else {
   229  		log.Trace(fmt.Sprintf("DPA.Put: %v chunk already known", entry.Key.Log()))
   230  		return
   231  	}
   232  	// from this point on the storage logic is the same with network storage requests
   233  	log.Trace(fmt.Sprintf("DPA.Put %v: %v", d.n, chunk.Key.Log()))
   234  	d.n++
   235  	d.netStore.Put(chunk)
   236  }
   237  
   238  // Close chunk store
   239  func (d *dpaChunkStore) Close() {}