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 }