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() {}