github.com/gobitfly/go-ethereum@v1.8.12/swarm/storage/netstore.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 "time" 21 22 "github.com/ethereum/go-ethereum/swarm/log" 23 ) 24 25 var ( 26 // NetStore.Get timeout for get and get retries 27 // This is the maximum period that the Get will block. 28 // If it is reached, Get will return ErrChunkNotFound. 29 netStoreRetryTimeout = 30 * time.Second 30 // Minimal period between calling get method on NetStore 31 // on retry. It protects calling get very frequently if 32 // it returns ErrChunkNotFound very fast. 33 netStoreMinRetryDelay = 3 * time.Second 34 // Timeout interval before retrieval is timed out. 35 // It is used in NetStore.get on waiting for ReqC to be 36 // closed on a single retrieve request. 37 searchTimeout = 10 * time.Second 38 ) 39 40 // NetStore implements the ChunkStore interface, 41 // this chunk access layer assumed 2 chunk stores 42 // local storage eg. LocalStore and network storage eg., NetStore 43 // access by calling network is blocking with a timeout 44 type NetStore struct { 45 localStore *LocalStore 46 retrieve func(chunk *Chunk) error 47 } 48 49 func NewNetStore(localStore *LocalStore, retrieve func(chunk *Chunk) error) *NetStore { 50 return &NetStore{localStore, retrieve} 51 } 52 53 // Get is the entrypoint for local retrieve requests 54 // waits for response or times out 55 // 56 // Get uses get method to retrieve request, but retries if the 57 // ErrChunkNotFound is returned by get, until the netStoreRetryTimeout 58 // is reached. 59 func (ns *NetStore) Get(addr Address) (chunk *Chunk, err error) { 60 timer := time.NewTimer(netStoreRetryTimeout) 61 defer timer.Stop() 62 63 // result and resultC provide results from the goroutine 64 // where NetStore.get is called. 65 type result struct { 66 chunk *Chunk 67 err error 68 } 69 resultC := make(chan result) 70 71 // quitC ensures that retring goroutine is terminated 72 // when this function returns. 73 quitC := make(chan struct{}) 74 defer close(quitC) 75 76 // do retries in a goroutine so that the timer can 77 // force this method to return after the netStoreRetryTimeout. 78 go func() { 79 // limiter ensures that NetStore.get is not called more frequently 80 // then netStoreMinRetryDelay. If NetStore.get takes longer 81 // then netStoreMinRetryDelay, the next retry call will be 82 // without a delay. 83 limiter := time.NewTimer(netStoreMinRetryDelay) 84 defer limiter.Stop() 85 86 for { 87 chunk, err := ns.get(addr, 0) 88 if err != ErrChunkNotFound { 89 // break retry only if the error is nil 90 // or other error then ErrChunkNotFound 91 select { 92 case <-quitC: 93 // Maybe NetStore.Get function has returned 94 // by the timer.C while we were waiting for the 95 // results. Terminate this goroutine. 96 case resultC <- result{chunk: chunk, err: err}: 97 // Send the result to the parrent goroutine. 98 } 99 return 100 101 } 102 select { 103 case <-quitC: 104 // NetStore.Get function has returned, possibly 105 // by the timer.C, which makes this goroutine 106 // not needed. 107 return 108 case <-limiter.C: 109 } 110 // Reset the limiter for the next iteration. 111 limiter.Reset(netStoreMinRetryDelay) 112 log.Debug("NetStore.Get retry chunk", "key", addr) 113 } 114 }() 115 116 select { 117 case r := <-resultC: 118 return r.chunk, r.err 119 case <-timer.C: 120 return nil, ErrChunkNotFound 121 } 122 } 123 124 // GetWithTimeout makes a single retrieval attempt for a chunk with a explicit timeout parameter 125 func (ns *NetStore) GetWithTimeout(addr Address, timeout time.Duration) (chunk *Chunk, err error) { 126 return ns.get(addr, timeout) 127 } 128 129 func (ns *NetStore) get(addr Address, timeout time.Duration) (chunk *Chunk, err error) { 130 if timeout == 0 { 131 timeout = searchTimeout 132 } 133 if ns.retrieve == nil { 134 chunk, err = ns.localStore.Get(addr) 135 if err == nil { 136 return chunk, nil 137 } 138 if err != ErrFetching { 139 return nil, err 140 } 141 } else { 142 var created bool 143 chunk, created = ns.localStore.GetOrCreateRequest(addr) 144 145 if chunk.ReqC == nil { 146 return chunk, nil 147 } 148 149 if created { 150 err := ns.retrieve(chunk) 151 if err != nil { 152 // mark chunk request as failed so that we can retry it later 153 chunk.SetErrored(ErrChunkUnavailable) 154 return nil, err 155 } 156 } 157 } 158 159 t := time.NewTicker(timeout) 160 defer t.Stop() 161 162 select { 163 case <-t.C: 164 // mark chunk request as failed so that we can retry 165 chunk.SetErrored(ErrChunkNotFound) 166 return nil, ErrChunkNotFound 167 case <-chunk.ReqC: 168 } 169 chunk.SetErrored(nil) 170 return chunk, nil 171 } 172 173 // Put is the entrypoint for local store requests coming from storeLoop 174 func (ns *NetStore) Put(chunk *Chunk) { 175 ns.localStore.Put(chunk) 176 } 177 178 // Close chunk store 179 func (ns *NetStore) Close() { 180 ns.localStore.Close() 181 }