github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/blockservice/blockservice.go (about)

     1  // package blockservice implements a BlockService interface that provides
     2  // a single GetBlock/AddBlock interface that seamlessly retrieves data either
     3  // locally or from a remote peer through the exchange.
     4  package blockservice
     5  
     6  import (
     7  	"errors"
     8  
     9  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    10  	blocks "github.com/ipfs/go-ipfs/blocks"
    11  	"github.com/ipfs/go-ipfs/blocks/blockstore"
    12  	key "github.com/ipfs/go-ipfs/blocks/key"
    13  	worker "github.com/ipfs/go-ipfs/blockservice/worker"
    14  	exchange "github.com/ipfs/go-ipfs/exchange"
    15  	u "github.com/ipfs/go-ipfs/util"
    16  )
    17  
    18  var wc = worker.Config{
    19  	// When running on a single core, NumWorkers has a harsh negative effect on
    20  	// throughput. (-80% when < 25)
    21  	// Running a lot more workers appears to have very little effect on both
    22  	// single and multicore configurations.
    23  	NumWorkers: 25,
    24  
    25  	// These have no effect on when running on multiple cores, but harsh
    26  	// negative effect on throughput when running on a single core
    27  	// On multicore configurations these buffers have little effect on
    28  	// throughput.
    29  	// On single core configurations, larger buffers have severe adverse
    30  	// effects on throughput.
    31  	ClientBufferSize: 0,
    32  	WorkerBufferSize: 0,
    33  }
    34  
    35  var log = u.Logger("blockservice")
    36  var ErrNotFound = errors.New("blockservice: key not found")
    37  
    38  // BlockService is a hybrid block datastore. It stores data in a local
    39  // datastore and may retrieve data from a remote Exchange.
    40  // It uses an internal `datastore.Datastore` instance to store values.
    41  type BlockService struct {
    42  	// TODO don't expose underlying impl details
    43  	Blockstore blockstore.Blockstore
    44  	Exchange   exchange.Interface
    45  
    46  	worker *worker.Worker
    47  }
    48  
    49  // NewBlockService creates a BlockService with given datastore instance.
    50  func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService {
    51  	if rem == nil {
    52  		log.Warning("blockservice running in local (offline) mode.")
    53  	}
    54  
    55  	return &BlockService{
    56  		Blockstore: bs,
    57  		Exchange:   rem,
    58  		worker:     worker.NewWorker(rem, wc),
    59  	}
    60  }
    61  
    62  // AddBlock adds a particular block to the service, Putting it into the datastore.
    63  // TODO pass a context into this if the remote.HasBlock is going to remain here.
    64  func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) {
    65  	k := b.Key()
    66  	err := s.Blockstore.Put(b)
    67  	if err != nil {
    68  		return k, err
    69  	}
    70  	if err := s.worker.HasBlock(b); err != nil {
    71  		return "", errors.New("blockservice is closed")
    72  	}
    73  	return k, nil
    74  }
    75  
    76  func (s *BlockService) AddBlocks(bs []*blocks.Block) ([]key.Key, error) {
    77  	err := s.Blockstore.PutMany(bs)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	var ks []key.Key
    83  	for _, b := range bs {
    84  		if err := s.worker.HasBlock(b); err != nil {
    85  			return nil, errors.New("blockservice is closed")
    86  		}
    87  		ks = append(ks, b.Key())
    88  	}
    89  	return ks, nil
    90  }
    91  
    92  // GetBlock retrieves a particular block from the service,
    93  // Getting it from the datastore using the key (hash).
    94  func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, error) {
    95  	log.Debugf("BlockService GetBlock: '%s'", k)
    96  	block, err := s.Blockstore.Get(k)
    97  	if err == nil {
    98  		return block, nil
    99  	}
   100  
   101  	if err == blockstore.ErrNotFound && s.Exchange != nil {
   102  		// TODO be careful checking ErrNotFound. If the underlying
   103  		// implementation changes, this will break.
   104  		log.Debug("Blockservice: Searching bitswap.")
   105  		blk, err := s.Exchange.GetBlock(ctx, k)
   106  		if err != nil {
   107  			if err == blockstore.ErrNotFound {
   108  				return nil, ErrNotFound
   109  			}
   110  			return nil, err
   111  		}
   112  		return blk, nil
   113  	}
   114  
   115  	log.Debug("Blockservice GetBlock: Not found.")
   116  	if err == blockstore.ErrNotFound {
   117  		return nil, ErrNotFound
   118  	}
   119  
   120  	return nil, err
   121  }
   122  
   123  // GetBlocks gets a list of blocks asynchronously and returns through
   124  // the returned channel.
   125  // NB: No guarantees are made about order.
   126  func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan *blocks.Block {
   127  	out := make(chan *blocks.Block, 0)
   128  	go func() {
   129  		defer close(out)
   130  		var misses []key.Key
   131  		for _, k := range ks {
   132  			hit, err := s.Blockstore.Get(k)
   133  			if err != nil {
   134  				misses = append(misses, k)
   135  				continue
   136  			}
   137  			log.Debug("Blockservice: Got data in datastore.")
   138  			select {
   139  			case out <- hit:
   140  			case <-ctx.Done():
   141  				return
   142  			}
   143  		}
   144  
   145  		rblocks, err := s.Exchange.GetBlocks(ctx, misses)
   146  		if err != nil {
   147  			log.Debugf("Error with GetBlocks: %s", err)
   148  			return
   149  		}
   150  
   151  		for b := range rblocks {
   152  			select {
   153  			case out <- b:
   154  			case <-ctx.Done():
   155  				return
   156  			}
   157  		}
   158  	}()
   159  	return out
   160  }
   161  
   162  // DeleteBlock deletes a block in the blockservice from the datastore
   163  func (s *BlockService) DeleteBlock(k key.Key) error {
   164  	return s.Blockstore.DeleteBlock(k)
   165  }
   166  
   167  func (s *BlockService) Close() error {
   168  	log.Debug("blockservice is shutting down...")
   169  	return s.worker.Close()
   170  }